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