mutt stable branch with some hacks
at jcs 2516 lines 57 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 "version.h" 25#include "mutt.h" 26#include "mutt_curses.h" 27#include "mime.h" 28#include "mailbox.h" 29#include "mx.h" 30#include "url.h" 31 32#ifdef USE_IMAP 33#include "imap.h" 34#endif 35 36#include "mutt_crypt.h" 37 38#include <string.h> 39#include <ctype.h> 40#include <unistd.h> 41#include <stdlib.h> 42#include <sys/wait.h> 43#include <errno.h> 44#include <sys/stat.h> 45#include <fcntl.h> 46#include <time.h> 47#include <sys/types.h> 48#include <utime.h> 49#include <dirent.h> 50 51BODY *mutt_new_body (void) 52{ 53 BODY *p = (BODY *) safe_calloc (1, sizeof (BODY)); 54 55 p->disposition = DISPATTACH; 56 p->use_disp = 1; 57 return (p); 58} 59 60/* Modified by blong to accept a "suggestion" for file name. If 61 * that file exists, then construct one with unique name but 62 * keep any extension. This might fail, I guess. 63 * Renamed to mutt_adv_mktemp so I only have to change where it's 64 * called, and not all possible cases. 65 */ 66void mutt_adv_mktemp (BUFFER *buf) 67{ 68 BUFFER *prefix = NULL; 69 char *suffix; 70 struct stat sb; 71 72 if (!(buf->data && buf->data[0])) 73 { 74 mutt_buffer_mktemp (buf); 75 } 76 else 77 { 78 prefix = mutt_buffer_pool_get (); 79 mutt_buffer_strcpy (prefix, buf->data); 80 mutt_sanitize_filename (prefix->data, 1); 81 mutt_buffer_printf (buf, "%s/%s", NONULL (Tempdir), mutt_b2s (prefix)); 82 if (lstat (mutt_b2s (buf), &sb) == -1 && errno == ENOENT) 83 goto out; 84 85 if ((suffix = strrchr (prefix->data, '.')) != NULL) 86 { 87 *suffix = 0; 88 ++suffix; 89 } 90 mutt_buffer_mktemp_pfx_sfx (buf, mutt_b2s (prefix), suffix); 91 92 out: 93 mutt_buffer_pool_release (&prefix); 94 } 95} 96 97/* create a send-mode duplicate from a receive-mode body */ 98 99int mutt_copy_body (FILE *fp, BODY **tgt, BODY *src) 100{ 101 BUFFER *tmp = NULL; 102 BODY *b; 103 PARAMETER *par, **ppar; 104 short use_disp; 105 106 tmp = mutt_buffer_pool_get (); 107 108 if (src->filename) 109 { 110 use_disp = 1; 111 mutt_buffer_strcpy (tmp, src->filename); 112 } 113 else 114 { 115 use_disp = 0; 116 } 117 118 mutt_adv_mktemp (tmp); 119 if (mutt_save_attachment (fp, src, mutt_b2s (tmp), 0, NULL) == -1) 120 { 121 mutt_buffer_pool_release (&tmp); 122 return -1; 123 } 124 125 *tgt = mutt_new_body (); 126 b = *tgt; 127 128 memcpy (b, src, sizeof (BODY)); 129 b->parts = NULL; 130 b->next = NULL; 131 132 b->filename = safe_strdup (mutt_b2s (tmp)); 133 b->use_disp = use_disp; 134 b->unlink = 1; 135 136 if (mutt_is_text_part (b)) 137 b->noconv = 1; 138 139 b->xtype = safe_strdup (b->xtype); 140 b->subtype = safe_strdup (b->subtype); 141 b->form_name = safe_strdup (b->form_name); 142 b->d_filename = safe_strdup (b->d_filename); 143 /* mutt_adv_mktemp() will mangle the filename in tmp, 144 * so preserve it in d_filename */ 145 if (!b->d_filename && use_disp) 146 b->d_filename = safe_strdup (src->filename); 147 b->description = safe_strdup (b->description); 148 149 /* 150 * we don't seem to need the HEADER structure currently. 151 * XXX - this may change in the future 152 */ 153 154 if (b->hdr) b->hdr = NULL; 155 156 /* copy parameters */ 157 for (par = b->parameter, ppar = &b->parameter; par; ppar = &(*ppar)->next, par = par->next) 158 { 159 *ppar = mutt_new_parameter (); 160 (*ppar)->attribute = safe_strdup (par->attribute); 161 (*ppar)->value = safe_strdup (par->value); 162 } 163 164 mutt_stamp_attachment (b); 165 166 mutt_buffer_pool_release (&tmp); 167 return 0; 168} 169 170 171 172void mutt_free_body (BODY **p) 173{ 174 BODY *a = *p, *b; 175 176 while (a) 177 { 178 b = a; 179 a = a->next; 180 181 if (b->parameter) 182 mutt_free_parameter (&b->parameter); 183 if (b->filename) 184 { 185 if (b->unlink) 186 unlink (b->filename); 187 dprint (1, (debugfile, "mutt_free_body: %sunlinking %s.\n", 188 b->unlink ? "" : "not ", b->filename)); 189 } 190 191 FREE (&b->filename); 192 FREE (&b->d_filename); 193 FREE (&b->charset); 194 FREE (&b->content); 195 FREE (&b->xtype); 196 FREE (&b->subtype); 197 FREE (&b->description); 198 FREE (&b->form_name); 199 200 if (b->hdr) 201 { 202 /* Don't free twice (b->hdr->content = b->parts) */ 203 b->hdr->content = NULL; 204 mutt_free_header(&b->hdr); 205 } 206 207 mutt_free_envelope (&b->mime_headers); 208 209 if (b->parts) 210 mutt_free_body (&b->parts); 211 212 FREE (&b); 213 } 214 215 *p = 0; 216} 217 218void mutt_free_parameter (PARAMETER **p) 219{ 220 PARAMETER *t = *p; 221 PARAMETER *o; 222 223 while (t) 224 { 225 FREE (&t->attribute); 226 FREE (&t->value); 227 o = t; 228 t = t->next; 229 FREE (&o); 230 } 231 *p = 0; 232} 233 234LIST *mutt_add_list (LIST *head, const char *data) 235{ 236 size_t len = mutt_strlen (data); 237 238 return mutt_add_list_n (head, data, len ? len + 1 : 0); 239} 240 241LIST *mutt_add_list_n (LIST *head, const void *data, size_t len) 242{ 243 LIST *tmp; 244 245 for (tmp = head; tmp && tmp->next; tmp = tmp->next) 246 ; 247 if (tmp) 248 { 249 tmp->next = safe_malloc (sizeof (LIST)); 250 tmp = tmp->next; 251 } 252 else 253 head = tmp = safe_malloc (sizeof (LIST)); 254 255 tmp->data = safe_malloc (len); 256 if (len) 257 memcpy (tmp->data, data, len); 258 tmp->next = NULL; 259 return head; 260} 261 262LIST *mutt_find_list (LIST *l, const char *data) 263{ 264 LIST *p = l; 265 266 while (p) 267 { 268 if (data == p->data) 269 return p; 270 if (data && p->data && mutt_strcmp (p->data, data) == 0) 271 return p; 272 p = p->next; 273 } 274 return NULL; 275} 276 277int mutt_remove_from_rx_list (RX_LIST **l, const char *str) 278{ 279 RX_LIST *p, *last = NULL; 280 int rv = -1; 281 282 if (mutt_strcmp ("*", str) == 0) 283 { 284 mutt_free_rx_list (l); /* ``unCMD *'' means delete all current entries */ 285 rv = 0; 286 } 287 else 288 { 289 p = *l; 290 last = NULL; 291 while (p) 292 { 293 if (ascii_strcasecmp (str, p->rx->pattern) == 0) 294 { 295 mutt_free_regexp (&p->rx); 296 if (last) 297 last->next = p->next; 298 else 299 (*l) = p->next; 300 FREE (&p); 301 rv = 0; 302 } 303 else 304 { 305 last = p; 306 p = p->next; 307 } 308 } 309 } 310 return (rv); 311} 312 313void mutt_free_list (LIST **list) 314{ 315 LIST *p; 316 317 if (!list) return; 318 while (*list) 319 { 320 p = *list; 321 *list = (*list)->next; 322 FREE (&p->data); 323 FREE (&p); 324 } 325} 326 327void mutt_free_list_generic(LIST **list, void (*data_free)(char **)) 328{ 329 LIST *p; 330 331 /* wrap mutt_free_list if no data_free function was provided */ 332 if (data_free == NULL) 333 { 334 mutt_free_list(list); 335 return; 336 } 337 338 if (!list) return; 339 while (*list) 340 { 341 p = *list; 342 *list = (*list)->next; 343 data_free(&p->data); 344 FREE (&p); 345 } 346} 347 348LIST *mutt_copy_list (LIST *p) 349{ 350 LIST *t, *r=NULL, *l=NULL; 351 352 for (; p; p = p->next) 353 { 354 t = (LIST *) safe_malloc (sizeof (LIST)); 355 t->data = safe_strdup (p->data); 356 t->next = NULL; 357 if (l) 358 { 359 r->next = t; 360 r = r->next; 361 } 362 else 363 l = r = t; 364 } 365 return (l); 366} 367 368HEADER *mutt_dup_header(HEADER *h) 369{ 370 HEADER *hnew; 371 372 hnew = mutt_new_header(); 373 memcpy(hnew, h, sizeof (HEADER)); 374 return hnew; 375} 376 377void mutt_free_header (HEADER **h) 378{ 379 if (!h || !*h) return; 380 mutt_free_envelope (&(*h)->env); 381 mutt_free_body (&(*h)->content); 382 FREE (&(*h)->maildir_flags); 383 FREE (&(*h)->tree); 384 FREE (&(*h)->path); 385#ifdef MIXMASTER 386 mutt_free_list (&(*h)->chain); 387#endif 388#if defined USE_POP || defined USE_IMAP 389 FREE (&(*h)->data); 390#endif 391 FREE (h); /* __FREE_CHECKED__ */ 392} 393 394/* returns true if the header contained in "s" is in list "t" */ 395int mutt_matches_ignore (const char *s, LIST *t) 396{ 397 for (; t; t = t->next) 398 { 399 if (!ascii_strncasecmp (s, t->data, mutt_strlen (t->data)) || *t->data == '*') 400 return 1; 401 } 402 return 0; 403} 404 405char *mutt_expand_path (char *s, size_t slen) 406{ 407 return _mutt_expand_path (s, slen, 0); 408} 409 410char *_mutt_expand_path (char *s, size_t slen, int rx) 411{ 412 BUFFER *s_buf; 413 414 s_buf = mutt_buffer_pool_get (); 415 416 mutt_buffer_addstr (s_buf, NONULL (s)); 417 _mutt_buffer_expand_path (s_buf, rx); 418 strfcpy (s, mutt_b2s (s_buf), slen); 419 420 mutt_buffer_pool_release (&s_buf); 421 422 return s; 423} 424 425void mutt_buffer_expand_path (BUFFER *src) 426{ 427 _mutt_buffer_expand_path (src, 0); 428} 429 430void _mutt_buffer_expand_path (BUFFER *src, int rx) 431{ 432 BUFFER *p, *q, *tmp; 433 const char *s, *tail = ""; 434 char *t; 435 int recurse = 0; 436 437 p = mutt_buffer_pool_get (); 438 q = mutt_buffer_pool_get (); 439 tmp = mutt_buffer_pool_get (); 440 441 do 442 { 443 recurse = 0; 444 s = mutt_b2s (src); 445 446 switch (*s) 447 { 448 case '~': 449 { 450 if (*(s + 1) == '/' || *(s + 1) == 0) 451 { 452 mutt_buffer_strcpy (p, NONULL(Homedir)); 453 tail = s + 1; 454 } 455 else 456 { 457 struct passwd *pw; 458 if ((t = strchr (s + 1, '/'))) 459 *t = 0; 460 461 if ((pw = getpwnam (s + 1))) 462 { 463 mutt_buffer_strcpy (p, pw->pw_dir); 464 if (t) 465 { 466 *t = '/'; 467 tail = t; 468 } 469 else 470 tail = ""; 471 } 472 else 473 { 474 /* user not found! */ 475 if (t) 476 *t = '/'; 477 mutt_buffer_clear (p); 478 tail = s; 479 } 480 } 481 } 482 break; 483 484 case '=': 485 case '+': 486 { 487#ifdef USE_IMAP 488 /* if folder = {host} or imap[s]://host/: don't append slash */ 489 if (mx_is_imap (NONULL (Maildir)) && 490 (Maildir[strlen (Maildir) - 1] == '}' || 491 Maildir[strlen (Maildir) - 1] == '/')) 492 mutt_buffer_strcpy (p, NONULL (Maildir)); 493 else 494#endif 495 if (Maildir && Maildir[strlen (Maildir) - 1] == '/') 496 mutt_buffer_strcpy (p, NONULL (Maildir)); 497 else 498 mutt_buffer_printf (p, "%s/", NONULL (Maildir)); 499 500 tail = s + 1; 501 } 502 break; 503 504 /* elm compatibility, @ expands alias to user name */ 505 506 case '@': 507 { 508 HEADER *h; 509 ADDRESS *alias; 510 511 if ((alias = mutt_lookup_alias (s + 1))) 512 { 513 h = mutt_new_header(); 514 h->env = mutt_new_envelope(); 515 h->env->from = h->env->to = alias; 516 517 /* TODO: fix mutt_default_save() to use BUFFER */ 518 mutt_buffer_increase_size (p, _POSIX_PATH_MAX); 519 mutt_default_save (p->data, p->dsize, h); 520 mutt_buffer_fix_dptr (p); 521 522 h->env->from = h->env->to = NULL; 523 mutt_free_header (&h); 524 /* Avoid infinite recursion if the resulting folder starts with '@' */ 525 if (*(mutt_b2s (p)) != '@') 526 recurse = 1; 527 528 tail = ""; 529 } 530 } 531 break; 532 533 case '>': 534 { 535 mutt_buffer_strcpy (p, NONULL(Inbox)); 536 tail = s + 1; 537 } 538 break; 539 540 case '<': 541 { 542 mutt_buffer_strcpy (p, NONULL(Outbox)); 543 tail = s + 1; 544 } 545 break; 546 547 case '!': 548 { 549 if (*(s+1) == '!') 550 { 551 mutt_buffer_strcpy (p, NONULL(LastFolder)); 552 tail = s + 2; 553 } 554 else 555 { 556 mutt_buffer_strcpy (p, NONULL(Spoolfile)); 557 tail = s + 1; 558 } 559 } 560 break; 561 562 case '-': 563 { 564 mutt_buffer_strcpy (p, NONULL(LastFolder)); 565 tail = s + 1; 566 } 567 break; 568 569 case '^': 570 { 571 mutt_buffer_strcpy (p, NONULL(CurrentFolder)); 572 tail = s + 1; 573 } 574 break; 575 576 default: 577 { 578 mutt_buffer_clear (p); 579 tail = s; 580 } 581 } 582 583 if (rx && *(mutt_b2s (p)) && !recurse) 584 { 585 mutt_rx_sanitize_string (q, mutt_b2s (p)); 586 mutt_buffer_printf (tmp, "%s%s", mutt_b2s (q), tail); 587 } 588 else 589 mutt_buffer_printf (tmp, "%s%s", mutt_b2s (p), tail); 590 591 mutt_buffer_strcpy (src, mutt_b2s (tmp)); 592 } 593 while (recurse); 594 595 mutt_buffer_pool_release (&p); 596 mutt_buffer_pool_release (&q); 597 mutt_buffer_pool_release (&tmp); 598 599#ifdef USE_IMAP 600 /* Rewrite IMAP path in canonical form - aids in string comparisons of 601 * folders. May possibly fail, in which case s should be the same. */ 602 if (mx_is_imap (mutt_b2s (src))) 603 imap_expand_path (src); 604#endif 605} 606 607/* Extract the real name from /etc/passwd's GECOS field. 608 * When set, honor the regular expression in GecosMask, 609 * otherwise assume that the GECOS field is a 610 * comma-separated list. 611 * Replace "&" by a capitalized version of the user's login 612 * name. 613 */ 614 615char *mutt_gecos_name (char *dest, size_t destlen, struct passwd *pw) 616{ 617 regmatch_t pat_match[1]; 618 size_t pwnl; 619 int idx; 620 char *p; 621 622 if (!pw || !pw->pw_gecos) 623 return NULL; 624 625 memset (dest, 0, destlen); 626 627 if (GecosMask.rx) 628 { 629 if (regexec (GecosMask.rx, pw->pw_gecos, 1, pat_match, 0) == 0) 630 strfcpy (dest, pw->pw_gecos + pat_match[0].rm_so, 631 MIN (pat_match[0].rm_eo - pat_match[0].rm_so + 1, destlen)); 632 } 633 else if ((p = strchr (pw->pw_gecos, ','))) 634 strfcpy (dest, pw->pw_gecos, MIN (destlen, p - pw->pw_gecos + 1)); 635 else 636 strfcpy (dest, pw->pw_gecos, destlen); 637 638 pwnl = strlen (pw->pw_name); 639 640 for (idx = 0; dest[idx]; idx++) 641 { 642 if (dest[idx] == '&') 643 { 644 memmove (&dest[idx + pwnl], &dest[idx + 1], 645 MAX((ssize_t)(destlen - idx - pwnl - 1), 0)); 646 memcpy (&dest[idx], pw->pw_name, MIN(destlen - idx - 1, pwnl)); 647 dest[idx] = toupper ((unsigned char) dest[idx]); 648 } 649 } 650 651 return dest; 652} 653 654 655char *mutt_get_parameter (const char *s, PARAMETER *p) 656{ 657 for (; p; p = p->next) 658 if (ascii_strcasecmp (s, p->attribute) == 0) 659 return (p->value); 660 661 return NULL; 662} 663 664void mutt_set_parameter (const char *attribute, const char *value, PARAMETER **p) 665{ 666 PARAMETER *q; 667 668 if (!value) 669 { 670 mutt_delete_parameter (attribute, p); 671 return; 672 } 673 674 for (q = *p; q; q = q->next) 675 { 676 if (ascii_strcasecmp (attribute, q->attribute) == 0) 677 { 678 mutt_str_replace (&q->value, value); 679 return; 680 } 681 } 682 683 q = mutt_new_parameter(); 684 q->attribute = safe_strdup(attribute); 685 q->value = safe_strdup(value); 686 q->next = *p; 687 *p = q; 688} 689 690void mutt_delete_parameter (const char *attribute, PARAMETER **p) 691{ 692 PARAMETER *q; 693 694 for (q = *p; q; p = &q->next, q = q->next) 695 { 696 if (ascii_strcasecmp (attribute, q->attribute) == 0) 697 { 698 *p = q->next; 699 q->next = NULL; 700 mutt_free_parameter (&q); 701 return; 702 } 703 } 704} 705 706/* returns 1 if Mutt can't display this type of data, 0 otherwise */ 707int mutt_needs_mailcap (BODY *m) 708{ 709 switch (m->type) 710 { 711 case TYPETEXT: 712 /* we can display any text, overridable by auto_view */ 713 return 0; 714 break; 715 716 case TYPEAPPLICATION: 717 if ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp(m)) 718 return 0; 719 if ((WithCrypto & APPLICATION_SMIME) && mutt_is_application_smime(m)) 720 return 0; 721 break; 722 723 case TYPEMULTIPART: 724 case TYPEMESSAGE: 725 return 0; 726 } 727 728 return 1; 729} 730 731int mutt_is_text_part (BODY *b) 732{ 733 int t = b->type; 734 char *s = b->subtype; 735 736 if ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b)) 737 return 0; 738 739 if (t == TYPETEXT) 740 return 1; 741 742 if (t == TYPEMESSAGE) 743 { 744 if (!ascii_strcasecmp ("delivery-status", s)) 745 return 1; 746 } 747 748 if ((WithCrypto & APPLICATION_PGP) && t == TYPEAPPLICATION) 749 { 750 if (!ascii_strcasecmp ("pgp-keys", s)) 751 return 1; 752 } 753 754 return 0; 755} 756 757#ifdef USE_AUTOCRYPT 758void mutt_free_autocrypthdr (AUTOCRYPTHDR **p) 759{ 760 AUTOCRYPTHDR *cur; 761 762 if (!p) 763 return; 764 765 while (*p) 766 { 767 cur = *p; 768 *p = (*p)->next; 769 FREE (&cur->addr); 770 FREE (&cur->keydata); 771 FREE (&cur); 772 } 773} 774#endif 775 776void mutt_free_envelope (ENVELOPE **p) 777{ 778 if (!*p) return; 779 rfc822_free_address (&(*p)->return_path); 780 rfc822_free_address (&(*p)->from); 781 rfc822_free_address (&(*p)->to); 782 rfc822_free_address (&(*p)->cc); 783 rfc822_free_address (&(*p)->bcc); 784 rfc822_free_address (&(*p)->sender); 785 rfc822_free_address (&(*p)->reply_to); 786 rfc822_free_address (&(*p)->mail_followup_to); 787 788 FREE (&(*p)->list_post); 789 FREE (&(*p)->subject); 790 /* real_subj is just an offset to subject and shouldn't be freed */ 791 FREE (&(*p)->disp_subj); 792 FREE (&(*p)->message_id); 793 FREE (&(*p)->supersedes); 794 FREE (&(*p)->date); 795 FREE (&(*p)->x_label); 796 797 mutt_buffer_free (&(*p)->spam); 798 799 mutt_free_list (&(*p)->references); 800 mutt_free_list (&(*p)->in_reply_to); 801 mutt_free_list (&(*p)->userhdrs); 802 803#ifdef USE_AUTOCRYPT 804 mutt_free_autocrypthdr (&(*p)->autocrypt); 805 mutt_free_autocrypthdr (&(*p)->autocrypt_gossip); 806#endif 807 808 FREE (p); /* __FREE_CHECKED__ */ 809} 810 811/* move all the headers from extra not present in base into base */ 812void mutt_merge_envelopes(ENVELOPE* base, ENVELOPE** extra) 813{ 814 /* copies each existing element if necessary, and sets the element 815 * to NULL in the source so that mutt_free_envelope doesn't leave us 816 * with dangling pointers. */ 817#define MOVE_ELEM(h) if (!base->h) { base->h = (*extra)->h; (*extra)->h = NULL; } 818 MOVE_ELEM(return_path); 819 MOVE_ELEM(from); 820 MOVE_ELEM(to); 821 MOVE_ELEM(cc); 822 MOVE_ELEM(bcc); 823 MOVE_ELEM(sender); 824 MOVE_ELEM(reply_to); 825 MOVE_ELEM(mail_followup_to); 826 MOVE_ELEM(list_post); 827 MOVE_ELEM(message_id); 828 MOVE_ELEM(supersedes); 829 MOVE_ELEM(date); 830 if (!(base->changed & MUTT_ENV_CHANGED_XLABEL)) 831 { 832 MOVE_ELEM(x_label); 833 } 834 if (!(base->changed & MUTT_ENV_CHANGED_REFS)) 835 { 836 MOVE_ELEM(references); 837 } 838 if (!(base->changed & MUTT_ENV_CHANGED_IRT)) 839 { 840 MOVE_ELEM(in_reply_to); 841 } 842 843 /* real_subj is subordinate to subject */ 844 if (!base->subject) 845 { 846 base->subject = (*extra)->subject; 847 base->real_subj = (*extra)->real_subj; 848 base->disp_subj = (*extra)->disp_subj; 849 (*extra)->subject = NULL; 850 (*extra)->real_subj = NULL; 851 (*extra)->disp_subj = NULL; 852 } 853 /* spam and user headers should never be hashed, and the new envelope may 854 * have better values. Use new versions regardless. */ 855 mutt_buffer_free (&base->spam); 856 mutt_free_list (&base->userhdrs); 857 MOVE_ELEM(spam); 858 MOVE_ELEM(userhdrs); 859#undef MOVE_ELEM 860 861 mutt_free_envelope(extra); 862} 863 864void _mutt_buffer_mktemp (BUFFER *buf, const char *prefix, const char *suffix, 865 const char *src, int line) 866{ 867 mutt_buffer_printf (buf, "%s/%s-%s-%d-%d-%ld%ld%s%s", 868 NONULL (Tempdir), NONULL (prefix), NONULL (Hostname), 869 (int) getuid (), (int) getpid (), random (), random (), 870 suffix ? "." : "", NONULL (suffix)); 871 dprint (3, (debugfile, "%s:%d: mutt_mktemp returns \"%s\".\n", src, line, mutt_b2s (buf))); 872 if (unlink (mutt_b2s (buf)) && errno != ENOENT) 873 dprint (1, (debugfile, "%s:%d: ERROR: unlink(\"%s\"): %s (errno %d)\n", 874 src, line, mutt_b2s (buf), strerror (errno), errno)); 875} 876 877void _mutt_mktemp (char *s, size_t slen, const char *prefix, const char *suffix, 878 const char *src, int line) 879{ 880 size_t n = snprintf (s, slen, "%s/%s-%s-%d-%d-%ld%ld%s%s", 881 NONULL (Tempdir), NONULL (prefix), NONULL (Hostname), 882 (int) getuid (), (int) getpid (), random (), random (), 883 suffix ? "." : "", NONULL (suffix)); 884 if (n >= slen) 885 dprint (1, (debugfile, "%s:%d: ERROR: insufficient buffer space to hold temporary filename! slen=%zu but need %zu\n", 886 src, line, slen, n)); 887 dprint (3, (debugfile, "%s:%d: mutt_mktemp returns \"%s\".\n", src, line, s)); 888 if (unlink (s) && errno != ENOENT) 889 dprint (1, (debugfile, "%s:%d: ERROR: unlink(\"%s\"): %s (errno %d)\n", src, line, s, strerror (errno), errno)); 890} 891 892/* these characters must be escaped in regular expressions */ 893 894static const char rx_special_chars[] = "^.[$()|*+?{\\"; 895 896int mutt_rx_sanitize_string (BUFFER *dest, const char *src) 897{ 898 mutt_buffer_clear (dest); 899 while (*src) 900 { 901 if (strchr (rx_special_chars, *src)) 902 mutt_buffer_addch (dest, '\\'); 903 mutt_buffer_addch (dest, *src++); 904 } 905 return 0; 906} 907 908void mutt_free_alias (ALIAS **p) 909{ 910 ALIAS *t; 911 912 while (*p) 913 { 914 t = *p; 915 *p = (*p)->next; 916 mutt_alias_delete_reverse (t); 917 FREE (&t->name); 918 rfc822_free_address (&t->addr); 919 FREE (&t); 920 } 921} 922 923void mutt_buffer_pretty_mailbox (BUFFER *s) 924{ 925 /* This reduces the size of the BUFFER, so we can pass it through. 926 * We adjust the size just to make sure s->data is not NULL though */ 927 mutt_buffer_increase_size (s, _POSIX_PATH_MAX); 928 mutt_pretty_mailbox (s->data, s->dsize); 929 mutt_buffer_fix_dptr (s); 930} 931 932/* collapse the pathname using ~ or = when possible */ 933void mutt_pretty_mailbox (char *s, size_t buflen) 934{ 935 char *p = s, *q = s; 936 size_t len; 937 url_scheme_t scheme; 938 char tmp[PATH_MAX]; 939 940 scheme = url_check_scheme (s); 941 942#ifdef USE_IMAP 943 if (scheme == U_IMAP || scheme == U_IMAPS) 944 { 945 imap_pretty_mailbox (s, buflen); 946 return; 947 } 948#endif 949 950 /* if s is an url, only collapse path component */ 951 if (scheme != U_UNKNOWN) 952 { 953 p = strchr(s, ':')+1; 954 if (!strncmp (p, "//", 2)) 955 q = strchr (p+2, '/'); 956 if (!q) 957 q = strchr (p, '\0'); 958 p = q; 959 } 960 961 /* cleanup path */ 962 if (strstr (p, "//") || strstr (p, "/./")) 963 { 964 /* first attempt to collapse the pathname, this is more 965 * lightweight than realpath() and doesn't resolve links 966 */ 967 while (*p) 968 { 969 if (*p == '/' && p[1] == '/') 970 { 971 *q++ = '/'; 972 p += 2; 973 } 974 else if (p[0] == '/' && p[1] == '.' && p[2] == '/') 975 { 976 *q++ = '/'; 977 p += 3; 978 } 979 else 980 *q++ = *p++; 981 } 982 *q = 0; 983 } 984 else if (strstr (p, "..") && 985 (scheme == U_UNKNOWN || scheme == U_FILE) && 986 realpath (p, tmp)) 987 strfcpy (p, tmp, buflen - (p - s)); 988 989 if (mutt_strncmp (s, Maildir, (len = mutt_strlen (Maildir))) == 0 && 990 s[len] == '/') 991 { 992 *s++ = '='; 993 memmove (s, s + len, mutt_strlen (s + len) + 1); 994 } 995 else if (mutt_strncmp (s, Homedir, (len = mutt_strlen (Homedir))) == 0 && 996 s[len] == '/') 997 { 998 *s++ = '~'; 999 memmove (s, s + len - 1, mutt_strlen (s + len - 1) + 1); 1000 } 1001} 1002 1003void mutt_pretty_size (char *s, size_t len, LOFF_T n) 1004{ 1005 if (option (OPTSIZESHOWBYTES) && (n < 1024)) 1006 snprintf (s, len, "%d", (int)n); 1007 else if (n == 0) 1008 strfcpy (s, 1009 option (OPTSIZEUNITSONLEFT) ? "K0" : "0K", 1010 len); 1011 else if (option (OPTSIZESHOWFRACTIONS) && (n < 10189)) /* 0.1K - 9.9K */ 1012 { 1013 snprintf (s, len, 1014 option (OPTSIZEUNITSONLEFT) ? "K%3.1f" : "%3.1fK", 1015 (n < 103) ? 0.1 : n / 1024.0); 1016 } 1017 else if (!option (OPTSIZESHOWMB) || (n < 1023949)) /* 10K - 999K */ 1018 { 1019 /* 51 is magic which causes 10189/10240 to be rounded up to 10 */ 1020 snprintf (s, len, 1021 option (OPTSIZEUNITSONLEFT) ? ("K" OFF_T_FMT) : (OFF_T_FMT "K"), 1022 (n + 51) / 1024); 1023 } 1024 else if (option (OPTSIZESHOWFRACTIONS) && (n < 10433332)) /* 1.0M - 9.9M */ 1025 { 1026 snprintf (s, len, 1027 option (OPTSIZEUNITSONLEFT) ? "M%3.1f" : "%3.1fM", 1028 n / 1048576.0); 1029 } 1030 else /* 10M+ */ 1031 { 1032 /* (10433332 + 52428) / 1048576 = 10 */ 1033 snprintf (s, len, 1034 option (OPTSIZEUNITSONLEFT) ? ("M" OFF_T_FMT) : (OFF_T_FMT "M"), 1035 (n + 52428) / 1048576); 1036 } 1037} 1038 1039void _mutt_buffer_quote_filename (BUFFER *d, const char *f, int add_outer) 1040{ 1041 mutt_buffer_clear (d); 1042 1043 if (!f) 1044 return; 1045 1046 if (add_outer) 1047 mutt_buffer_addch (d, '\''); 1048 1049 for (; *f; f++) 1050 { 1051 if (*f == '\'' || *f == '`') 1052 { 1053 mutt_buffer_addch (d, '\''); 1054 mutt_buffer_addch (d, '\\'); 1055 mutt_buffer_addch (d, *f); 1056 mutt_buffer_addch (d, '\''); 1057 } 1058 else 1059 mutt_buffer_addch (d, *f); 1060 } 1061 1062 if (add_outer) 1063 mutt_buffer_addch (d, '\''); 1064} 1065 1066static const char safe_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+@{}._-:%/"; 1067 1068void mutt_buffer_sanitize_filename (BUFFER *d, const char *f, short slash) 1069{ 1070 mutt_buffer_clear (d); 1071 1072 if (!f) 1073 return; 1074 1075 for (; *f; f++) 1076 { 1077 if ((slash && *f == '/') || !strchr (safe_chars, *f)) 1078 mutt_buffer_addch (d, '_'); 1079 else 1080 mutt_buffer_addch (d, *f); 1081 } 1082} 1083 1084void mutt_expand_file_fmt (BUFFER *dest, const char *fmt, const char *src) 1085{ 1086 BUFFER *tmp; 1087 1088 tmp = mutt_buffer_pool_get (); 1089 mutt_buffer_quote_filename (tmp, src); 1090 mutt_expand_fmt (dest, fmt, mutt_b2s (tmp)); 1091 mutt_buffer_pool_release (&tmp); 1092} 1093 1094void mutt_expand_fmt (BUFFER *dest, const char *fmt, const char *src) 1095{ 1096 const char *p; 1097 int found = 0; 1098 1099 mutt_buffer_clear (dest); 1100 1101 for (p = fmt; *p; p++) 1102 { 1103 if (*p == '%') 1104 { 1105 switch (p[1]) 1106 { 1107 case '%': 1108 mutt_buffer_addch (dest, *p++); 1109 break; 1110 case 's': 1111 found = 1; 1112 mutt_buffer_addstr (dest, src); 1113 p++; 1114 break; 1115 default: 1116 mutt_buffer_addch (dest, *p); 1117 break; 1118 } 1119 } 1120 else 1121 { 1122 mutt_buffer_addch (dest, *p); 1123 } 1124 } 1125 1126 if (!found) 1127 { 1128 mutt_buffer_addch (dest, ' '); 1129 mutt_buffer_addstr (dest, src); 1130 } 1131} 1132 1133/* return 0 on success, -1 on abort, 1 on error */ 1134int mutt_check_overwrite (const char *attname, const char *path, 1135 BUFFER *fname, int *append, char **directory) 1136{ 1137 int rc = 0; 1138 BUFFER *tmp = NULL; 1139 struct stat st; 1140 1141 mutt_buffer_strcpy (fname, path); 1142 if (access (mutt_b2s (fname), F_OK) != 0) 1143 return 0; 1144 if (stat (mutt_b2s (fname), &st) != 0) 1145 return -1; 1146 if (S_ISDIR (st.st_mode)) 1147 { 1148 if (directory) 1149 { 1150 switch (mutt_multi_choice 1151 /* L10N: 1152 Means "The path you specified as the destination file is a directory." 1153 See the msgid "Save to file: " (alias.c, recvattach.c) */ 1154 (_("File is a directory, save under it? [(y)es, (n)o, (a)ll]"), _("yna"))) 1155 { 1156 case 3: /* all */ 1157 mutt_str_replace (directory, mutt_b2s (fname)); 1158 break; 1159 case 1: /* yes */ 1160 FREE (directory); /* __FREE_CHECKED__ */ 1161 break; 1162 case -1: /* abort */ 1163 FREE (directory); /* __FREE_CHECKED__ */ 1164 return -1; 1165 case 2: /* no */ 1166 FREE (directory); /* __FREE_CHECKED__ */ 1167 return 1; 1168 } 1169 } 1170 /* L10N: 1171 Means "The path you specified as the destination file is a directory." 1172 See the msgid "Save to file: " (alias.c, recvattach.c) */ 1173 else if ((rc = mutt_yesorno (_("File is a directory, save under it?"), MUTT_YES)) != MUTT_YES) 1174 return (rc == MUTT_NO) ? 1 : -1; 1175 1176 tmp = mutt_buffer_pool_get (); 1177 mutt_buffer_strcpy (tmp, mutt_basename (NONULL (attname))); 1178 if ((mutt_buffer_get_field (_("File under directory: "), tmp, 1179 MUTT_FILE | MUTT_CLEAR) != 0) || 1180 !mutt_buffer_len (tmp)) 1181 { 1182 mutt_buffer_pool_release (&tmp); 1183 return (-1); 1184 } 1185 mutt_buffer_concat_path (fname, path, mutt_b2s (tmp)); 1186 mutt_buffer_pool_release (&tmp); 1187 } 1188 1189 if (*append == 0 && access (mutt_b2s (fname), F_OK) == 0) 1190 { 1191 switch (mutt_multi_choice 1192 (_("File exists, (o)verwrite, (a)ppend, or (c)ancel?"), _("oac"))) 1193 { 1194 case -1: /* abort */ 1195 return -1; 1196 case 3: /* cancel */ 1197 return 1; 1198 1199 case 2: /* append */ 1200 *append = MUTT_SAVE_APPEND; 1201 break; 1202 case 1: /* overwrite */ 1203 *append = MUTT_SAVE_OVERWRITE; 1204 break; 1205 } 1206 } 1207 return 0; 1208} 1209 1210void mutt_save_path (char *d, size_t dsize, ADDRESS *a) 1211{ 1212 if (a && a->mailbox) 1213 { 1214 strfcpy (d, a->mailbox, dsize); 1215 if (!option (OPTSAVEADDRESS)) 1216 { 1217 char *p; 1218 1219 if ((p = strpbrk (d, "%@"))) 1220 *p = 0; 1221 } 1222 mutt_strlower (d); 1223 } 1224 else 1225 *d = 0; 1226} 1227 1228void mutt_buffer_save_path (BUFFER *dest, ADDRESS *a) 1229{ 1230 if (a && a->mailbox) 1231 { 1232 mutt_buffer_strcpy (dest, a->mailbox); 1233 if (!option (OPTSAVEADDRESS)) 1234 { 1235 char *p; 1236 1237 if ((p = strpbrk (dest->data, "%@"))) 1238 { 1239 *p = 0; 1240 mutt_buffer_fix_dptr (dest); 1241 } 1242 } 1243 mutt_strlower (dest->data); 1244 } 1245 else 1246 mutt_buffer_clear (dest); 1247} 1248 1249void mutt_safe_path (BUFFER *dest, ADDRESS *a) 1250{ 1251 char *p; 1252 1253 mutt_buffer_save_path (dest, a); 1254 for (p = dest->data; *p; p++) 1255 if (*p == '/' || ISSPACE (*p) || !IsPrint ((unsigned char) *p)) 1256 *p = '_'; 1257} 1258 1259void mutt_buffer_concat_path (BUFFER *d, const char *dir, const char *fname) 1260{ 1261 const char *fmt = "%s/%s"; 1262 1263 if (!*fname || (*dir && dir[strlen(dir)-1] == '/')) 1264 fmt = "%s%s"; 1265 1266 mutt_buffer_printf (d, fmt, dir, fname); 1267} 1268 1269/* 1270 * Write the concatened pathname (dir + "/" + fname) into dst. 1271 * The slash is omitted when dir or fname is of 0 length. 1272 */ 1273void mutt_buffer_concatn_path (BUFFER *dst, const char *dir, size_t dirlen, 1274 const char *fname, size_t fnamelen) 1275{ 1276 mutt_buffer_clear (dst); 1277 if (dirlen) 1278 mutt_buffer_addstr_n (dst, dir, dirlen); 1279 if (dirlen && fnamelen) 1280 mutt_buffer_addch (dst, '/'); 1281 if (fnamelen) 1282 mutt_buffer_addstr_n (dst, fname, fnamelen); 1283} 1284 1285const char *mutt_getcwd (BUFFER *cwd) 1286{ 1287 char *retval; 1288 1289 mutt_buffer_increase_size (cwd, _POSIX_PATH_MAX); 1290 retval = getcwd (cwd->data, cwd->dsize); 1291 while (!retval && errno == ERANGE) 1292 { 1293 mutt_buffer_increase_size (cwd, cwd->dsize + STRING); 1294 retval = getcwd (cwd->data, cwd->dsize); 1295 } 1296 if (retval) 1297 mutt_buffer_fix_dptr (cwd); 1298 else 1299 mutt_buffer_clear (cwd); 1300 1301 return retval; 1302} 1303 1304/* Note this function uses a fixed size buffer of LONG_STRING and so 1305 * should only be used for visual modifications, such as disp_subj. */ 1306char *mutt_apply_replace (char *dbuf, size_t dlen, char *sbuf, REPLACE_LIST *rlist) 1307{ 1308 REPLACE_LIST *l; 1309 static regmatch_t *pmatch = NULL; 1310 static int nmatch = 0; 1311 static char twinbuf[2][LONG_STRING]; 1312 int switcher = 0; 1313 char *p; 1314 int i, n; 1315 size_t cpysize, tlen; 1316 char *src, *dst; 1317 1318 if (dbuf && dlen) 1319 dbuf[0] = '\0'; 1320 1321 if (sbuf == NULL || *sbuf == '\0' || (dbuf && !dlen)) 1322 return dbuf; 1323 1324 twinbuf[0][0] = '\0'; 1325 twinbuf[1][0] = '\0'; 1326 src = twinbuf[switcher]; 1327 dst = src; 1328 1329 strfcpy(src, sbuf, LONG_STRING); 1330 1331 for (l = rlist; l; l = l->next) 1332 { 1333 /* If this pattern needs more matches, expand pmatch. */ 1334 if (l->nmatch > nmatch) 1335 { 1336 safe_realloc (&pmatch, l->nmatch * sizeof(regmatch_t)); 1337 nmatch = l->nmatch; 1338 } 1339 1340 if (regexec (l->rx->rx, src, l->nmatch, pmatch, 0) == 0) 1341 { 1342 tlen = 0; 1343 switcher ^= 1; 1344 dst = twinbuf[switcher]; 1345 1346 dprint (5, (debugfile, "mutt_apply_replace: %s matches %s\n", src, l->rx->pattern)); 1347 1348 /* Copy into other twinbuf with substitutions */ 1349 if (l->template) 1350 { 1351 for (p = l->template; *p && (tlen < LONG_STRING - 1); ) 1352 { 1353 if (*p == '%') 1354 { 1355 p++; 1356 if (*p == 'L') 1357 { 1358 p++; 1359 cpysize = MIN (pmatch[0].rm_so, LONG_STRING - tlen - 1); 1360 strncpy(&dst[tlen], src, cpysize); 1361 tlen += cpysize; 1362 } 1363 else if (*p == 'R') 1364 { 1365 p++; 1366 cpysize = MIN (strlen (src) - pmatch[0].rm_eo, LONG_STRING - tlen - 1); 1367 strncpy(&dst[tlen], &src[pmatch[0].rm_eo], cpysize); 1368 tlen += cpysize; 1369 } 1370 else 1371 { 1372 n = strtoul(p, &p, 10); /* get subst number */ 1373 while (isdigit((unsigned char)*p)) /* skip subst token */ 1374 ++p; 1375 for (i = pmatch[n].rm_so; (i < pmatch[n].rm_eo) && (tlen < LONG_STRING-1); i++) 1376 dst[tlen++] = src[i]; 1377 } 1378 } 1379 else 1380 dst[tlen++] = *p++; 1381 } 1382 } 1383 dst[tlen] = '\0'; 1384 dprint (5, (debugfile, "mutt_apply_replace: subst %s\n", dst)); 1385 } 1386 src = dst; 1387 } 1388 1389 if (dbuf) 1390 strfcpy(dbuf, dst, dlen); 1391 else 1392 dbuf = safe_strdup(dst); 1393 return dbuf; 1394} 1395 1396 1397void mutt_FormatString (char *dest, /* output buffer */ 1398 size_t destlen, /* output buffer len */ 1399 size_t col, /* starting column (nonzero when called recursively) */ 1400 int cols, /* maximum columns */ 1401 const char *src, /* template string */ 1402 format_t *callback, /* callback for processing */ 1403 unsigned long data, /* callback data */ 1404 format_flag flags) /* callback flags */ 1405{ 1406 char prefix[SHORT_STRING], buf[LONG_STRING], *cp, *wptr = dest, ch; 1407 char ifstring[SHORT_STRING], elsestring[SHORT_STRING]; 1408 size_t wlen, count, len, wid; 1409 pid_t pid; 1410 FILE *filter; 1411 int n; 1412 char *recycler; 1413 1414 prefix[0] = '\0'; 1415 destlen--; /* save room for the terminal \0 */ 1416 wlen = ((flags & MUTT_FORMAT_ARROWCURSOR) && option (OPTARROWCURSOR)) ? 3 : 0; 1417 col += wlen; 1418 1419 if ((flags & MUTT_FORMAT_NOFILTER) == 0) 1420 { 1421 int off = -1; 1422 1423 /* Do not consider filters if no pipe at end */ 1424 n = mutt_strlen(src); 1425 if (n > 1 && src[n-1] == '|') 1426 { 1427 /* Scan backwards for backslashes */ 1428 off = n; 1429 while (off > 0 && src[off-2] == '\\') 1430 off--; 1431 } 1432 1433 /* If number of backslashes is even, the pipe is real. */ 1434 /* n-off is the number of backslashes. */ 1435 if (off > 0 && ((n-off) % 2) == 0) 1436 { 1437 BUFFER *srcbuf, *word, *command; 1438 char srccopy[LONG_STRING]; 1439#ifdef DEBUG 1440 int i = 0; 1441#endif 1442 1443 dprint(3, (debugfile, "fmtpipe = %s\n", src)); 1444 1445 strncpy(srccopy, src, n); 1446 srccopy[n-1] = '\0'; 1447 1448 /* prepare BUFFERs */ 1449 srcbuf = mutt_buffer_from (srccopy); 1450 /* note: we are resetting dptr and *reading* from the buffer, so we don't 1451 * want to use mutt_buffer_clear(). */ 1452 srcbuf->dptr = srcbuf->data; 1453 word = mutt_buffer_new (); 1454 command = mutt_buffer_new (); 1455 1456 /* Iterate expansions across successive arguments */ 1457 do 1458 { 1459 char *p; 1460 1461 /* Extract the command name and copy to command line */ 1462 dprint(3, (debugfile, "fmtpipe +++: %s\n", srcbuf->dptr)); 1463 if (word->data) 1464 *word->data = '\0'; 1465 mutt_extract_token(word, srcbuf, 0); 1466 dprint(3, (debugfile, "fmtpipe %2d: %s\n", i++, word->data)); 1467 mutt_buffer_addch(command, '\''); 1468 mutt_FormatString(buf, sizeof(buf), 0, cols, word->data, callback, data, 1469 flags | MUTT_FORMAT_NOFILTER); 1470 for (p = buf; p && *p; p++) 1471 { 1472 if (*p == '\'') 1473 /* shell quoting doesn't permit escaping a single quote within 1474 * single-quoted material. double-quoting instead will lead 1475 * shell variable expansions, so break out of the single-quoted 1476 * span, insert a double-quoted single quote, and resume. */ 1477 mutt_buffer_addstr(command, "'\"'\"'"); 1478 else 1479 mutt_buffer_addch(command, *p); 1480 } 1481 mutt_buffer_addch(command, '\''); 1482 mutt_buffer_addch(command, ' '); 1483 } while (MoreArgs(srcbuf)); 1484 1485 dprint(3, (debugfile, "fmtpipe > %s\n", command->data)); 1486 1487 col -= wlen; /* reset to passed in value */ 1488 wptr = dest; /* reset write ptr */ 1489 wlen = ((flags & MUTT_FORMAT_ARROWCURSOR) && option (OPTARROWCURSOR)) ? 3 : 0; 1490 if ((pid = mutt_create_filter(command->data, NULL, &filter, NULL)) != -1) 1491 { 1492 int rc; 1493 1494 n = fread(dest, 1, destlen /* already decremented */, filter); 1495 safe_fclose (&filter); 1496 rc = mutt_wait_filter(pid); 1497 if (rc != 0) 1498 dprint(1, (debugfile, "format pipe command exited code %d\n", rc)); 1499 if (n > 0) 1500 { 1501 dest[n] = 0; 1502 while ((n > 0) && (dest[n-1] == '\n' || dest[n-1] == '\r')) 1503 dest[--n] = '\0'; 1504 dprint(3, (debugfile, "fmtpipe < %s\n", dest)); 1505 1506 /* If the result ends with '%', this indicates that the filter 1507 * generated %-tokens that mutt can expand. Eliminate the '%' 1508 * marker and recycle the string through mutt_FormatString(). 1509 * To literally end with "%", use "%%". */ 1510 if ((n > 0) && dest[n-1] == '%') 1511 { 1512 --n; 1513 dest[n] = '\0'; /* remove '%' */ 1514 if ((n > 0) && dest[n-1] != '%') 1515 { 1516 recycler = safe_strdup(dest); 1517 if (recycler) 1518 { 1519 /* destlen is decremented at the start of this function 1520 * to save space for the terminal nul char. We can add 1521 * it back for the recursive call since the expansion of 1522 * format pipes does not try to append a nul itself. 1523 */ 1524 mutt_FormatString(dest, destlen+1, col, cols, recycler, callback, data, flags); 1525 FREE(&recycler); 1526 } 1527 } 1528 } 1529 } 1530 else 1531 { 1532 /* read error */ 1533 dprint(1, (debugfile, "error reading from fmtpipe: %s (errno=%d)\n", strerror(errno), errno)); 1534 *wptr = 0; 1535 } 1536 } 1537 else 1538 { 1539 /* Filter failed; erase write buffer */ 1540 *wptr = '\0'; 1541 } 1542 1543 mutt_buffer_free(&command); 1544 mutt_buffer_free(&srcbuf); 1545 mutt_buffer_free(&word); 1546 return; 1547 } 1548 } 1549 1550 while (*src && wlen < destlen) 1551 { 1552 if (*src == '%') 1553 { 1554 if (*++src == '%') 1555 { 1556 *wptr++ = '%'; 1557 wlen++; 1558 col++; 1559 src++; 1560 continue; 1561 } 1562 1563 if (*src == '?') 1564 { 1565 flags |= MUTT_FORMAT_OPTIONAL; 1566 src++; 1567 } 1568 else 1569 { 1570 flags &= ~MUTT_FORMAT_OPTIONAL; 1571 1572 /* eat the format string */ 1573 cp = prefix; 1574 count = 0; 1575 while (count < sizeof (prefix) && 1576 (isdigit ((unsigned char) *src) || *src == '.' || *src == '-' || *src == '=')) 1577 { 1578 *cp++ = *src++; 1579 count++; 1580 } 1581 *cp = 0; 1582 } 1583 1584 if (!*src) 1585 break; /* bad format */ 1586 1587 ch = *src++; /* save the character to switch on */ 1588 1589 if (flags & MUTT_FORMAT_OPTIONAL) 1590 { 1591 if (*src != '?') 1592 break; /* bad format */ 1593 src++; 1594 1595 /* eat the `if' part of the string */ 1596 cp = ifstring; 1597 count = 0; 1598 while (count < sizeof (ifstring) && *src && *src != '?' && *src != '&') 1599 { 1600 *cp++ = *src++; 1601 count++; 1602 } 1603 *cp = 0; 1604 1605 /* eat the `else' part of the string (optional) */ 1606 if (*src == '&') 1607 src++; /* skip the & */ 1608 cp = elsestring; 1609 count = 0; 1610 while (count < sizeof (elsestring) && *src && *src != '?') 1611 { 1612 *cp++ = *src++; 1613 count++; 1614 } 1615 *cp = 0; 1616 1617 if (!*src) 1618 break; /* bad format */ 1619 1620 src++; /* move past the trailing `?' */ 1621 } 1622 1623 /* handle generic cases first */ 1624 if (ch == '>' || ch == '*') 1625 { 1626 /* %>X: right justify to EOL, left takes precedence 1627 * %*X: right justify to EOL, right takes precedence */ 1628 int soft = ch == '*'; 1629 int pl, pw; 1630 if ((pl = mutt_charlen (src, &pw)) <= 0) 1631 pl = pw = 1; 1632 1633 /* see if there's room to add content, else ignore */ 1634 if ((col < cols && wlen < destlen) || soft) 1635 { 1636 int pad; 1637 1638 /* get contents after padding */ 1639 mutt_FormatString (buf, sizeof (buf), 0, cols, src + pl, callback, data, flags); 1640 len = mutt_strlen (buf); 1641 wid = mutt_strwidth (buf); 1642 1643 pad = (cols - col - wid) / pw; 1644 if (pad >= 0) 1645 { 1646 /* try to consume as many columns as we can, if we don't have 1647 * memory for that, use as much memory as possible */ 1648 if (wlen + (pad * pl) + len > destlen) 1649 pad = (destlen > wlen + len) ? ((destlen - wlen - len) / pl) : 0; 1650 else 1651 { 1652 /* Add pre-spacing to make multi-column pad characters and 1653 * the contents after padding line up */ 1654 while ((col + (pad * pw) + wid < cols) && 1655 (wlen + (pad * pl) + len < destlen)) 1656 { 1657 *wptr++ = ' '; 1658 wlen++; 1659 col++; 1660 } 1661 } 1662 while (pad-- > 0) 1663 { 1664 memcpy (wptr, src, pl); 1665 wptr += pl; 1666 wlen += pl; 1667 col += pw; 1668 } 1669 } 1670 else if (soft && pad < 0) 1671 { 1672 int offset = ((flags & MUTT_FORMAT_ARROWCURSOR) && option (OPTARROWCURSOR)) ? 3 : 0; 1673 int avail_cols = (cols > offset) ? (cols - offset) : 0; 1674 /* \0-terminate dest for length computation in mutt_wstr_trunc() */ 1675 *wptr = 0; 1676 /* make sure right part is at most as wide as display */ 1677 len = mutt_wstr_trunc (buf, destlen, avail_cols, &wid); 1678 /* truncate left so that right part fits completely in */ 1679 wlen = mutt_wstr_trunc (dest, destlen - len, avail_cols - wid, &col); 1680 wptr = dest + wlen; 1681 /* Multi-column characters may be truncated in the middle. 1682 * Add spacing so the right hand side lines up. */ 1683 while ((col + wid < avail_cols) && (wlen + len < destlen)) 1684 { 1685 *wptr++ = ' '; 1686 wlen++; 1687 col++; 1688 } 1689 } 1690 if (len + wlen > destlen) 1691 len = mutt_wstr_trunc (buf, destlen - wlen, cols - col, NULL); 1692 memcpy (wptr, buf, len); 1693 wptr += len; 1694 wlen += len; 1695 col += wid; 1696 src += pl; 1697 } 1698 break; /* skip rest of input */ 1699 } 1700 else if (ch == '|') 1701 { 1702 /* pad to EOL */ 1703 int pl, pw, c; 1704 if ((pl = mutt_charlen (src, &pw)) <= 0) 1705 pl = pw = 1; 1706 1707 /* see if there's room to add content, else ignore */ 1708 if (col < cols && wlen < destlen) 1709 { 1710 c = (cols - col) / pw; 1711 if (c > 0 && wlen + (c * pl) > destlen) 1712 c = ((signed)(destlen - wlen)) / pl; 1713 while (c > 0) 1714 { 1715 memcpy (wptr, src, pl); 1716 wptr += pl; 1717 wlen += pl; 1718 col += pw; 1719 c--; 1720 } 1721 src += pl; 1722 } 1723 break; /* skip rest of input */ 1724 } 1725 else 1726 { 1727 short tolower = 0; 1728 short nodots = 0; 1729 1730 while (ch == '_' || ch == ':') 1731 { 1732 if (ch == '_') 1733 tolower = 1; 1734 else if (ch == ':') 1735 nodots = 1; 1736 1737 ch = *src++; 1738 } 1739 1740 /* use callback function to handle this case */ 1741 src = callback (buf, sizeof (buf), col, cols, ch, src, prefix, ifstring, elsestring, data, flags); 1742 1743 if (tolower) 1744 mutt_strlower (buf); 1745 if (nodots) 1746 { 1747 char *p = buf; 1748 for (; *p; p++) 1749 if (*p == '.') 1750 *p = '_'; 1751 } 1752 1753 if ((len = mutt_strlen (buf)) + wlen > destlen) 1754 len = mutt_wstr_trunc (buf, destlen - wlen, cols - col, NULL); 1755 1756 memcpy (wptr, buf, len); 1757 wptr += len; 1758 wlen += len; 1759 col += mutt_strwidth (buf); 1760 } 1761 } 1762 else if (*src == '\\') 1763 { 1764 if (!*++src) 1765 break; 1766 switch (*src) 1767 { 1768 case 'n': 1769 *wptr = '\n'; 1770 break; 1771 case 't': 1772 *wptr = '\t'; 1773 break; 1774 case 'r': 1775 *wptr = '\r'; 1776 break; 1777 case 'f': 1778 *wptr = '\f'; 1779 break; 1780 case 'v': 1781 *wptr = '\v'; 1782 break; 1783 default: 1784 *wptr = *src; 1785 break; 1786 } 1787 src++; 1788 wptr++; 1789 wlen++; 1790 col++; 1791 } 1792 else 1793 { 1794 int tmp, w; 1795 /* in case of error, simply copy byte */ 1796 if ((tmp = mutt_charlen (src, &w)) < 0) 1797 tmp = w = 1; 1798 if (tmp > 0 && wlen + tmp < destlen) 1799 { 1800 memcpy (wptr, src, tmp); 1801 wptr += tmp; 1802 src += tmp; 1803 wlen += tmp; 1804 col += w; 1805 } 1806 else 1807 { 1808 src += destlen - wlen; 1809 wlen = destlen; 1810 } 1811 } 1812 } 1813 *wptr = 0; 1814} 1815 1816/* This function allows the user to specify a command to read stdout from in 1817 place of a normal file. If the last character in the string is a pipe (|), 1818 then we assume it is a command to run instead of a normal file. */ 1819FILE *mutt_open_read (const char *path, pid_t *thepid) 1820{ 1821 FILE *f; 1822 struct stat s; 1823 1824 int len = mutt_strlen (path); 1825 1826 if (path[len - 1] == '|') 1827 { 1828 /* read from a pipe */ 1829 1830 char *s = safe_strdup (path); 1831 1832 s[len - 1] = 0; 1833 mutt_endwin (NULL); 1834 *thepid = mutt_create_filter (s, NULL, &f, NULL); 1835 FREE (&s); 1836 } 1837 else 1838 { 1839 if (stat (path, &s) < 0) 1840 return (NULL); 1841 if (S_ISDIR (s.st_mode)) 1842 { 1843 errno = EINVAL; 1844 return (NULL); 1845 } 1846 f = fopen (path, "r"); 1847 *thepid = -1; 1848 } 1849 return (f); 1850} 1851 1852/* returns 0 if OK to proceed, -1 to abort, 1 to retry */ 1853int mutt_save_confirm (const char *s, struct stat *st) 1854{ 1855 BUFFER *tmp = NULL; 1856 int ret = 0; 1857 int rc; 1858 int magic = 0; 1859 1860 magic = mx_get_magic (s); 1861 1862#ifdef USE_POP 1863 if (magic == MUTT_POP) 1864 { 1865 mutt_error _("Can't save message to POP mailbox."); 1866 return 1; 1867 } 1868#endif 1869 1870 if (magic > 0 && !mx_access (s, W_OK)) 1871 { 1872 if (option (OPTCONFIRMAPPEND)) 1873 { 1874 tmp = mutt_buffer_pool_get (); 1875 mutt_buffer_printf (tmp, _("Append messages to %s?"), s); 1876 if ((rc = mutt_yesorno (mutt_b2s (tmp), MUTT_YES)) == MUTT_NO) 1877 ret = 1; 1878 else if (rc == -1) 1879 ret = -1; 1880 mutt_buffer_pool_release (&tmp); 1881 } 1882 } 1883 1884 if (stat (s, st) != -1) 1885 { 1886 if (magic == -1) 1887 { 1888 mutt_error (_("%s is not a mailbox!"), s); 1889 return 1; 1890 } 1891 } 1892 else if (magic != MUTT_IMAP) 1893 { 1894 st->st_mtime = 0; 1895 st->st_atime = 0; 1896 1897 if (errno == ENOENT) 1898 { 1899 if (option (OPTCONFIRMCREATE)) 1900 { 1901 tmp = mutt_buffer_pool_get (); 1902 mutt_buffer_printf (tmp, _("Create %s?"), s); 1903 if ((rc = mutt_yesorno (mutt_b2s (tmp), MUTT_YES)) == MUTT_NO) 1904 ret = 1; 1905 else if (rc == -1) 1906 ret = -1; 1907 mutt_buffer_pool_release (&tmp); 1908 } 1909 } 1910 else 1911 { 1912 mutt_perror (s); 1913 return 1; 1914 } 1915 } 1916 1917 mutt_window_clearline (MuttMessageWindow, 0); 1918 return (ret); 1919} 1920 1921void state_prefix_putc (char c, STATE *s) 1922{ 1923 if (s->flags & MUTT_PENDINGPREFIX) 1924 { 1925 state_reset_prefix (s); 1926 if (s->prefix) 1927 state_puts (s->prefix, s); 1928 } 1929 1930 state_putc (c, s); 1931 1932 if (c == '\n') 1933 state_set_prefix (s); 1934} 1935 1936int state_printf (STATE *s, const char *fmt, ...) 1937{ 1938 int rv; 1939 va_list ap; 1940 1941 va_start (ap, fmt); 1942 rv = vfprintf (s->fpout, fmt, ap); 1943 va_end (ap); 1944 1945 return rv; 1946} 1947 1948void state_mark_attach (STATE *s) 1949{ 1950 if ((s->flags & MUTT_DISPLAY) && 1951 (!Pager || !mutt_strcmp (Pager, "builtin"))) 1952 state_puts (AttachmentMarker, s); 1953} 1954 1955void state_mark_protected_header (STATE *s) 1956{ 1957 if ((s->flags & MUTT_DISPLAY) && 1958 (!Pager || !mutt_strcmp (Pager, "builtin"))) 1959 state_puts (ProtectedHeaderMarker, s); 1960} 1961 1962void state_attach_puts (const char *t, STATE *s) 1963{ 1964 if (*t != '\n') state_mark_attach (s); 1965 while (*t) 1966 { 1967 state_putc (*t, s); 1968 if (*t++ == '\n' && *t) 1969 if (*t != '\n') state_mark_attach (s); 1970 } 1971} 1972 1973int state_putwc (wchar_t wc, STATE *s) 1974{ 1975 char mb[MB_LEN_MAX] = ""; 1976 int rc; 1977 1978 if ((rc = wcrtomb (mb, wc, NULL)) < 0) 1979 return rc; 1980 if (fputs (mb, s->fpout) == EOF) 1981 return -1; 1982 return 0; 1983} 1984 1985int state_putws (const wchar_t *ws, STATE *s) 1986{ 1987 const wchar_t *p = ws; 1988 1989 while (p && *p != L'\0') 1990 { 1991 if (state_putwc (*p, s) < 0) 1992 return -1; 1993 p++; 1994 } 1995 return 0; 1996} 1997 1998void mutt_display_sanitize (char *s) 1999{ 2000 for (; *s; s++) 2001 { 2002 if (!IsPrint (*s)) 2003 *s = '?'; 2004 } 2005} 2006 2007void mutt_sleep (short s) 2008{ 2009 if (SleepTime > s) 2010 sleep (SleepTime); 2011 else if (s) 2012 sleep(s); 2013} 2014 2015/* Decrease a file's modification time by 1 second */ 2016 2017time_t mutt_decrease_mtime (const char *f, struct stat *st) 2018{ 2019 struct utimbuf utim; 2020 struct stat _st; 2021 time_t mtime; 2022 2023 if (!st) 2024 { 2025 if (stat (f, &_st) == -1) 2026 return -1; 2027 st = &_st; 2028 } 2029 2030 if ((mtime = st->st_mtime) == time (NULL)) 2031 { 2032 mtime -= 1; 2033 utim.actime = mtime; 2034 utim.modtime = mtime; 2035 utime (f, &utim); 2036 } 2037 2038 return mtime; 2039} 2040 2041/* sets mtime of 'to' to mtime of 'from' */ 2042void mutt_set_mtime (const char* from, const char* to) 2043{ 2044 struct utimbuf utim; 2045 struct stat st; 2046 2047 if (stat (from, &st) != -1) 2048 { 2049 utim.actime = st.st_mtime; 2050 utim.modtime = st.st_mtime; 2051 utime (to, &utim); 2052 } 2053} 2054 2055/* set atime to current time, just as read() would do on !noatime. 2056 * Silently ignored if unsupported. */ 2057void mutt_touch_atime (int f) 2058{ 2059#ifdef HAVE_FUTIMENS 2060 struct timespec times[2]={{0,UTIME_NOW},{0,UTIME_OMIT}}; 2061 futimens(f, times); 2062#endif 2063} 2064 2065int mutt_timespec_compare (struct timespec *a, struct timespec *b) 2066{ 2067 if (a->tv_sec < b->tv_sec) 2068 return -1; 2069 if (a->tv_sec > b->tv_sec) 2070 return 1; 2071 2072 if (a->tv_nsec < b->tv_nsec) 2073 return -1; 2074 if (a->tv_nsec > b->tv_nsec) 2075 return 1; 2076 return 0; 2077} 2078 2079void mutt_get_stat_timespec (struct timespec *dest, struct stat *sb, mutt_stat_type type) 2080{ 2081 dest->tv_sec = 0; 2082 dest->tv_nsec = 0; 2083 2084 switch (type) 2085 { 2086 case MUTT_STAT_ATIME: 2087 dest->tv_sec = sb->st_atime; 2088#ifdef HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC 2089 dest->tv_nsec = sb->st_atim.tv_nsec; 2090#endif 2091 break; 2092 case MUTT_STAT_MTIME: 2093 dest->tv_sec = sb->st_mtime; 2094#ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC 2095 dest->tv_nsec = sb->st_mtim.tv_nsec; 2096#endif 2097 break; 2098 case MUTT_STAT_CTIME: 2099 dest->tv_sec = sb->st_ctime; 2100#ifdef HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC 2101 dest->tv_nsec = sb->st_ctim.tv_nsec; 2102#endif 2103 break; 2104 } 2105} 2106 2107int mutt_stat_timespec_compare (struct stat *sba, mutt_stat_type type, struct timespec *b) 2108{ 2109 struct timespec a; 2110 2111 mutt_get_stat_timespec (&a, sba, type); 2112 return mutt_timespec_compare (&a, b); 2113} 2114 2115int mutt_stat_compare (struct stat *sba, mutt_stat_type sba_type, 2116 struct stat *sbb, mutt_stat_type sbb_type) 2117{ 2118 struct timespec a, b; 2119 2120 mutt_get_stat_timespec (&a, sba, sba_type); 2121 mutt_get_stat_timespec (&b, sbb, sbb_type); 2122 return mutt_timespec_compare (&a, &b); 2123} 2124 2125const char *mutt_make_version (void) 2126{ 2127 static char vstring[STRING]; 2128 snprintf (vstring, sizeof (vstring), "Mutt %s (%s)", 2129 MUTT_VERSION, ReleaseDate); 2130 return vstring; 2131} 2132 2133REGEXP *mutt_compile_regexp (const char *s, int flags) 2134{ 2135 REGEXP *pp = safe_calloc (sizeof (REGEXP), 1); 2136 pp->pattern = safe_strdup (s); 2137 pp->rx = safe_calloc (sizeof (regex_t), 1); 2138 if (REGCOMP (pp->rx, NONULL(s), flags) != 0) 2139 mutt_free_regexp (&pp); 2140 2141 return pp; 2142} 2143 2144void mutt_free_regexp (REGEXP **pp) 2145{ 2146 FREE (&(*pp)->pattern); 2147 regfree ((*pp)->rx); 2148 FREE (&(*pp)->rx); 2149 FREE (pp); /* __FREE_CHECKED__ */ 2150} 2151 2152void mutt_free_rx_list (RX_LIST **list) 2153{ 2154 RX_LIST *p; 2155 2156 if (!list) return; 2157 while (*list) 2158 { 2159 p = *list; 2160 *list = (*list)->next; 2161 mutt_free_regexp (&p->rx); 2162 FREE (&p); 2163 } 2164} 2165 2166void mutt_free_replace_list (REPLACE_LIST **list) 2167{ 2168 REPLACE_LIST *p; 2169 2170 if (!list) return; 2171 while (*list) 2172 { 2173 p = *list; 2174 *list = (*list)->next; 2175 mutt_free_regexp (&p->rx); 2176 FREE (&p->template); 2177 FREE (&p); 2178 } 2179} 2180 2181int mutt_match_rx_list (const char *s, RX_LIST *l) 2182{ 2183 if (!s) return 0; 2184 2185 for (; l; l = l->next) 2186 { 2187 if (regexec (l->rx->rx, s, (size_t) 0, (regmatch_t *) 0, (int) 0) == 0) 2188 { 2189 dprint (5, (debugfile, "mutt_match_rx_list: %s matches %s\n", s, l->rx->pattern)); 2190 return 1; 2191 } 2192 } 2193 2194 return 0; 2195} 2196 2197/* Match a string against the patterns defined by the 'spam' command and output 2198 * the expanded format into `text` when there is a match. If textsize<=0, the 2199 * match is performed but the format is not expanded and no assumptions are made 2200 * about the value of `text` so it may be NULL. 2201 * 2202 * Returns 1 if the argument `s` matches a pattern in the spam list, otherwise 2203 * 0. */ 2204int mutt_match_spam_list (const char *s, REPLACE_LIST *l, char *text, int textsize) 2205{ 2206 static regmatch_t *pmatch = NULL; 2207 static int nmatch = 0; 2208 int tlen = 0; 2209 char *p; 2210 2211 if (!s) return 0; 2212 2213 for (; l; l = l->next) 2214 { 2215 /* If this pattern needs more matches, expand pmatch. */ 2216 if (l->nmatch > nmatch) 2217 { 2218 safe_realloc (&pmatch, l->nmatch * sizeof(regmatch_t)); 2219 nmatch = l->nmatch; 2220 } 2221 2222 /* Does this pattern match? */ 2223 if (regexec (l->rx->rx, s, (size_t) l->nmatch, (regmatch_t *) pmatch, (int) 0) == 0) 2224 { 2225 dprint (5, (debugfile, "mutt_match_spam_list: %s matches %s\n", s, l->rx->pattern)); 2226 dprint (5, (debugfile, "mutt_match_spam_list: %d subs\n", (int)l->rx->rx->re_nsub)); 2227 2228 /* Copy template into text, with substitutions. */ 2229 for (p = l->template; *p && tlen < textsize - 1;) 2230 { 2231 /* backreference to pattern match substring, eg. %1, %2, etc) */ 2232 if (*p == '%') 2233 { 2234 char *e; /* used as pointer to end of integer backreference in strtol() call */ 2235 int n; 2236 2237 ++p; /* skip over % char */ 2238 n = strtol(p, &e, 10); 2239 /* Ensure that the integer conversion succeeded (e!=p) and bounds check. The upper bound check 2240 * should not strictly be necessary since add_to_spam_list() finds the largest value, and 2241 * the static array above is always large enough based on that value. */ 2242 if (e != p && n >= 0 && n <= l->nmatch && pmatch[n].rm_so != -1) 2243 { 2244 /* copy as much of the substring match as will fit in the output buffer, saving space for 2245 * the terminating nul char */ 2246 int idx; 2247 for (idx = pmatch[n].rm_so; (idx < pmatch[n].rm_eo) && (tlen < textsize - 1); ++idx) 2248 text[tlen++] = s[idx]; 2249 } 2250 p = e; /* skip over the parsed integer */ 2251 } 2252 else 2253 { 2254 text[tlen++] = *p++; 2255 } 2256 } 2257 /* tlen should always be less than textsize except when textsize<=0 2258 * because the bounds checks in the above code leave room for the 2259 * terminal nul char. This should avoid returning an unterminated 2260 * string to the caller. When textsize<=0 we make no assumption about 2261 * the validity of the text pointer. */ 2262 if (tlen < textsize) 2263 { 2264 text[tlen] = '\0'; 2265 dprint (5, (debugfile, "mutt_match_spam_list: \"%s\"\n", text)); 2266 } 2267 return 1; 2268 } 2269 } 2270 2271 return 0; 2272} 2273 2274void mutt_encode_path (BUFFER *dest, const char *src) 2275{ 2276 char *p; 2277 int rc; 2278 2279 p = safe_strdup (src); 2280 rc = mutt_convert_string (&p, Charset, "utf-8", 0); 2281 /* `src' may be NULL, such as when called from the pop3 driver. */ 2282 mutt_buffer_strcpy (dest, (rc == 0) ? NONULL(p) : NONULL(src)); 2283 FREE (&p); 2284} 2285 2286 2287/************************************************************************ 2288 * These functions are transplanted from lib.c, in order to modify them * 2289 * to use BUFFERs. * 2290 ************************************************************************/ 2291 2292/* remove a directory and everything under it */ 2293int mutt_rmtree (const char* path) 2294{ 2295 DIR* dirp; 2296 struct dirent* de; 2297 BUFFER *cur = NULL; 2298 struct stat statbuf; 2299 int rc = 0; 2300 2301 if (!(dirp = opendir (path))) 2302 { 2303 dprint (1, (debugfile, "mutt_rmtree: error opening directory %s\n", path)); 2304 return -1; 2305 } 2306 2307 /* We avoid using the buffer pool for this function, because it 2308 * invokes recursively to an unknown depth. */ 2309 cur = mutt_buffer_new (); 2310 mutt_buffer_increase_size (cur, _POSIX_PATH_MAX); 2311 2312 while ((de = readdir (dirp))) 2313 { 2314 if (!strcmp (".", de->d_name) || !strcmp ("..", de->d_name)) 2315 continue; 2316 2317 mutt_buffer_printf (cur, "%s/%s", path, de->d_name); 2318 /* XXX make nonrecursive version */ 2319 2320 if (stat(mutt_b2s (cur), &statbuf) == -1) 2321 { 2322 rc = 1; 2323 continue; 2324 } 2325 2326 if (S_ISDIR (statbuf.st_mode)) 2327 rc |= mutt_rmtree (mutt_b2s (cur)); 2328 else 2329 rc |= unlink (mutt_b2s (cur)); 2330 } 2331 closedir (dirp); 2332 2333 rc |= rmdir (path); 2334 2335 mutt_buffer_free (&cur); 2336 return rc; 2337} 2338 2339/* Create a temporary directory next to a file name */ 2340 2341static int mutt_mkwrapdir (const char *path, BUFFER *newfile, BUFFER *newdir) 2342{ 2343 const char *basename; 2344 BUFFER *parent = NULL; 2345 char *p; 2346 int rc = 0; 2347 2348 parent = mutt_buffer_pool_get (); 2349 mutt_buffer_strcpy (parent, NONULL (path)); 2350 2351 if ((p = strrchr (parent->data, '/'))) 2352 { 2353 *p = '\0'; 2354 basename = p + 1; 2355 } 2356 else 2357 { 2358 mutt_buffer_strcpy (parent, "."); 2359 basename = path; 2360 } 2361 2362 mutt_buffer_printf (newdir, "%s/%s", mutt_b2s (parent), ".muttXXXXXX"); 2363 if (mkdtemp(newdir->data) == NULL) 2364 { 2365 dprint(1, (debugfile, "mutt_mkwrapdir: mkdtemp() failed\n")); 2366 rc = -1; 2367 goto cleanup; 2368 } 2369 2370 mutt_buffer_printf (newfile, "%s/%s", mutt_b2s (newdir), NONULL(basename)); 2371 2372cleanup: 2373 mutt_buffer_pool_release (&parent); 2374 return rc; 2375} 2376 2377static int mutt_put_file_in_place (const char *path, const char *safe_file, const char *safe_dir) 2378{ 2379 int rv; 2380 2381 rv = safe_rename (safe_file, path); 2382 unlink (safe_file); 2383 rmdir (safe_dir); 2384 return rv; 2385} 2386 2387int safe_open (const char *path, int flags) 2388{ 2389 struct stat osb, nsb; 2390 int fd; 2391 BUFFER *safe_file = NULL; 2392 BUFFER *safe_dir = NULL; 2393 2394 if (flags & O_EXCL) 2395 { 2396 safe_file = mutt_buffer_pool_get (); 2397 safe_dir = mutt_buffer_pool_get (); 2398 2399 if (mutt_mkwrapdir (path, safe_file, safe_dir) == -1) 2400 { 2401 fd = -1; 2402 goto cleanup; 2403 } 2404 2405 if ((fd = open (mutt_b2s (safe_file), flags, 0600)) < 0) 2406 { 2407 rmdir (mutt_b2s (safe_dir)); 2408 goto cleanup; 2409 } 2410 2411 /* NFS and I believe cygwin do not handle movement of open files well */ 2412 close (fd); 2413 if (mutt_put_file_in_place (path, mutt_b2s (safe_file), mutt_b2s (safe_dir)) == -1) 2414 { 2415 fd = -1; 2416 goto cleanup; 2417 } 2418 } 2419 2420 if ((fd = open (path, flags & ~O_EXCL, 0600)) < 0) 2421 goto cleanup; 2422 2423 /* make sure the file is not symlink */ 2424 if (lstat (path, &osb) < 0 || fstat (fd, &nsb) < 0 || 2425 compare_stat(&osb, &nsb) == -1) 2426 { 2427/* dprint (1, (debugfile, "safe_open(): %s is a symlink!\n", path)); */ 2428 close (fd); 2429 fd = -1; 2430 goto cleanup; 2431 } 2432 2433cleanup: 2434 mutt_buffer_pool_release (&safe_file); 2435 mutt_buffer_pool_release (&safe_dir); 2436 2437 return (fd); 2438} 2439 2440/* when opening files for writing, make sure the file doesn't already exist 2441 * to avoid race conditions. 2442 */ 2443FILE *safe_fopen (const char *path, const char *mode) 2444{ 2445 if (mode[0] == 'w') 2446 { 2447 int fd; 2448 int flags = O_CREAT | O_EXCL; 2449 2450#ifdef O_NOFOLLOW 2451 flags |= O_NOFOLLOW; 2452#endif 2453 2454 if (mode[1] == '+') 2455 flags |= O_RDWR; 2456 else 2457 flags |= O_WRONLY; 2458 2459 if ((fd = safe_open (path, flags)) < 0) 2460 return (NULL); 2461 2462 return (fdopen (fd, mode)); 2463 } 2464 else 2465 return (fopen (path, mode)); 2466} 2467 2468int safe_symlink(const char *oldpath, const char *newpath) 2469{ 2470 struct stat osb, nsb; 2471 2472 if (!oldpath || !newpath) 2473 return -1; 2474 2475 if (unlink(newpath) == -1 && errno != ENOENT) 2476 return -1; 2477 2478 if (oldpath[0] == '/') 2479 { 2480 if (symlink (oldpath, newpath) == -1) 2481 return -1; 2482 } 2483 else 2484 { 2485 BUFFER *abs_oldpath = NULL; 2486 2487 abs_oldpath = mutt_buffer_pool_get (); 2488 2489 if (mutt_getcwd (abs_oldpath) == NULL) 2490 { 2491 mutt_buffer_pool_release (&abs_oldpath); 2492 return -1; 2493 } 2494 2495 mutt_buffer_addch (abs_oldpath, '/'); 2496 mutt_buffer_addstr (abs_oldpath, oldpath); 2497 if (symlink (mutt_b2s (abs_oldpath), newpath) == -1) 2498 { 2499 mutt_buffer_pool_release (&abs_oldpath); 2500 return -1; 2501 } 2502 2503 mutt_buffer_pool_release (&abs_oldpath); 2504 } 2505 2506 if (stat(oldpath, &osb) == -1 || stat(newpath, &nsb) == -1 2507 || compare_stat(&osb, &nsb) == -1) 2508 { 2509 unlink(newpath); 2510 return -1; 2511 } 2512 2513 return 0; 2514} 2515 2516/* END lib.c transplant functions */