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