mutt stable branch with some hacks
at master 2004 lines 45 kB view raw
1/* 2 * Copyright (C) 1996-2000,2007,2010,2013 Michael R. Elkins <me@mutt.org> 3 * Copyright (C) 1999-2008 Thomas Roessler <roessler@does-not-exist.org> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 */ 19 20#if HAVE_CONFIG_H 21# include "config.h" 22#endif 23 24#include "mutt.h" 25#include "mutt_curses.h" 26#include "mime.h" 27#include "mailbox.h" 28#include "mx.h" 29#include "url.h" 30 31#ifdef USE_IMAP 32#include "imap.h" 33#endif 34 35#include "mutt_crypt.h" 36 37#include <string.h> 38#include <ctype.h> 39#include <unistd.h> 40#include <stdlib.h> 41#include <sys/wait.h> 42#include <errno.h> 43#include <sys/stat.h> 44#include <fcntl.h> 45#include <time.h> 46#include <sys/types.h> 47#include <utime.h> 48 49BODY *mutt_new_body (void) 50{ 51 BODY *p = (BODY *) safe_calloc (1, sizeof (BODY)); 52 53 p->disposition = DISPATTACH; 54 p->use_disp = 1; 55 return (p); 56} 57 58 59/* Modified by blong to accept a "suggestion" for file name. If 60 * that file exists, then construct one with unique name but 61 * keep any extension. This might fail, I guess. 62 * Renamed to mutt_adv_mktemp so I only have to change where it's 63 * called, and not all possible cases. 64 */ 65void mutt_adv_mktemp (char *s, size_t l) 66{ 67 char prefix[_POSIX_PATH_MAX]; 68 char *suffix; 69 struct stat sb; 70 71 if (s[0] == '\0') 72 { 73 mutt_mktemp (s, l); 74 } 75 else 76 { 77 strfcpy (prefix, s, sizeof (prefix)); 78 mutt_sanitize_filename (prefix, 1); 79 snprintf (s, l, "%s/%s", NONULL (Tempdir), prefix); 80 if (lstat (s, &sb) == -1 && errno == ENOENT) 81 return; 82 83 if ((suffix = strrchr (prefix, '.')) != NULL) 84 { 85 *suffix = 0; 86 ++suffix; 87 } 88 mutt_mktemp_pfx_sfx (s, l, prefix, suffix); 89 } 90} 91 92/* create a send-mode duplicate from a receive-mode body */ 93 94int mutt_copy_body (FILE *fp, BODY **tgt, BODY *src) 95{ 96 char tmp[_POSIX_PATH_MAX]; 97 BODY *b; 98 99 PARAMETER *par, **ppar; 100 101 short use_disp; 102 103 if (src->filename) 104 { 105 use_disp = 1; 106 strfcpy (tmp, src->filename, sizeof (tmp)); 107 } 108 else 109 { 110 use_disp = 0; 111 tmp[0] = '\0'; 112 } 113 114 mutt_adv_mktemp (tmp, sizeof (tmp)); 115 if (mutt_save_attachment (fp, src, tmp, 0, NULL) == -1) 116 return -1; 117 118 *tgt = mutt_new_body (); 119 b = *tgt; 120 121 memcpy (b, src, sizeof (BODY)); 122 b->parts = NULL; 123 b->next = NULL; 124 125 b->filename = safe_strdup (tmp); 126 b->use_disp = use_disp; 127 b->unlink = 1; 128 129 if (mutt_is_text_part (b)) 130 b->noconv = 1; 131 132 b->xtype = safe_strdup (b->xtype); 133 b->subtype = safe_strdup (b->subtype); 134 b->form_name = safe_strdup (b->form_name); 135 b->d_filename = safe_strdup (b->d_filename); 136 /* mutt_adv_mktemp() will mangle the filename in tmp, 137 * so preserve it in d_filename */ 138 if (!b->d_filename && use_disp) 139 b->d_filename = safe_strdup (src->filename); 140 b->description = safe_strdup (b->description); 141 142 /* 143 * we don't seem to need the HEADER structure currently. 144 * XXX - this may change in the future 145 */ 146 147 if (b->hdr) b->hdr = NULL; 148 149 /* copy parameters */ 150 for (par = b->parameter, ppar = &b->parameter; par; ppar = &(*ppar)->next, par = par->next) 151 { 152 *ppar = mutt_new_parameter (); 153 (*ppar)->attribute = safe_strdup (par->attribute); 154 (*ppar)->value = safe_strdup (par->value); 155 } 156 157 mutt_stamp_attachment (b); 158 159 return 0; 160} 161 162 163 164void mutt_free_body (BODY **p) 165{ 166 BODY *a = *p, *b; 167 168 while (a) 169 { 170 b = a; 171 a = a->next; 172 173 if (b->parameter) 174 mutt_free_parameter (&b->parameter); 175 if (b->filename) 176 { 177 if (b->unlink) 178 unlink (b->filename); 179 dprint (1, (debugfile, "mutt_free_body: %sunlinking %s.\n", 180 b->unlink ? "" : "not ", b->filename)); 181 } 182 183 FREE (&b->filename); 184 FREE (&b->d_filename); 185 FREE (&b->charset); 186 FREE (&b->content); 187 FREE (&b->xtype); 188 FREE (&b->subtype); 189 FREE (&b->description); 190 FREE (&b->form_name); 191 192 if (b->hdr) 193 { 194 /* Don't free twice (b->hdr->content = b->parts) */ 195 b->hdr->content = NULL; 196 mutt_free_header(&b->hdr); 197 } 198 199 if (b->parts) 200 mutt_free_body (&b->parts); 201 202 FREE (&b); 203 } 204 205 *p = 0; 206} 207 208void mutt_free_parameter (PARAMETER **p) 209{ 210 PARAMETER *t = *p; 211 PARAMETER *o; 212 213 while (t) 214 { 215 FREE (&t->attribute); 216 FREE (&t->value); 217 o = t; 218 t = t->next; 219 FREE (&o); 220 } 221 *p = 0; 222} 223 224LIST *mutt_add_list (LIST *head, const char *data) 225{ 226 size_t len = mutt_strlen (data); 227 228 return mutt_add_list_n (head, data, len ? len + 1 : 0); 229} 230 231LIST *mutt_add_list_n (LIST *head, const void *data, size_t len) 232{ 233 LIST *tmp; 234 235 for (tmp = head; tmp && tmp->next; tmp = tmp->next) 236 ; 237 if (tmp) 238 { 239 tmp->next = safe_malloc (sizeof (LIST)); 240 tmp = tmp->next; 241 } 242 else 243 head = tmp = safe_malloc (sizeof (LIST)); 244 245 tmp->data = safe_malloc (len); 246 if (len) 247 memcpy (tmp->data, data, len); 248 tmp->next = NULL; 249 return head; 250} 251 252LIST *mutt_find_list (LIST *l, const char *data) 253{ 254 LIST *p = l; 255 256 while (p) 257 { 258 if (data == p->data) 259 return p; 260 if (data && p->data && mutt_strcmp (p->data, data) == 0) 261 return p; 262 p = p->next; 263 } 264 return NULL; 265} 266 267int mutt_remove_from_rx_list (RX_LIST **l, const char *str) 268{ 269 RX_LIST *p, *last = NULL; 270 int rv = -1; 271 272 if (mutt_strcmp ("*", str) == 0) 273 { 274 mutt_free_rx_list (l); /* ``unCMD *'' means delete all current entries */ 275 rv = 0; 276 } 277 else 278 { 279 p = *l; 280 last = NULL; 281 while (p) 282 { 283 if (ascii_strcasecmp (str, p->rx->pattern) == 0) 284 { 285 mutt_free_regexp (&p->rx); 286 if (last) 287 last->next = p->next; 288 else 289 (*l) = p->next; 290 FREE (&p); 291 rv = 0; 292 } 293 else 294 { 295 last = p; 296 p = p->next; 297 } 298 } 299 } 300 return (rv); 301} 302 303void mutt_free_list (LIST **list) 304{ 305 LIST *p; 306 307 if (!list) return; 308 while (*list) 309 { 310 p = *list; 311 *list = (*list)->next; 312 FREE (&p->data); 313 FREE (&p); 314 } 315} 316 317LIST *mutt_copy_list (LIST *p) 318{ 319 LIST *t, *r=NULL, *l=NULL; 320 321 for (; p; p = p->next) 322 { 323 t = (LIST *) safe_malloc (sizeof (LIST)); 324 t->data = safe_strdup (p->data); 325 t->next = NULL; 326 if (l) 327 { 328 r->next = t; 329 r = r->next; 330 } 331 else 332 l = r = t; 333 } 334 return (l); 335} 336 337HEADER *mutt_dup_header(HEADER *h) 338{ 339 HEADER *hnew; 340 341 hnew = mutt_new_header(); 342 memcpy(hnew, h, sizeof (HEADER)); 343 return hnew; 344} 345 346void mutt_free_header (HEADER **h) 347{ 348 if(!h || !*h) return; 349 mutt_free_envelope (&(*h)->env); 350 mutt_free_body (&(*h)->content); 351 FREE (&(*h)->maildir_flags); 352 FREE (&(*h)->tree); 353 FREE (&(*h)->path); 354#ifdef MIXMASTER 355 mutt_free_list (&(*h)->chain); 356#endif 357#if defined USE_POP || defined USE_IMAP 358 FREE (&(*h)->data); 359#endif 360 FREE (h); /* __FREE_CHECKED__ */ 361} 362 363/* returns true if the header contained in "s" is in list "t" */ 364int mutt_matches_ignore (const char *s, LIST *t) 365{ 366 for (; t; t = t->next) 367 { 368 if (!ascii_strncasecmp (s, t->data, mutt_strlen (t->data)) || *t->data == '*') 369 return 1; 370 } 371 return 0; 372} 373 374/* prepend the path part of *path to *link */ 375void mutt_expand_link (char *newpath, const char *path, const char *link) 376{ 377 const char *lb = NULL; 378 size_t len; 379 380 /* link is full path */ 381 if (*link == '/') 382 { 383 strfcpy (newpath, link, _POSIX_PATH_MAX); 384 return; 385 } 386 387 if ((lb = strrchr (path, '/')) == NULL) 388 { 389 /* no path in link */ 390 strfcpy (newpath, link, _POSIX_PATH_MAX); 391 return; 392 } 393 394 len = lb - path + 1; 395 memcpy (newpath, path, len); 396 strfcpy (newpath + len, link, _POSIX_PATH_MAX - len); 397} 398 399char *mutt_expand_path (char *s, size_t slen) 400{ 401 return _mutt_expand_path (s, slen, 0); 402} 403 404char *_mutt_expand_path (char *s, size_t slen, int rx) 405{ 406 char p[_POSIX_PATH_MAX] = ""; 407 char q[_POSIX_PATH_MAX] = ""; 408 char tmp[_POSIX_PATH_MAX]; 409 char *t; 410 411 char *tail = ""; 412 413 int recurse = 0; 414 415 do 416 { 417 recurse = 0; 418 419 switch (*s) 420 { 421 case '~': 422 { 423 if (*(s + 1) == '/' || *(s + 1) == 0) 424 { 425 strfcpy (p, NONULL(Homedir), sizeof (p)); 426 tail = s + 1; 427 } 428 else 429 { 430 struct passwd *pw; 431 if ((t = strchr (s + 1, '/'))) 432 *t = 0; 433 434 if ((pw = getpwnam (s + 1))) 435 { 436 strfcpy (p, pw->pw_dir, sizeof (p)); 437 if (t) 438 { 439 *t = '/'; 440 tail = t; 441 } 442 else 443 tail = ""; 444 } 445 else 446 { 447 /* user not found! */ 448 if (t) 449 *t = '/'; 450 *p = '\0'; 451 tail = s; 452 } 453 } 454 } 455 break; 456 457 case '=': 458 case '+': 459 { 460#ifdef USE_IMAP 461 /* if folder = {host} or imap[s]://host/: don't append slash */ 462 if (mx_is_imap (NONULL (Maildir)) && 463 (Maildir[strlen (Maildir) - 1] == '}' || 464 Maildir[strlen (Maildir) - 1] == '/')) 465 strfcpy (p, NONULL (Maildir), sizeof (p)); 466 else 467#endif 468 if (Maildir && *Maildir && Maildir[strlen (Maildir) - 1] == '/') 469 strfcpy (p, NONULL (Maildir), sizeof (p)); 470 else 471 snprintf (p, sizeof (p), "%s/", NONULL (Maildir)); 472 473 tail = s + 1; 474 } 475 break; 476 477 /* elm compatibility, @ expands alias to user name */ 478 479 case '@': 480 { 481 HEADER *h; 482 ADDRESS *alias; 483 484 if ((alias = mutt_lookup_alias (s + 1))) 485 { 486 h = mutt_new_header(); 487 h->env = mutt_new_envelope(); 488 h->env->from = h->env->to = alias; 489 mutt_default_save (p, sizeof (p), h); 490 h->env->from = h->env->to = NULL; 491 mutt_free_header (&h); 492 /* Avoid infinite recursion if the resulting folder starts with '@' */ 493 if (*p != '@') 494 recurse = 1; 495 496 tail = ""; 497 } 498 } 499 break; 500 501 case '>': 502 { 503 strfcpy (p, NONULL(Inbox), sizeof (p)); 504 tail = s + 1; 505 } 506 break; 507 508 case '<': 509 { 510 strfcpy (p, NONULL(Outbox), sizeof (p)); 511 tail = s + 1; 512 } 513 break; 514 515 case '!': 516 { 517 if (*(s+1) == '!') 518 { 519 strfcpy (p, NONULL(LastFolder), sizeof (p)); 520 tail = s + 2; 521 } 522 else 523 { 524 strfcpy (p, NONULL(Spoolfile), sizeof (p)); 525 tail = s + 1; 526 } 527 } 528 break; 529 530 case '-': 531 { 532 strfcpy (p, NONULL(LastFolder), sizeof (p)); 533 tail = s + 1; 534 } 535 break; 536 537 case '^': 538 { 539 strfcpy (p, NONULL(CurrentFolder), sizeof (p)); 540 tail = s + 1; 541 } 542 break; 543 544 default: 545 { 546 *p = '\0'; 547 tail = s; 548 } 549 } 550 551 if (rx && *p && !recurse) 552 { 553 mutt_rx_sanitize_string (q, sizeof (q), p); 554 snprintf (tmp, sizeof (tmp), "%s%s", q, tail); 555 } 556 else 557 snprintf (tmp, sizeof (tmp), "%s%s", p, tail); 558 559 strfcpy (s, tmp, slen); 560 } 561 while (recurse); 562 563#ifdef USE_IMAP 564 /* Rewrite IMAP path in canonical form - aids in string comparisons of 565 * folders. May possibly fail, in which case s should be the same. */ 566 if (mx_is_imap (s)) 567 imap_expand_path (s, slen); 568#endif 569 570 return (s); 571} 572 573/* Extract the real name from /etc/passwd's GECOS field. 574 * When set, honor the regular expression in GecosMask, 575 * otherwise assume that the GECOS field is a 576 * comma-separated list. 577 * Replace "&" by a capitalized version of the user's login 578 * name. 579 */ 580 581char *mutt_gecos_name (char *dest, size_t destlen, struct passwd *pw) 582{ 583 regmatch_t pat_match[1]; 584 size_t pwnl; 585 int idx; 586 char *p; 587 588 if (!pw || !pw->pw_gecos) 589 return NULL; 590 591 memset (dest, 0, destlen); 592 593 if (GecosMask.rx) 594 { 595 if (regexec (GecosMask.rx, pw->pw_gecos, 1, pat_match, 0) == 0) 596 strfcpy (dest, pw->pw_gecos + pat_match[0].rm_so, 597 MIN (pat_match[0].rm_eo - pat_match[0].rm_so + 1, destlen)); 598 } 599 else if ((p = strchr (pw->pw_gecos, ','))) 600 strfcpy (dest, pw->pw_gecos, MIN (destlen, p - pw->pw_gecos + 1)); 601 else 602 strfcpy (dest, pw->pw_gecos, destlen); 603 604 pwnl = strlen (pw->pw_name); 605 606 for (idx = 0; dest[idx]; idx++) 607 { 608 if (dest[idx] == '&') 609 { 610 memmove (&dest[idx + pwnl], &dest[idx + 1], 611 MAX((ssize_t)(destlen - idx - pwnl - 1), 0)); 612 memcpy (&dest[idx], pw->pw_name, MIN(destlen - idx - 1, pwnl)); 613 dest[idx] = toupper ((unsigned char) dest[idx]); 614 } 615 } 616 617 return dest; 618} 619 620 621char *mutt_get_parameter (const char *s, PARAMETER *p) 622{ 623 for (; p; p = p->next) 624 if (ascii_strcasecmp (s, p->attribute) == 0) 625 return (p->value); 626 627 return NULL; 628} 629 630void mutt_set_parameter (const char *attribute, const char *value, PARAMETER **p) 631{ 632 PARAMETER *q; 633 634 if (!value) 635 { 636 mutt_delete_parameter (attribute, p); 637 return; 638 } 639 640 for(q = *p; q; q = q->next) 641 { 642 if (ascii_strcasecmp (attribute, q->attribute) == 0) 643 { 644 mutt_str_replace (&q->value, value); 645 return; 646 } 647 } 648 649 q = mutt_new_parameter(); 650 q->attribute = safe_strdup(attribute); 651 q->value = safe_strdup(value); 652 q->next = *p; 653 *p = q; 654} 655 656void mutt_delete_parameter (const char *attribute, PARAMETER **p) 657{ 658 PARAMETER *q; 659 660 for (q = *p; q; p = &q->next, q = q->next) 661 { 662 if (ascii_strcasecmp (attribute, q->attribute) == 0) 663 { 664 *p = q->next; 665 q->next = NULL; 666 mutt_free_parameter (&q); 667 return; 668 } 669 } 670} 671 672/* returns 1 if Mutt can't display this type of data, 0 otherwise */ 673int mutt_needs_mailcap (BODY *m) 674{ 675 switch (m->type) 676 { 677 case TYPETEXT: 678 /* we can display any text, overridable by auto_view */ 679 return 0; 680 break; 681 682 case TYPEAPPLICATION: 683 if((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp(m)) 684 return 0; 685 if((WithCrypto & APPLICATION_SMIME) && mutt_is_application_smime(m)) 686 return 0; 687 break; 688 689 case TYPEMULTIPART: 690 case TYPEMESSAGE: 691 return 0; 692 } 693 694 return 1; 695} 696 697int mutt_is_text_part (BODY *b) 698{ 699 int t = b->type; 700 char *s = b->subtype; 701 702 if ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b)) 703 return 0; 704 705 if (t == TYPETEXT) 706 return 1; 707 708 if (t == TYPEMESSAGE) 709 { 710 if (!ascii_strcasecmp ("delivery-status", s)) 711 return 1; 712 } 713 714 if ((WithCrypto & APPLICATION_PGP) && t == TYPEAPPLICATION) 715 { 716 if (!ascii_strcasecmp ("pgp-keys", s)) 717 return 1; 718 } 719 720 return 0; 721} 722 723void mutt_free_envelope (ENVELOPE **p) 724{ 725 if (!*p) return; 726 rfc822_free_address (&(*p)->return_path); 727 rfc822_free_address (&(*p)->from); 728 rfc822_free_address (&(*p)->to); 729 rfc822_free_address (&(*p)->cc); 730 rfc822_free_address (&(*p)->bcc); 731 rfc822_free_address (&(*p)->sender); 732 rfc822_free_address (&(*p)->reply_to); 733 rfc822_free_address (&(*p)->mail_followup_to); 734 735 FREE (&(*p)->list_post); 736 FREE (&(*p)->subject); 737 /* real_subj is just an offset to subject and shouldn't be freed */ 738 FREE (&(*p)->message_id); 739 FREE (&(*p)->supersedes); 740 FREE (&(*p)->date); 741 FREE (&(*p)->x_label); 742 743 mutt_buffer_free (&(*p)->spam); 744 745 mutt_free_list (&(*p)->references); 746 mutt_free_list (&(*p)->in_reply_to); 747 mutt_free_list (&(*p)->userhdrs); 748 FREE (p); /* __FREE_CHECKED__ */ 749} 750 751/* move all the headers from extra not present in base into base */ 752void mutt_merge_envelopes(ENVELOPE* base, ENVELOPE** extra) 753{ 754 /* copies each existing element if necessary, and sets the element 755 * to NULL in the source so that mutt_free_envelope doesn't leave us 756 * with dangling pointers. */ 757#define MOVE_ELEM(h) if (!base->h) { base->h = (*extra)->h; (*extra)->h = NULL; } 758 MOVE_ELEM(return_path); 759 MOVE_ELEM(from); 760 MOVE_ELEM(to); 761 MOVE_ELEM(cc); 762 MOVE_ELEM(bcc); 763 MOVE_ELEM(sender); 764 MOVE_ELEM(reply_to); 765 MOVE_ELEM(mail_followup_to); 766 MOVE_ELEM(list_post); 767 MOVE_ELEM(message_id); 768 MOVE_ELEM(supersedes); 769 MOVE_ELEM(date); 770 MOVE_ELEM(x_label); 771 if (!base->refs_changed) 772 { 773 MOVE_ELEM(references); 774 } 775 if (!base->irt_changed) 776 { 777 MOVE_ELEM(in_reply_to); 778 } 779 780 /* real_subj is subordinate to subject */ 781 if (!base->subject) 782 { 783 base->subject = (*extra)->subject; 784 base->real_subj = (*extra)->real_subj; 785 (*extra)->subject = NULL; 786 (*extra)->real_subj = NULL; 787 } 788 /* spam and user headers should never be hashed, and the new envelope may 789 * have better values. Use new versions regardless. */ 790 mutt_buffer_free (&base->spam); 791 mutt_free_list (&base->userhdrs); 792 MOVE_ELEM(spam); 793 MOVE_ELEM(userhdrs); 794#undef MOVE_ELEM 795 796 mutt_free_envelope(extra); 797} 798 799void _mutt_mktemp (char *s, size_t slen, const char *prefix, const char *suffix, 800 const char *src, int line) 801{ 802 size_t n = snprintf (s, slen, "%s/%s-%s-%d-%d-%ld%ld%s%s", 803 NONULL (Tempdir), NONULL (prefix), NONULL (Hostname), 804 (int) getuid (), (int) getpid (), random (), random (), 805 suffix ? "." : "", NONULL (suffix)); 806 if (n >= slen) 807 dprint (1, (debugfile, "%s:%d: ERROR: insufficient buffer space to hold temporary filename! slen=%zu but need %zu\n", 808 src, line, slen, n)); 809 dprint (3, (debugfile, "%s:%d: mutt_mktemp returns \"%s\".\n", src, line, s)); 810 if (unlink (s) && errno != ENOENT) 811 dprint (1, (debugfile, "%s:%d: ERROR: unlink(\"%s\"): %s (errno %d)\n", src, line, s, strerror (errno), errno)); 812} 813 814void mutt_free_alias (ALIAS **p) 815{ 816 ALIAS *t; 817 818 while (*p) 819 { 820 t = *p; 821 *p = (*p)->next; 822 mutt_alias_delete_reverse (t); 823 FREE (&t->name); 824 rfc822_free_address (&t->addr); 825 FREE (&t); 826 } 827} 828 829/* collapse the pathname using ~ or = when possible */ 830void mutt_pretty_mailbox (char *s, size_t buflen) 831{ 832 char *p = s, *q = s; 833 size_t len; 834 url_scheme_t scheme; 835 char tmp[PATH_MAX]; 836 837 scheme = url_check_scheme (s); 838 839#ifdef USE_IMAP 840 if (scheme == U_IMAP || scheme == U_IMAPS) 841 { 842 imap_pretty_mailbox (s); 843 return; 844 } 845#endif 846 847 /* if s is an url, only collapse path component */ 848 if (scheme != U_UNKNOWN) 849 { 850 p = strchr(s, ':')+1; 851 if (!strncmp (p, "//", 2)) 852 q = strchr (p+2, '/'); 853 if (!q) 854 q = strchr (p, '\0'); 855 p = q; 856 } 857 858 /* cleanup path */ 859 if (strstr (p, "//") || strstr (p, "/./")) 860 { 861 /* first attempt to collapse the pathname, this is more 862 * lightweight than realpath() and doesn't resolve links 863 */ 864 while (*p) 865 { 866 if (*p == '/' && p[1] == '/') 867 { 868 *q++ = '/'; 869 p += 2; 870 } 871 else if (p[0] == '/' && p[1] == '.' && p[2] == '/') 872 { 873 *q++ = '/'; 874 p += 3; 875 } 876 else 877 *q++ = *p++; 878 } 879 *q = 0; 880 } 881 else if (strstr (p, "..") && 882 (scheme == U_UNKNOWN || scheme == U_FILE) && 883 realpath (p, tmp)) 884 strfcpy (p, tmp, buflen - (p - s)); 885 886 if (mutt_strncmp (s, Maildir, (len = mutt_strlen (Maildir))) == 0 && 887 s[len] == '/') 888 { 889 *s++ = '='; 890 memmove (s, s + len, mutt_strlen (s + len) + 1); 891 } 892 else if (mutt_strncmp (s, Homedir, (len = mutt_strlen (Homedir))) == 0 && 893 s[len] == '/') 894 { 895 *s++ = '~'; 896 memmove (s, s + len - 1, mutt_strlen (s + len - 1) + 1); 897 } 898} 899 900void mutt_pretty_size (char *s, size_t len, LOFF_T n) 901{ 902 if (n == 0) 903 strfcpy (s, "0K", len); 904 else if (n < 10189) /* 0.1K - 9.9K */ 905 snprintf (s, len, "%3.1fK", (n < 103) ? 0.1 : n / 1024.0); 906 else if (n < 1023949) /* 10K - 999K */ 907 { 908 /* 51 is magic which causes 10189/10240 to be rounded up to 10 */ 909 snprintf (s, len, OFF_T_FMT "K", (n + 51) / 1024); 910 } 911 else if (n < 10433332) /* 1.0M - 9.9M */ 912 snprintf (s, len, "%3.1fM", n / 1048576.0); 913 else /* 10M+ */ 914 { 915 /* (10433332 + 52428) / 1048576 = 10 */ 916 snprintf (s, len, OFF_T_FMT "M", (n + 52428) / 1048576); 917 } 918} 919 920void mutt_expand_file_fmt (char *dest, size_t destlen, const char *fmt, const char *src) 921{ 922 char tmp[LONG_STRING]; 923 924 mutt_quote_filename (tmp, sizeof (tmp), src); 925 mutt_expand_fmt (dest, destlen, fmt, tmp); 926} 927 928void mutt_expand_fmt (char *dest, size_t destlen, const char *fmt, const char *src) 929{ 930 const char *p; 931 char *d; 932 size_t slen; 933 int found = 0; 934 935 slen = mutt_strlen (src); 936 destlen--; 937 938 for (p = fmt, d = dest; destlen && *p; p++) 939 { 940 if (*p == '%') 941 { 942 switch (p[1]) 943 { 944 case '%': 945 *d++ = *p++; 946 destlen--; 947 break; 948 case 's': 949 found = 1; 950 strfcpy (d, src, destlen + 1); 951 d += destlen > slen ? slen : destlen; 952 destlen -= destlen > slen ? slen : destlen; 953 p++; 954 break; 955 default: 956 *d++ = *p; 957 destlen--; 958 break; 959 } 960 } 961 else 962 { 963 *d++ = *p; 964 destlen--; 965 } 966 } 967 968 *d = '\0'; 969 970 if (!found && destlen > 0) 971 { 972 safe_strcat (dest, destlen, " "); 973 safe_strcat (dest, destlen, src); 974 } 975 976} 977 978/* return 0 on success, -1 on abort, 1 on error */ 979int mutt_check_overwrite (const char *attname, const char *path, 980 char *fname, size_t flen, int *append, char **directory) 981{ 982 int rc = 0; 983 char tmp[_POSIX_PATH_MAX]; 984 struct stat st; 985 986 strfcpy (fname, path, flen); 987 if (access (fname, F_OK) != 0) 988 return 0; 989 if (stat (fname, &st) != 0) 990 return -1; 991 if (S_ISDIR (st.st_mode)) 992 { 993 if (directory) 994 { 995 switch (mutt_multi_choice 996 /* L10N: 997 Means "The path you specified as the destination file is a directory." 998 See the msgid "Save to file: " (alias.c, recvattach.c) */ 999 (_("File is a directory, save under it? [(y)es, (n)o, (a)ll]"), _("yna"))) 1000 { 1001 case 3: /* all */ 1002 mutt_str_replace (directory, fname); 1003 break; 1004 case 1: /* yes */ 1005 FREE (directory); /* __FREE_CHECKED__ */ 1006 break; 1007 case -1: /* abort */ 1008 FREE (directory); /* __FREE_CHECKED__ */ 1009 return -1; 1010 case 2: /* no */ 1011 FREE (directory); /* __FREE_CHECKED__ */ 1012 return 1; 1013 } 1014 } 1015 /* L10N: 1016 Means "The path you specified as the destination file is a directory." 1017 See the msgid "Save to file: " (alias.c, recvattach.c) */ 1018 else if ((rc = mutt_yesorno (_("File is a directory, save under it?"), MUTT_YES)) != MUTT_YES) 1019 return (rc == MUTT_NO) ? 1 : -1; 1020 1021 strfcpy (tmp, mutt_basename (NONULL (attname)), sizeof (tmp)); 1022 if (mutt_get_field (_("File under directory: "), tmp, sizeof (tmp), 1023 MUTT_FILE | MUTT_CLEAR) != 0 || !tmp[0]) 1024 return (-1); 1025 mutt_concat_path (fname, path, tmp, flen); 1026 } 1027 1028 if (*append == 0 && access (fname, F_OK) == 0) 1029 { 1030 switch (mutt_multi_choice 1031 (_("File exists, (o)verwrite, (a)ppend, or (c)ancel?"), _("oac"))) 1032 { 1033 case -1: /* abort */ 1034 return -1; 1035 case 3: /* cancel */ 1036 return 1; 1037 1038 case 2: /* append */ 1039 *append = MUTT_SAVE_APPEND; 1040 break; 1041 case 1: /* overwrite */ 1042 *append = MUTT_SAVE_OVERWRITE; 1043 break; 1044 } 1045 } 1046 return 0; 1047} 1048 1049void mutt_save_path (char *d, size_t dsize, ADDRESS *a) 1050{ 1051 if (a && a->mailbox) 1052 { 1053 strfcpy (d, a->mailbox, dsize); 1054 if (!option (OPTSAVEADDRESS)) 1055 { 1056 char *p; 1057 1058 if ((p = strpbrk (d, "%@"))) 1059 *p = 0; 1060 } 1061 mutt_strlower (d); 1062 } 1063 else 1064 *d = 0; 1065} 1066 1067void mutt_safe_path (char *s, size_t l, ADDRESS *a) 1068{ 1069 char *p; 1070 1071 mutt_save_path (s, l, a); 1072 for (p = s; *p; p++) 1073 if (*p == '/' || ISSPACE (*p) || !IsPrint ((unsigned char) *p)) 1074 *p = '_'; 1075} 1076 1077 1078void mutt_FormatString (char *dest, /* output buffer */ 1079 size_t destlen, /* output buffer len */ 1080 size_t col, /* starting column (nonzero when called recursively) */ 1081 int cols, /* maximum columns */ 1082 const char *src, /* template string */ 1083 format_t *callback, /* callback for processing */ 1084 unsigned long data, /* callback data */ 1085 format_flag flags) /* callback flags */ 1086{ 1087 char prefix[SHORT_STRING], buf[LONG_STRING], *cp, *wptr = dest, ch; 1088 char ifstring[SHORT_STRING], elsestring[SHORT_STRING]; 1089 size_t wlen, count, len, wid; 1090 pid_t pid; 1091 FILE *filter; 1092 int n; 1093 char *recycler; 1094 1095 prefix[0] = '\0'; 1096 destlen--; /* save room for the terminal \0 */ 1097 wlen = ((flags & MUTT_FORMAT_ARROWCURSOR) && option (OPTARROWCURSOR)) ? 3 : 0; 1098 col += wlen; 1099 1100 if ((flags & MUTT_FORMAT_NOFILTER) == 0) 1101 { 1102 int off = -1; 1103 1104 /* Do not consider filters if no pipe at end */ 1105 n = mutt_strlen(src); 1106 if (n > 1 && src[n-1] == '|') 1107 { 1108 /* Scan backwards for backslashes */ 1109 off = n; 1110 while (off > 0 && src[off-2] == '\\') 1111 off--; 1112 } 1113 1114 /* If number of backslashes is even, the pipe is real. */ 1115 /* n-off is the number of backslashes. */ 1116 if (off > 0 && ((n-off) % 2) == 0) 1117 { 1118 BUFFER *srcbuf, *word, *command; 1119 char srccopy[LONG_STRING]; 1120#ifdef DEBUG 1121 int i = 0; 1122#endif 1123 1124 dprint(3, (debugfile, "fmtpipe = %s\n", src)); 1125 1126 strncpy(srccopy, src, n); 1127 srccopy[n-1] = '\0'; 1128 1129 /* prepare BUFFERs */ 1130 srcbuf = mutt_buffer_from (srccopy); 1131 srcbuf->dptr = srcbuf->data; 1132 word = mutt_buffer_new (); 1133 command = mutt_buffer_new (); 1134 1135 /* Iterate expansions across successive arguments */ 1136 do { 1137 char *p; 1138 1139 /* Extract the command name and copy to command line */ 1140 dprint(3, (debugfile, "fmtpipe +++: %s\n", srcbuf->dptr)); 1141 if (word->data) 1142 *word->data = '\0'; 1143 mutt_extract_token(word, srcbuf, 0); 1144 dprint(3, (debugfile, "fmtpipe %2d: %s\n", i++, word->data)); 1145 mutt_buffer_addch(command, '\''); 1146 mutt_FormatString(buf, sizeof(buf), 0, cols, word->data, callback, data, 1147 flags | MUTT_FORMAT_NOFILTER); 1148 for (p = buf; p && *p; p++) 1149 { 1150 if (*p == '\'') 1151 /* shell quoting doesn't permit escaping a single quote within 1152 * single-quoted material. double-quoting instead will lead 1153 * shell variable expansions, so break out of the single-quoted 1154 * span, insert a double-quoted single quote, and resume. */ 1155 mutt_buffer_addstr(command, "'\"'\"'"); 1156 else 1157 mutt_buffer_addch(command, *p); 1158 } 1159 mutt_buffer_addch(command, '\''); 1160 mutt_buffer_addch(command, ' '); 1161 } while (MoreArgs(srcbuf)); 1162 1163 dprint(3, (debugfile, "fmtpipe > %s\n", command->data)); 1164 1165 col -= wlen; /* reset to passed in value */ 1166 wptr = dest; /* reset write ptr */ 1167 wlen = ((flags & MUTT_FORMAT_ARROWCURSOR) && option (OPTARROWCURSOR)) ? 3 : 0; 1168 if ((pid = mutt_create_filter(command->data, NULL, &filter, NULL)) != -1) 1169 { 1170 int rc; 1171 1172 n = fread(dest, 1, destlen /* already decremented */, filter); 1173 safe_fclose (&filter); 1174 rc = mutt_wait_filter(pid); 1175 if (rc != 0) 1176 dprint(1, (debugfile, "format pipe command exited code %d\n", rc)); 1177 if (n > 0) { 1178 dest[n] = 0; 1179 while ((n > 0) && (dest[n-1] == '\n' || dest[n-1] == '\r')) 1180 dest[--n] = '\0'; 1181 dprint(3, (debugfile, "fmtpipe < %s\n", dest)); 1182 1183 /* If the result ends with '%', this indicates that the filter 1184 * generated %-tokens that mutt can expand. Eliminate the '%' 1185 * marker and recycle the string through mutt_FormatString(). 1186 * To literally end with "%", use "%%". */ 1187 if ((n > 0) && dest[n-1] == '%') 1188 { 1189 --n; 1190 dest[n] = '\0'; /* remove '%' */ 1191 if ((n > 0) && dest[n-1] != '%') 1192 { 1193 recycler = safe_strdup(dest); 1194 if (recycler) 1195 { 1196 /* destlen is decremented at the start of this function 1197 * to save space for the terminal nul char. We can add 1198 * it back for the recursive call since the expansion of 1199 * format pipes does not try to append a nul itself. 1200 */ 1201 mutt_FormatString(dest, destlen+1, col, cols, recycler, callback, data, flags); 1202 FREE(&recycler); 1203 } 1204 } 1205 } 1206 } 1207 else 1208 { 1209 /* read error */ 1210 dprint(1, (debugfile, "error reading from fmtpipe: %s (errno=%d)\n", strerror(errno), errno)); 1211 *wptr = 0; 1212 } 1213 } 1214 else 1215 { 1216 /* Filter failed; erase write buffer */ 1217 *wptr = '\0'; 1218 } 1219 1220 mutt_buffer_free(&command); 1221 mutt_buffer_free(&srcbuf); 1222 mutt_buffer_free(&word); 1223 return; 1224 } 1225 } 1226 1227 while (*src && wlen < destlen) 1228 { 1229 if (*src == '%') 1230 { 1231 if (*++src == '%') 1232 { 1233 *wptr++ = '%'; 1234 wlen++; 1235 col++; 1236 src++; 1237 continue; 1238 } 1239 1240 if (*src == '?') 1241 { 1242 flags |= MUTT_FORMAT_OPTIONAL; 1243 src++; 1244 } 1245 else 1246 { 1247 flags &= ~MUTT_FORMAT_OPTIONAL; 1248 1249 /* eat the format string */ 1250 cp = prefix; 1251 count = 0; 1252 while (count < sizeof (prefix) && 1253 (isdigit ((unsigned char) *src) || *src == '.' || *src == '-' || *src == '=')) 1254 { 1255 *cp++ = *src++; 1256 count++; 1257 } 1258 *cp = 0; 1259 } 1260 1261 if (!*src) 1262 break; /* bad format */ 1263 1264 ch = *src++; /* save the character to switch on */ 1265 1266 if (flags & MUTT_FORMAT_OPTIONAL) 1267 { 1268 if (*src != '?') 1269 break; /* bad format */ 1270 src++; 1271 1272 /* eat the `if' part of the string */ 1273 cp = ifstring; 1274 count = 0; 1275 while (count < sizeof (ifstring) && *src && *src != '?' && *src != '&') 1276 { 1277 *cp++ = *src++; 1278 count++; 1279 } 1280 *cp = 0; 1281 1282 /* eat the `else' part of the string (optional) */ 1283 if (*src == '&') 1284 src++; /* skip the & */ 1285 cp = elsestring; 1286 count = 0; 1287 while (count < sizeof (elsestring) && *src && *src != '?') 1288 { 1289 *cp++ = *src++; 1290 count++; 1291 } 1292 *cp = 0; 1293 1294 if (!*src) 1295 break; /* bad format */ 1296 1297 src++; /* move past the trailing `?' */ 1298 } 1299 1300 /* handle generic cases first */ 1301 if (ch == '>' || ch == '*') 1302 { 1303 /* %>X: right justify to EOL, left takes precedence 1304 * %*X: right justify to EOL, right takes precedence */ 1305 int soft = ch == '*'; 1306 int pl, pw; 1307 if ((pl = mutt_charlen (src, &pw)) <= 0) 1308 pl = pw = 1; 1309 1310 /* see if there's room to add content, else ignore */ 1311 if ((col < cols && wlen < destlen) || soft) 1312 { 1313 int pad; 1314 1315 /* get contents after padding */ 1316 mutt_FormatString (buf, sizeof (buf), 0, cols, src + pl, callback, data, flags); 1317 len = mutt_strlen (buf); 1318 wid = mutt_strwidth (buf); 1319 1320 pad = (cols - col - wid) / pw; 1321 if (pad >= 0) 1322 { 1323 /* try to consume as many columns as we can, if we don't have 1324 * memory for that, use as much memory as possible */ 1325 if (wlen + (pad * pl) + len > destlen) 1326 pad = (destlen > wlen + len) ? ((destlen - wlen - len) / pl) : 0; 1327 else 1328 { 1329 /* Add pre-spacing to make multi-column pad characters and 1330 * the contents after padding line up */ 1331 while ((col + (pad * pw) + wid < cols) && 1332 (wlen + (pad * pl) + len < destlen)) 1333 { 1334 *wptr++ = ' '; 1335 wlen++; 1336 col++; 1337 } 1338 } 1339 while (pad-- > 0) 1340 { 1341 memcpy (wptr, src, pl); 1342 wptr += pl; 1343 wlen += pl; 1344 col += pw; 1345 } 1346 } 1347 else if (soft && pad < 0) 1348 { 1349 int offset = ((flags & MUTT_FORMAT_ARROWCURSOR) && option (OPTARROWCURSOR)) ? 3 : 0; 1350 int avail_cols = (cols > offset) ? (cols - offset) : 0; 1351 /* \0-terminate dest for length computation in mutt_wstr_trunc() */ 1352 *wptr = 0; 1353 /* make sure right part is at most as wide as display */ 1354 len = mutt_wstr_trunc (buf, destlen, avail_cols, &wid); 1355 /* truncate left so that right part fits completely in */ 1356 wlen = mutt_wstr_trunc (dest, destlen - len, avail_cols - wid, &col); 1357 wptr = dest + wlen; 1358 /* Multi-column characters may be truncated in the middle. 1359 * Add spacing so the right hand side lines up. */ 1360 while ((col + wid < avail_cols) && (wlen + len < destlen)) 1361 { 1362 *wptr++ = ' '; 1363 wlen++; 1364 col++; 1365 } 1366 } 1367 if (len + wlen > destlen) 1368 len = mutt_wstr_trunc (buf, destlen - wlen, cols - col, NULL); 1369 memcpy (wptr, buf, len); 1370 wptr += len; 1371 wlen += len; 1372 col += wid; 1373 src += pl; 1374 } 1375 break; /* skip rest of input */ 1376 } 1377 else if (ch == '|') 1378 { 1379 /* pad to EOL */ 1380 int pl, pw, c; 1381 if ((pl = mutt_charlen (src, &pw)) <= 0) 1382 pl = pw = 1; 1383 1384 /* see if there's room to add content, else ignore */ 1385 if (col < cols && wlen < destlen) 1386 { 1387 c = (cols - col) / pw; 1388 if (c > 0 && wlen + (c * pl) > destlen) 1389 c = ((signed)(destlen - wlen)) / pl; 1390 while (c > 0) 1391 { 1392 memcpy (wptr, src, pl); 1393 wptr += pl; 1394 wlen += pl; 1395 col += pw; 1396 c--; 1397 } 1398 src += pl; 1399 } 1400 break; /* skip rest of input */ 1401 } 1402 else 1403 { 1404 short tolower = 0; 1405 short nodots = 0; 1406 1407 while (ch == '_' || ch == ':') 1408 { 1409 if (ch == '_') 1410 tolower = 1; 1411 else if (ch == ':') 1412 nodots = 1; 1413 1414 ch = *src++; 1415 } 1416 1417 /* use callback function to handle this case */ 1418 src = callback (buf, sizeof (buf), col, cols, ch, src, prefix, ifstring, elsestring, data, flags); 1419 1420 if (tolower) 1421 mutt_strlower (buf); 1422 if (nodots) 1423 { 1424 char *p = buf; 1425 for (; *p; p++) 1426 if (*p == '.') 1427 *p = '_'; 1428 } 1429 1430 if ((len = mutt_strlen (buf)) + wlen > destlen) 1431 len = mutt_wstr_trunc (buf, destlen - wlen, cols - col, NULL); 1432 1433 memcpy (wptr, buf, len); 1434 wptr += len; 1435 wlen += len; 1436 col += mutt_strwidth (buf); 1437 } 1438 } 1439 else if (*src == '\\') 1440 { 1441 if (!*++src) 1442 break; 1443 switch (*src) 1444 { 1445 case 'n': 1446 *wptr = '\n'; 1447 break; 1448 case 't': 1449 *wptr = '\t'; 1450 break; 1451 case 'r': 1452 *wptr = '\r'; 1453 break; 1454 case 'f': 1455 *wptr = '\f'; 1456 break; 1457 case 'v': 1458 *wptr = '\v'; 1459 break; 1460 default: 1461 *wptr = *src; 1462 break; 1463 } 1464 src++; 1465 wptr++; 1466 wlen++; 1467 col++; 1468 } 1469 else 1470 { 1471 int tmp, w; 1472 /* in case of error, simply copy byte */ 1473 if ((tmp = mutt_charlen (src, &w)) < 0) 1474 tmp = w = 1; 1475 if (tmp > 0 && wlen + tmp < destlen) 1476 { 1477 memcpy (wptr, src, tmp); 1478 wptr += tmp; 1479 src += tmp; 1480 wlen += tmp; 1481 col += w; 1482 } 1483 else 1484 { 1485 src += destlen - wlen; 1486 wlen = destlen; 1487 } 1488 } 1489 } 1490 *wptr = 0; 1491 1492#if 0 1493 if (flags & MUTT_FORMAT_MAKEPRINT) 1494 { 1495 /* Make sure that the string is printable by changing all non-printable 1496 chars to dots, or spaces for non-printable whitespace */ 1497 for (cp = dest ; *cp ; cp++) 1498 if (!IsPrint (*cp) && 1499 !((flags & MUTT_FORMAT_TREE) && (*cp <= MUTT_TREE_MAX))) 1500 *cp = isspace ((unsigned char) *cp) ? ' ' : '.'; 1501 } 1502#endif 1503} 1504 1505/* This function allows the user to specify a command to read stdout from in 1506 place of a normal file. If the last character in the string is a pipe (|), 1507 then we assume it is a command to run instead of a normal file. */ 1508FILE *mutt_open_read (const char *path, pid_t *thepid) 1509{ 1510 FILE *f; 1511 struct stat s; 1512 1513 int len = mutt_strlen (path); 1514 1515 if (path[len - 1] == '|') 1516 { 1517 /* read from a pipe */ 1518 1519 char *s = safe_strdup (path); 1520 1521 s[len - 1] = 0; 1522 mutt_endwin (NULL); 1523 *thepid = mutt_create_filter (s, NULL, &f, NULL); 1524 FREE (&s); 1525 } 1526 else 1527 { 1528 if (stat (path, &s) < 0) 1529 return (NULL); 1530 if (S_ISDIR (s.st_mode)) 1531 { 1532 errno = EINVAL; 1533 return (NULL); 1534 } 1535 f = fopen (path, "r"); 1536 *thepid = -1; 1537 } 1538 return (f); 1539} 1540 1541/* returns 0 if OK to proceed, -1 to abort, 1 to retry */ 1542int mutt_save_confirm (const char *s, struct stat *st) 1543{ 1544 char tmp[_POSIX_PATH_MAX]; 1545 int ret = 0; 1546 int rc; 1547 int magic = 0; 1548 1549 magic = mx_get_magic (s); 1550 1551#ifdef USE_POP 1552 if (magic == MUTT_POP) 1553 { 1554 mutt_error _("Can't save message to POP mailbox."); 1555 return 1; 1556 } 1557#endif 1558 1559 if (magic > 0 && !mx_access (s, W_OK)) 1560 { 1561 if (option (OPTCONFIRMAPPEND)) 1562 { 1563 snprintf (tmp, sizeof (tmp), _("Append messages to %s?"), s); 1564 if ((rc = mutt_yesorno (tmp, MUTT_YES)) == MUTT_NO) 1565 ret = 1; 1566 else if (rc == -1) 1567 ret = -1; 1568 } 1569 } 1570 1571 if (stat (s, st) != -1) 1572 { 1573 if (magic == -1) 1574 { 1575 mutt_error (_("%s is not a mailbox!"), s); 1576 return 1; 1577 } 1578 } 1579 else if (magic != MUTT_IMAP) 1580 { 1581 st->st_mtime = 0; 1582 st->st_atime = 0; 1583 1584 if (errno == ENOENT) 1585 { 1586 if (option (OPTCONFIRMCREATE)) 1587 { 1588 snprintf (tmp, sizeof (tmp), _("Create %s?"), s); 1589 if ((rc = mutt_yesorno (tmp, MUTT_YES)) == MUTT_NO) 1590 ret = 1; 1591 else if (rc == -1) 1592 ret = -1; 1593 } 1594 } 1595 else 1596 { 1597 mutt_perror (s); 1598 return 1; 1599 } 1600 } 1601 1602 mutt_window_clearline (MuttMessageWindow, 0); 1603 return (ret); 1604} 1605 1606void state_prefix_putc (char c, STATE *s) 1607{ 1608 if (s->flags & MUTT_PENDINGPREFIX) 1609 { 1610 state_reset_prefix (s); 1611 if (s->prefix) 1612 state_puts (s->prefix, s); 1613 } 1614 1615 state_putc (c, s); 1616 1617 if (c == '\n') 1618 state_set_prefix (s); 1619} 1620 1621int state_printf (STATE *s, const char *fmt, ...) 1622{ 1623 int rv; 1624 va_list ap; 1625 1626 va_start (ap, fmt); 1627 rv = vfprintf (s->fpout, fmt, ap); 1628 va_end (ap); 1629 1630 return rv; 1631} 1632 1633void state_mark_attach (STATE *s) 1634{ 1635 if ((s->flags & MUTT_DISPLAY) && !mutt_strcmp (Pager, "builtin")) 1636 state_puts (AttachmentMarker, s); 1637} 1638 1639void state_attach_puts (const char *t, STATE *s) 1640{ 1641 if (*t != '\n') state_mark_attach (s); 1642 while (*t) 1643 { 1644 state_putc (*t, s); 1645 if (*t++ == '\n' && *t) 1646 if (*t != '\n') state_mark_attach (s); 1647 } 1648} 1649 1650int state_putwc (wchar_t wc, STATE *s) 1651{ 1652 char mb[MB_LEN_MAX] = ""; 1653 int rc; 1654 1655 if ((rc = wcrtomb (mb, wc, NULL)) < 0) 1656 return rc; 1657 if (fputs (mb, s->fpout) == EOF) 1658 return -1; 1659 return 0; 1660} 1661 1662int state_putws (const wchar_t *ws, STATE *s) 1663{ 1664 const wchar_t *p = ws; 1665 1666 while (p && *p != L'\0') 1667 { 1668 if (state_putwc (*p, s) < 0) 1669 return -1; 1670 p++; 1671 } 1672 return 0; 1673} 1674 1675void mutt_display_sanitize (char *s) 1676{ 1677 for (; *s; s++) 1678 { 1679 if (!IsPrint (*s)) 1680 *s = '?'; 1681 } 1682} 1683 1684void mutt_sleep (short s) 1685{ 1686 if (SleepTime > s) 1687 sleep (SleepTime); 1688 else if (s) 1689 sleep(s); 1690} 1691 1692/* creates and initializes a BUFFER */ 1693BUFFER *mutt_buffer_new(void) { 1694 BUFFER *b; 1695 1696 b = safe_malloc(sizeof(BUFFER)); 1697 1698 mutt_buffer_init(b); 1699 1700 return b; 1701} 1702 1703/* initialize a new BUFFER */ 1704BUFFER *mutt_buffer_init (BUFFER *b) { 1705 memset(b, 0, sizeof(BUFFER)); 1706 return b; 1707} 1708 1709/* 1710 * Creates and initializes a BUFFER*. If passed an existing BUFFER*, 1711 * just initializes. Frees anything already in the buffer. Copies in 1712 * the seed string. 1713 * 1714 * Disregards the 'destroy' flag, which seems reserved for caller. 1715 * This is bad, but there's no apparent protocol for it. 1716 */ 1717BUFFER *mutt_buffer_from (char *seed) { 1718 BUFFER *b; 1719 1720 if (!seed) 1721 return NULL; 1722 1723 b = mutt_buffer_new (); 1724 b->data = safe_strdup(seed); 1725 b->dsize = mutt_strlen(seed); 1726 b->dptr = (char *) b->data + b->dsize; 1727 return b; 1728} 1729 1730int mutt_buffer_printf (BUFFER* buf, const char* fmt, ...) 1731{ 1732 va_list ap, ap_retry; 1733 int len, blen, doff; 1734 1735 va_start (ap, fmt); 1736 va_copy (ap_retry, ap); 1737 1738 if (!buf->dptr) 1739 buf->dptr = buf->data; 1740 1741 doff = buf->dptr - buf->data; 1742 blen = buf->dsize - doff; 1743 /* solaris 9 vsnprintf barfs when blen is 0 */ 1744 if (!blen) 1745 { 1746 blen = 128; 1747 buf->dsize += blen; 1748 safe_realloc (&buf->data, buf->dsize); 1749 buf->dptr = buf->data + doff; 1750 } 1751 if ((len = vsnprintf (buf->dptr, blen, fmt, ap)) >= blen) 1752 { 1753 blen = ++len - blen; 1754 if (blen < 128) 1755 blen = 128; 1756 buf->dsize += blen; 1757 safe_realloc (&buf->data, buf->dsize); 1758 buf->dptr = buf->data + doff; 1759 len = vsnprintf (buf->dptr, len, fmt, ap_retry); 1760 } 1761 if (len > 0) 1762 buf->dptr += len; 1763 1764 va_end (ap); 1765 va_end (ap_retry); 1766 1767 return len; 1768} 1769 1770void mutt_buffer_addstr (BUFFER* buf, const char* s) 1771{ 1772 mutt_buffer_add (buf, s, mutt_strlen (s)); 1773} 1774 1775void mutt_buffer_addch (BUFFER* buf, char c) 1776{ 1777 mutt_buffer_add (buf, &c, 1); 1778} 1779 1780void mutt_buffer_free (BUFFER **p) 1781{ 1782 if (!p || !*p) 1783 return; 1784 1785 FREE(&(*p)->data); 1786 /* dptr is just an offset to data and shouldn't be freed */ 1787 FREE(p); /* __FREE_CHECKED__ */ 1788} 1789 1790/* dynamically grows a BUFFER to accommodate s, in increments of 128 bytes. 1791 * Always one byte bigger than necessary for the null terminator, and 1792 * the buffer is always null-terminated */ 1793void mutt_buffer_add (BUFFER* buf, const char* s, size_t len) 1794{ 1795 size_t offset; 1796 1797 if (buf->dptr + len + 1 > buf->data + buf->dsize) 1798 { 1799 offset = buf->dptr - buf->data; 1800 buf->dsize += len < 128 ? 128 : len + 1; 1801 /* suppress compiler aliasing warning */ 1802 safe_realloc ((void**) (void*) &buf->data, buf->dsize); 1803 buf->dptr = buf->data + offset; 1804 } 1805 memcpy (buf->dptr, s, len); 1806 buf->dptr += len; 1807 *(buf->dptr) = '\0'; 1808} 1809 1810/* Decrease a file's modification time by 1 second */ 1811 1812time_t mutt_decrease_mtime (const char *f, struct stat *st) 1813{ 1814 struct utimbuf utim; 1815 struct stat _st; 1816 time_t mtime; 1817 1818 if (!st) 1819 { 1820 if (stat (f, &_st) == -1) 1821 return -1; 1822 st = &_st; 1823 } 1824 1825 if ((mtime = st->st_mtime) == time (NULL)) 1826 { 1827 mtime -= 1; 1828 utim.actime = mtime; 1829 utim.modtime = mtime; 1830 utime (f, &utim); 1831 } 1832 1833 return mtime; 1834} 1835 1836/* sets mtime of 'to' to mtime of 'from' */ 1837void mutt_set_mtime (const char* from, const char* to) 1838{ 1839 struct utimbuf utim; 1840 struct stat st; 1841 1842 if (stat (from, &st) != -1) 1843 { 1844 utim.actime = st.st_mtime; 1845 utim.modtime = st.st_mtime; 1846 utime (to, &utim); 1847 } 1848} 1849 1850const char *mutt_make_version (void) 1851{ 1852 static char vstring[STRING]; 1853 snprintf (vstring, sizeof (vstring), "Mutt %s (%s)", 1854 MUTT_VERSION, ReleaseDate); 1855 return vstring; 1856} 1857 1858REGEXP *mutt_compile_regexp (const char *s, int flags) 1859{ 1860 REGEXP *pp = safe_calloc (sizeof (REGEXP), 1); 1861 pp->pattern = safe_strdup (s); 1862 pp->rx = safe_calloc (sizeof (regex_t), 1); 1863 if (REGCOMP (pp->rx, NONULL(s), flags) != 0) 1864 mutt_free_regexp (&pp); 1865 1866 return pp; 1867} 1868 1869void mutt_free_regexp (REGEXP **pp) 1870{ 1871 FREE (&(*pp)->pattern); 1872 regfree ((*pp)->rx); 1873 FREE (&(*pp)->rx); 1874 FREE (pp); /* __FREE_CHECKED__ */ 1875} 1876 1877void mutt_free_rx_list (RX_LIST **list) 1878{ 1879 RX_LIST *p; 1880 1881 if (!list) return; 1882 while (*list) 1883 { 1884 p = *list; 1885 *list = (*list)->next; 1886 mutt_free_regexp (&p->rx); 1887 FREE (&p); 1888 } 1889} 1890 1891void mutt_free_spam_list (SPAM_LIST **list) 1892{ 1893 SPAM_LIST *p; 1894 1895 if (!list) return; 1896 while (*list) 1897 { 1898 p = *list; 1899 *list = (*list)->next; 1900 mutt_free_regexp (&p->rx); 1901 FREE (&p->template); 1902 FREE (&p); 1903 } 1904} 1905 1906int mutt_match_rx_list (const char *s, RX_LIST *l) 1907{ 1908 if (!s) return 0; 1909 1910 for (; l; l = l->next) 1911 { 1912 if (regexec (l->rx->rx, s, (size_t) 0, (regmatch_t *) 0, (int) 0) == 0) 1913 { 1914 dprint (5, (debugfile, "mutt_match_rx_list: %s matches %s\n", s, l->rx->pattern)); 1915 return 1; 1916 } 1917 } 1918 1919 return 0; 1920} 1921 1922/* Match a string against the patterns defined by the 'spam' command and output 1923 * the expanded format into `text` when there is a match. If textsize<=0, the 1924 * match is performed but the format is not expanded and no assumptions are made 1925 * about the value of `text` so it may be NULL. 1926 * 1927 * Returns 1 if the argument `s` matches a pattern in the spam list, otherwise 1928 * 0. */ 1929int mutt_match_spam_list (const char *s, SPAM_LIST *l, char *text, int textsize) 1930{ 1931 static regmatch_t *pmatch = NULL; 1932 static int nmatch = 0; 1933 int tlen = 0; 1934 char *p; 1935 1936 if (!s) return 0; 1937 1938 for (; l; l = l->next) 1939 { 1940 /* If this pattern needs more matches, expand pmatch. */ 1941 if (l->nmatch > nmatch) 1942 { 1943 safe_realloc (&pmatch, l->nmatch * sizeof(regmatch_t)); 1944 nmatch = l->nmatch; 1945 } 1946 1947 /* Does this pattern match? */ 1948 if (regexec (l->rx->rx, s, (size_t) l->nmatch, (regmatch_t *) pmatch, (int) 0) == 0) 1949 { 1950 dprint (5, (debugfile, "mutt_match_spam_list: %s matches %s\n", s, l->rx->pattern)); 1951 dprint (5, (debugfile, "mutt_match_spam_list: %d subs\n", (int)l->rx->rx->re_nsub)); 1952 1953 /* Copy template into text, with substitutions. */ 1954 for (p = l->template; *p && tlen < textsize - 1;) 1955 { 1956 /* backreference to pattern match substring, eg. %1, %2, etc) */ 1957 if (*p == '%') 1958 { 1959 char *e; /* used as pointer to end of integer backreference in strtol() call */ 1960 int n; 1961 1962 ++p; /* skip over % char */ 1963 n = strtol(p, &e, 10); 1964 /* Ensure that the integer conversion succeeded (e!=p) and bounds check. The upper bound check 1965 * should not strictly be necessary since add_to_spam_list() finds the largest value, and 1966 * the static array above is always large enough based on that value. */ 1967 if (e != p && n >= 0 && n <= l->nmatch && pmatch[n].rm_so != -1) { 1968 /* copy as much of the substring match as will fit in the output buffer, saving space for 1969 * the terminating nul char */ 1970 int idx; 1971 for (idx = pmatch[n].rm_so; (idx < pmatch[n].rm_eo) && (tlen < textsize - 1); ++idx) 1972 text[tlen++] = s[idx]; 1973 } 1974 p = e; /* skip over the parsed integer */ 1975 } 1976 else 1977 { 1978 text[tlen++] = *p++; 1979 } 1980 } 1981 /* tlen should always be less than textsize except when textsize<=0 1982 * because the bounds checks in the above code leave room for the 1983 * terminal nul char. This should avoid returning an unterminated 1984 * string to the caller. When textsize<=0 we make no assumption about 1985 * the validity of the text pointer. */ 1986 if (tlen < textsize) { 1987 text[tlen] = '\0'; 1988 dprint (5, (debugfile, "mutt_match_spam_list: \"%s\"\n", text)); 1989 } 1990 return 1; 1991 } 1992 } 1993 1994 return 0; 1995} 1996 1997void mutt_encode_path (char *dest, size_t dlen, const char *src) 1998{ 1999 char *p = safe_strdup (src); 2000 int rc = mutt_convert_string (&p, Charset, "utf-8", 0); 2001 /* `src' may be NULL, such as when called from the pop3 driver. */ 2002 strfcpy (dest, (rc == 0) ? NONULL(p) : NONULL(src), dlen); 2003 FREE (&p); 2004}