Fix compilation warnings in pgp.c
[apps/madmutt.git] / muttlib.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
4  * Copyright (C) 1999-2000 Thomas Roessler <roessler@does-not-exist.org>
5  *
6  * This file is part of mutt-ng, see http://www.muttng.org/.
7  * It's licensed under the GNU General Public License,
8  * please see the file GPL in the top level source directory.
9  */
10
11 #include <lib-lib/lib-lib.h>
12
13 #include <grp.h>
14 #include <pwd.h>
15 #include <utime.h>
16
17 #include <lib-mime/mime.h>
18
19 #include <lib-ui/curses.h>
20 #include <lib-ui/enter.h>
21
22 #include "alias.h"
23 #include "mutt.h"
24 #include "mx.h"
25 #include "attach.h"
26
27 #include "version.h"
28
29 #include <imap/imap.h>
30 #include <imap/mx_imap.h>
31
32 #include <lib-crypt/crypt.h>
33
34 #define SW              (option(OPTMBOXPANE)?SidebarWidth:0)
35
36 /* Modified by blong to accept a "suggestion" for file name.  If
37  * that file exists, then construct one with unique name but 
38  * keep any extension.  This might fail, I guess.
39  * Renamed to mutt_adv_mktemp so I only have to change where it's
40  * called, and not all possible cases.
41  */
42 void mutt_adv_mktemp (const char* dir, char *s, ssize_t l)
43 {
44   char buf[_POSIX_PATH_MAX];
45   char tmp[_POSIX_PATH_MAX];
46   char *period;
47   ssize_t sl;
48   struct stat sb;
49
50   m_strcpy(buf, sizeof(buf), m_strisempty(dir) ? NONULL(Tempdir) : dir);
51   mutt_expand_path (buf, sizeof (buf));
52   if (s[0] == '\0') {
53     snprintf (s, l, "%s/muttXXXXXX", buf);
54     mktemp (s);
55   }
56   else {
57     m_strcpy(tmp, sizeof(tmp), s);
58     mutt_sanitize_filename (tmp, 1);
59     snprintf (s, l, "%s/%s", buf, tmp);
60     if (lstat (s, &sb) == -1 && errno == ENOENT)
61       return;
62     if ((period = strrchr (tmp, '.')) != NULL)
63       *period = 0;
64     snprintf (s, l, "%s/%s.XXXXXX", buf, tmp);
65     mktemp (s);
66     if (period != NULL) {
67       *period = '.';
68       sl = m_strlen(s);
69       m_strcpy(s + sl, l - sl, period);
70     }
71   }
72 }
73
74 /* create a send-mode duplicate from a receive-mode body */
75
76 int mutt_copy_body (FILE * fp, BODY ** tgt, BODY * src)
77 {
78   char tmp[_POSIX_PATH_MAX];
79   BODY *b;
80
81   parameter_t *par, **ppar;
82
83   short use_disp;
84
85   if (src->filename) {
86     use_disp = 1;
87     m_strcpy(tmp, sizeof(tmp), src->filename);
88   }
89   else {
90     use_disp = 0;
91     tmp[0] = '\0';
92   }
93
94   mutt_adv_mktemp (NULL, tmp, sizeof (tmp));
95   if (mutt_save_attachment (fp, src, tmp, 0, NULL) == -1)
96     return -1;
97
98   *tgt = body_new();
99   b = *tgt;
100
101   memcpy (b, src, sizeof (BODY));
102   b->parts = NULL;
103   b->next = NULL;
104
105   b->filename = m_strdup(tmp);
106   b->use_disp = use_disp;
107   b->unlink = 1;
108
109   if (mutt_is_text_part (b))
110     b->noconv = 1;
111
112   b->xtype = m_strdup(b->xtype);
113   b->subtype = m_strdup(b->subtype);
114   b->form_name = m_strdup(b->form_name);
115   b->filename = m_strdup(b->filename);
116   b->d_filename = m_strdup(b->d_filename);
117   b->description = m_strdup(b->description);
118
119   /* 
120    * we don't seem to need the HEADER structure currently.
121    * XXX - this may change in the future
122    */
123
124   if (b->hdr)
125     b->hdr = NULL;
126
127   /* copy parameters */
128   for (par = b->parameter, ppar = &b->parameter; par;
129        ppar = &(*ppar)->next, par = par->next) {
130     *ppar = parameter_new();
131     (*ppar)->attribute = m_strdup(par->attribute);
132     (*ppar)->value = m_strdup(par->value);
133   }
134
135   mutt_stamp_attachment (b);
136
137   return 0;
138 }
139
140 /* returns true if the header contained in "s" is in list "t" */
141 int mutt_matches_ignore (const char *s, string_list_t * t)
142 {
143   for (; t; t = t->next) {
144     if (!ascii_strncasecmp (s, t->data, m_strlen(t->data))
145         || *t->data == '*')
146       return 1;
147   }
148   return 0;
149 }
150
151 /* prepend the path part of *path to *lnk */
152 void mutt_expand_link (char *newpath, const char *path, const char *lnk)
153 {
154   const char *lb = NULL;
155   ssize_t len;
156
157   /* lnk is full path */
158   if (*lnk == '/') {
159     m_strcpy(newpath, _POSIX_PATH_MAX, lnk);
160     return;
161   }
162
163   if ((lb = strrchr (path, '/')) == NULL) {
164     /* no path in lnk */
165     m_strcpy(newpath, _POSIX_PATH_MAX, lnk);
166     return;
167   }
168
169   len = lb - path + 1;
170   memcpy (newpath, path, len);
171   m_strcpy(newpath + len, _POSIX_PATH_MAX - len, lnk);
172 }
173
174 char *mutt_expand_path (char *s, ssize_t slen)
175 {
176   return _mutt_expand_path (s, slen, 0);
177 }
178
179 char *_mutt_expand_path (char *s, ssize_t slen, int rx)
180 {
181   char p[_POSIX_PATH_MAX] = "";
182   char q[_POSIX_PATH_MAX] = "";
183   char tmp[_POSIX_PATH_MAX];
184   char *t;
185
186   const char *tail = "";
187
188   int recurse = 0;
189
190   do {
191     recurse = 0;
192
193     switch (*s) {
194     case '~':
195       {
196         if (*(s + 1) == '/' || *(s + 1) == 0) {
197           m_strcpy(p, sizeof(p), NONULL(Homedir));
198           tail = s + 1;
199         }
200         else {
201           struct passwd *pw;
202
203           if ((t = strchr (s + 1, '/')))
204             *t = 0;
205
206           if ((pw = getpwnam (s + 1))) {
207             m_strcpy(p, sizeof(p), pw->pw_dir);
208             if (t) {
209               *t = '/';
210               tail = t;
211             }
212             else
213               tail = "";
214           }
215           else {
216             /* user not found! */
217             if (t)
218               *t = '/';
219             *p = '\0';
220             tail = s;
221           }
222         }
223       }
224       break;
225
226     case '=':
227     case '+':
228       {
229         /* if folder = imap[s]://host/: don't append slash */
230         if (imap_is_magic (NONULL (Maildir), NULL) == M_IMAP && 
231             Maildir[m_strlen(Maildir) - 1] == '/')
232           m_strcpy(p, sizeof(p), NONULL(Maildir));
233         else
234           snprintf (p, sizeof (p), "%s/", NONULL (Maildir));
235
236         tail = s + 1;
237       }
238       break;
239
240       /* elm compatibility, @ expands alias to user name */
241
242     case '@':
243       {
244         HEADER *h;
245         /* FIXME: BUG ? */
246         address_t *alias;
247
248         if ((alias = alias_lookup(s + 1))) {
249           h = header_new();
250           h->env = envelope_new();
251           h->env->from = h->env->to = alias;
252           mutt_default_save (p, sizeof (p), h);
253           h->env->from = h->env->to = NULL;
254           header_delete(&h);
255           /* Avoid infinite recursion if the resulting folder starts with '@' */
256           if (*p != '@')
257             recurse = 1;
258
259           tail = "";
260         }
261       }
262       break;
263
264     case '>':
265       {
266         m_strcpy(p, sizeof(p), NONULL(Inbox));
267         tail = s + 1;
268       }
269       break;
270
271     case '<':
272       {
273         m_strcpy(p, sizeof(p), NONULL(Outbox));
274         tail = s + 1;
275       }
276       break;
277
278     case '!':
279       {
280         if (*(s + 1) == '!') {
281           m_strcpy(p, sizeof(p), NONULL(LastFolder));
282           tail = s + 2;
283         }
284         else {
285           m_strcpy(p, sizeof(p), NONULL(Spoolfile));
286           tail = s + 1;
287         }
288       }
289       break;
290
291     case '-':
292       {
293         m_strcpy(p, sizeof(p), NONULL(LastFolder));
294         tail = s + 1;
295       }
296       break;
297
298     case '^':
299       {
300         m_strcpy(p, sizeof(p), NONULL(CurrentFolder));
301         tail = s + 1;
302       }
303       break;
304
305     default:
306       {
307         *p = '\0';
308         tail = s;
309       }
310     }
311
312     if (rx && *p && !recurse) {
313       rx_sanitize_string (q, sizeof (q), p);
314       snprintf (tmp, sizeof (tmp), "%s%s", q, tail);
315     }
316     else
317       snprintf (tmp, sizeof (tmp), "%s%s", p, tail);
318
319     m_strcpy(s, slen, tmp);
320   }
321   while (recurse);
322
323   return (s);
324 }
325
326 /* move all the headers from extra not present in base into base */
327 void mutt_merge_envelopes(ENVELOPE* base, ENVELOPE** extra)
328 {
329   /* copies each existing element if necessary, and sets the element
330   * to NULL in the source so that envelope_delete doesn't leave us
331   * with dangling pointers. */
332 #define MOVE_ELEM(h) if (!base->h) { base->h = (*extra)->h; (*extra)->h = NULL; }
333   MOVE_ELEM(return_path);
334   MOVE_ELEM(from);
335   MOVE_ELEM(to);
336   MOVE_ELEM(cc);
337   MOVE_ELEM(bcc);
338   MOVE_ELEM(sender);
339   MOVE_ELEM(reply_to);
340   MOVE_ELEM(mail_followup_to);
341   MOVE_ELEM(list_post);
342   MOVE_ELEM(message_id);
343   MOVE_ELEM(supersedes);
344   MOVE_ELEM(date);
345   MOVE_ELEM(x_label);
346   if (!base->refs_changed) {
347     MOVE_ELEM(references);
348   }
349   if (!base->irt_changed) {
350     MOVE_ELEM(in_reply_to);
351   }
352   /* real_subj is subordinate to subject */
353   if (!base->subject) {
354     base->subject = (*extra)->subject;
355     base->real_subj = (*extra)->real_subj;
356     (*extra)->subject = NULL;
357     (*extra)->real_subj = NULL;
358   }
359   /* spam and user headers should never be hashed, and the new envelope may
360    * have better values. Use new versions regardless. */
361   mutt_buffer_free (&base->spam);
362   string_list_wipe(&base->userhdrs);
363   MOVE_ELEM(spam);
364   MOVE_ELEM(userhdrs);
365 #undef MOVE_ELEM
366   
367   envelope_delete(extra);
368 }
369
370 void _mutt_mktemp (char *s, const char *src, int line)
371 {
372
373   snprintf (s, _POSIX_PATH_MAX, "%s/madmutt-%s-%d-%d-%d-%x%x", NONULL (Tempdir),
374             NONULL (Hostname), (int) getuid (), (int) getpid (), Counter++, 
375             (unsigned int) rand(), (unsigned int) rand());
376   unlink (s);
377 }
378
379 /* collapse the pathname using ~ or = when possible */
380 void mutt_pretty_mailbox (char *s)
381 {
382   char *p = s, *q = s;
383   ssize_t len;
384   url_scheme_t scheme;
385
386   scheme = url_check_scheme (s);
387
388   if (scheme == U_IMAP || scheme == U_IMAPS) {
389     imap_pretty_mailbox (s);
390     return;
391   }
392
393   /* if s is an url, only collapse path component */
394   if (scheme != U_UNKNOWN) {
395     p = strchr (s, ':') + 1;
396     if (!strncmp (p, "//", 2))
397       q = strchr (p + 2, '/');
398     if (!q)
399       q = strchr (p, '\0');
400     p = q;
401   }
402
403   /* first attempt to collapse the pathname */
404   while (*p) {
405     if (*p == '/' && p[1] == '/') {
406       *q++ = '/';
407       p += 2;
408     }
409     else if (p[0] == '/' && p[1] == '.' && p[2] == '/') {
410       *q++ = '/';
411       p += 3;
412     }
413     else
414       *q++ = *p++;
415   }
416   *q = 0;
417
418   if (m_strncmp(s, Maildir, (len = m_strlen(Maildir))) == 0 &&
419       s[len] == '/') {
420     *s++ = '=';
421     memmove (s, s + len, m_strlen(s + len) + 1);
422   }
423   else if (m_strncmp(s, Homedir, (len = m_strlen(Homedir))) == 0 &&
424            s[len] == '/') {
425     *s++ = '~';
426     memmove (s, s + len - 1, m_strlen(s + len - 1) + 1);
427   }
428 }
429
430 void mutt_pretty_size (char *s, ssize_t len, long n)
431 {
432   if (n == 0)
433     m_strcpy(s, len, "0K");
434   else if (n < 10189)           /* 0.1K - 9.9K */
435     snprintf (s, len, "%3.1fK", (n < 103) ? 0.1 : n / 1024.0);
436   else if (n < 1023949) {       /* 10K - 999K */
437     /* 51 is magic which causes 10189/10240 to be rounded up to 10 */
438     snprintf (s, len, "%ldK", (n + 51) / 1024);
439   }
440   else if (n < 10433332)        /* 1.0M - 9.9M */
441     snprintf (s, len, "%3.1fM", n / 1048576.0);
442   else {                        /* 10M+ */
443
444     /* (10433332 + 52428) / 1048576 = 10 */
445     snprintf (s, len, "%ldM", (n + 52428) / 1048576);
446   }
447 }
448
449 void mutt_expand_file_fmt(char *dest, ssize_t destlen,
450                           const char *fmt, const char *src)
451 {
452     char tmp[LONG_STRING];
453
454     mutt_quote_filename(tmp, sizeof(tmp), src);
455     mutt_expand_fmt(dest, destlen, fmt, tmp);
456 }
457
458 void mutt_expand_fmt(char *dst, ssize_t dlen,
459                      const char *fmt, const char *src)
460 {
461     ssize_t pos = 0;
462     int found = 0;
463
464     while (*fmt && pos < dlen - 1) {
465         if (*fmt == '%') {
466             switch (*++fmt) {
467               case 's':
468                 found = 1;
469                 pos += m_strcpy(dst + pos, dlen - pos, src);
470                 break;
471
472               case '%':
473                 dst[pos++] = *fmt++;
474                 break;
475
476               default:
477                 dst[pos++] = '%';
478                 break;
479             }
480         } else {
481             dst[pos++] = *fmt++;
482         }
483     }
484
485     dst[pos] = '\0';
486     if (!found) {
487         pos += m_strcpy(dst + pos, dlen - pos, " ");
488         pos += m_strcpy(dst + pos, dlen - pos, src);
489     }
490 }
491
492 /* return 0 on success, -1 on abort, 1 on error */
493 int mutt_check_overwrite (const char *attname, const char *path,
494                           char *fname, ssize_t flen, int *append,
495                           char **directory)
496 {
497   int rc = 0;
498   char tmp[_POSIX_PATH_MAX];
499   struct stat st;
500
501   m_strcpy(fname, flen, path);
502   if (access (fname, F_OK) != 0)
503     return 0;
504   if (stat (fname, &st) != 0)
505     return -1;
506   if (S_ISDIR (st.st_mode)) {
507     if (directory) {
508       switch (mutt_multi_choice
509               (_("File is a directory, save under it? [(y)es, (n)o, (a)ll]"),
510                _("yna"))) {
511       case 3:                  /* all */
512         m_strreplace(directory, fname);
513         break;
514       case 1:                  /* yes */
515         p_delete(directory);
516         break;
517       case -1:                 /* abort */
518         p_delete(directory);
519         return -1;
520       case 2:                  /* no */
521         p_delete(directory);
522         return 1;
523       }
524     }
525     else
526       if ((rc =
527            mutt_yesorno (_("File is a directory, save under it?"),
528                          M_YES)) != M_YES)
529       return (rc == M_NO) ? 1 : -1;
530
531     if (!attname || !attname[0]) {
532       tmp[0] = 0;
533       if (mutt_get_field (_("File under directory: "), tmp, sizeof (tmp),
534                           M_FILE | M_CLEAR) != 0 || !tmp[0])
535         return (-1);
536       mutt_concat_path(fname, flen, path, tmp);
537     }
538     else
539       mutt_concat_path(fname, flen, path, mutt_basename(attname));
540   }
541
542   if (*append == 0 && access (fname, F_OK) == 0) {
543     switch (mutt_multi_choice
544             (_("File exists, (o)verwrite, (a)ppend, or (c)ancel?"), _("oac")))
545     {
546     case -1:                   /* abort */
547       return -1;
548     case 3:                    /* cancel */
549       return 1;
550
551     case 2:                    /* append */
552       *append = M_SAVE_APPEND;
553       break;
554     case 1:                    /* overwrite */
555       *append = M_SAVE_OVERWRITE;
556       break;
557     }
558   }
559   return 0;
560 }
561
562 void mutt_save_path(char *d, ssize_t dsize, address_t *a)
563 {
564     if (a && a->mailbox) {
565         m_strcpy(d, dsize, a->mailbox);
566
567         if (!option(OPTSAVEADDRESS)) {
568             char *p = strpbrk(d, "%@");
569             if (p)
570                 *p = '\0';
571         }
572         m_strtolower(d);
573     } else {
574         *d = '\0';
575     }
576 }
577
578 void mutt_safe_path(char *s, ssize_t l, address_t *a)
579 {
580     mutt_save_path(s, l, a);
581
582     while (*s) {
583         if (*s == '/' || ISSPACE(*s) || !isprint((unsigned char)*s))
584             *s = '_';
585         s++;
586     }
587 }
588
589 void mutt_FormatString (char *dest,     /* output buffer */
590                         ssize_t destlen, /* output buffer len */
591                         const char *src,        /* template string */
592                         format_t * callback,    /* callback for processing */
593                         unsigned long data,     /* callback data */
594                         format_flag flags)
595 {                               /* callback flags */
596   char prefix[SHORT_STRING], buf[LONG_STRING], *cp, *wptr = dest, ch;
597   char ifstring[SHORT_STRING], elsestring[SHORT_STRING];
598   ssize_t wlen, wid, count, col, len;
599
600   prefix[0] = '\0';
601   destlen--;                    /* save room for the terminal \0 */
602   wlen = (flags & M_FORMAT_ARROWCURSOR && option (OPTARROWCURSOR)) ? 3 : 0;
603   col = wlen;
604
605   while (*src && wlen < destlen) {
606     if (*src == '%') {
607       if (*++src == '%') {
608         *wptr++ = '%';
609         wlen++;
610         col++;
611         src++;
612         continue;
613       }
614
615       if (*src == '?') {
616         flags |= M_FORMAT_OPTIONAL;
617         src++;
618       }
619       else {
620         flags &= ~M_FORMAT_OPTIONAL;
621
622         /* eat the format string */
623         cp = prefix;
624         count = 0;
625         while (count < ssizeof (prefix) &&
626                (isdigit ((unsigned char) *src) || *src == '.' || *src == '-'))
627         {
628           *cp++ = *src++;
629           count++;
630         }
631         *cp = 0;
632       }
633
634       if (!*src)
635         break;                  /* bad format */
636
637       ch = *src++;              /* save the character to switch on */
638
639       if (flags & M_FORMAT_OPTIONAL) {
640         if (*src != '?')
641           break;                /* bad format */
642         src++;
643
644         /* eat the `if' part of the string */
645         cp = ifstring;
646         count = 0;
647         while (count < ssizeof (ifstring) && *src && *src != '?'
648                && *src != '&') {
649           *cp++ = *src++;
650           count++;
651         }
652         *cp = 0;
653
654         /* eat the `else' part of the string (optional) */
655         if (*src == '&')
656           src++;                /* skip the & */
657         cp = elsestring;
658         count = 0;
659         while (count < ssizeof (elsestring) && *src && *src != '?') {
660           *cp++ = *src++;
661           count++;
662         }
663         *cp = 0;
664
665         if (!*src)
666           break;                /* bad format */
667
668         src++;                  /* move past the trailing `?' */
669       }
670
671       /* handle generic cases first */
672       if (ch == '>') {
673         /* right justify to EOL */
674         ch = *src++;            /* pad char */
675         /* calculate space left on line.  if we've already written more data
676            than will fit on the line, ignore the rest of the line */
677         if (DrawFullLine || option (OPTSTATUSONTOP))
678           count = (COLS < destlen ? COLS : destlen);
679         else
680           count = ((COLS - SW) < destlen ? (COLS - SW) : destlen);
681         if (count > col) {
682           count -= col;         /* how many columns left on this line */
683           mutt_FormatString (buf, sizeof (buf), src, callback, data, flags);
684           wid = m_strlen(buf);
685           if (count > wid) {
686             count -= wid;       /* how many chars to pad */
687             memset (wptr, ch, count);
688             wptr += count;
689             col += count;
690           }
691           if (wid + wlen > destlen)
692             len = destlen - wlen;
693           else
694             len = wid;
695           memcpy (wptr, buf, len);
696           wptr += len;
697           wlen += len;
698           col += mutt_strwidth (buf);
699         }
700         break;                  /* skip rest of input */
701       }
702       else if (ch == '|') {
703         /* pad to EOL */
704         ch = *src++;
705         if (destlen > COLS)
706           destlen = COLS;
707         if (destlen > wlen) {
708           count = destlen - wlen;
709           memset (wptr, ch, count);
710           wptr += count;
711         }
712         break;                  /* skip rest of input */
713       }
714       else {
715         short lower = 0;
716         short nodots = 0;
717
718         while (ch == '_' || ch == ':') {
719           if (ch == '_')
720             lower = 1;
721           else if (ch == ':')
722             nodots = 1;
723
724           ch = *src++;
725         }
726
727         /* use callback function to handle this case */
728         src =
729           callback (buf, sizeof (buf), ch, src, prefix, ifstring, elsestring,
730                     data, flags);
731
732         if (lower)
733           m_strtolower(buf);
734         if (nodots) {
735           char *p = buf;
736
737           for (; *p; p++)
738             if (*p == '.')
739               *p = '_';
740         }
741
742         if ((len = m_strlen(buf)) + wlen > destlen)
743           len = (destlen - wlen > 0) ? (destlen - wlen) : 0;
744
745         memcpy (wptr, buf, len);
746         wptr += len;
747         wlen += len;
748         col += mutt_strwidth (buf);
749       }
750     }
751     else if (*src == '\\') {
752       if (!*++src)
753         break;
754       switch (*src) {
755       case 'n':
756         *wptr = '\n';
757         break;
758       case 't':
759         *wptr = '\t';
760         break;
761       case 'r':
762         *wptr = '\r';
763         break;
764       case 'f':
765         *wptr = '\f';
766         break;
767       case 'v':
768         *wptr = '\v';
769         break;
770       default:
771         *wptr = *src;
772         break;
773       }
774       src++;
775       wptr++;
776       wlen++;
777       col++;
778     }
779     else {
780       unsigned int bar = strcspn(src, "%\\");
781       char *bar2 = p_dupstr(src, bar);
782
783       while (bar--) {
784         *wptr++ = *src++;
785         wlen++;
786       }
787       col += mutt_strwidth (bar2);
788       p_delete(&bar2);
789     }
790   }
791   *wptr = 0;
792
793 #if 0
794   if (flags & M_FORMAT_MAKEPRINT) {
795     /* Make sure that the string is printable by changing all non-printable
796        chars to dots, or spaces for non-printable whitespace */
797     for (cp = dest; *cp; cp++)
798       if (!isprint(*cp) && !((flags & M_FORMAT_TREE) && (*cp <= M_TREE_MAX)))
799         *cp = isspace ((unsigned char) *cp) ? ' ' : '.';
800   }
801 #endif
802 }
803
804 /* returns 0 if OK to proceed, -1 to abort, 1 to retry */
805 int mutt_save_confirm (const char *s, struct stat *st)
806 {
807   char tmp[_POSIX_PATH_MAX];
808   int ret = 0;
809   int rc;
810   int magic = 0;
811
812   magic = mx_get_magic (s);
813
814   if (magic == M_POP) {
815     mutt_error _("Can't save message to POP mailbox.");
816
817     return 1;
818   }
819
820 #ifdef USE_NNTP
821   if (magic == M_NNTP) {
822     mutt_error _("Can't save message to newsserver.");
823
824     return 0;
825   }
826 #endif
827
828   if (magic > 0 && !mx_access (s, W_OK)) {
829     if (option (OPTCONFIRMAPPEND) &&
830         (!TrashPath || (m_strcmp(s, TrashPath) != 0))) {
831       /* if we're appending to the trash, there's no point in asking */
832       snprintf (tmp, sizeof (tmp), _("Append messages to %s?"), s);
833       if ((rc = mutt_yesorno (tmp, M_YES)) == M_NO)
834         ret = 1;
835       else if (rc == -1)
836         ret = -1;
837     }
838   }
839
840   if (stat (s, st) != -1) {
841     if (magic == -1) {
842       mutt_error (_("%s is not a mailbox!"), s);
843       return 1;
844     }
845   }
846   else {
847     if (magic != M_IMAP)
848     {
849       st->st_mtime = 0;
850       st->st_atime = 0;
851
852       if (errno == ENOENT) {
853         if (option (OPTCONFIRMCREATE)) {
854           snprintf (tmp, sizeof (tmp), _("Create %s?"), s);
855           if ((rc = mutt_yesorno (tmp, M_YES)) == M_NO)
856             ret = 1;
857           else if (rc == -1)
858             ret = -1;
859         }
860       }
861       else {
862         mutt_perror (s);
863         return 1;
864       }
865     }
866   }
867
868   CLEARLINE (LINES - 1);
869   return (ret);
870 }
871
872 void mutt_sleep (short s)
873 {
874     sleep(MAX(s, SleepTime));
875 }
876
877 /* Decrease a file's modification time by 1 second */
878 time_t mutt_decrease_mtime (const char *f, struct stat *st)
879 {
880   struct utimbuf utim;
881   struct stat _st;
882   time_t mtime;
883
884   if (!st) {
885     if (stat (f, &_st) == -1)
886       return -1;
887     st = &_st;
888   }
889
890   if ((mtime = st->st_mtime) == time (NULL)) {
891     mtime -= 1;
892     utim.actime = mtime;
893     utim.modtime = mtime;
894     utime (f, &utim);
895   }
896
897   return mtime;
898 }
899
900 /* sets mtime of 'to' to mtime of 'from' */
901 void mutt_set_mtime (const char* from, const char* to) {
902   struct utimbuf utim;
903   struct stat st;
904
905   if (stat (from, &st) != -1) {
906     utim.actime = st.st_mtime;
907     utim.modtime = st.st_mtime;
908     utime (to, &utim);
909   }
910 }
911
912 const char *mutt_make_version (int full)
913 {
914   static char vstring[STRING];
915
916   if (full)
917     snprintf (vstring, sizeof (vstring),
918               "Madmutt/%s-r%s (based on Mutt 1.5.11)",
919               MUTT_VERSION, MUTT_REVISION);
920   else
921     snprintf (vstring, sizeof (vstring), "Madmutt/%s-%s",
922               MUTT_VERSION, MUTT_REVISION);
923   return vstring;
924 }
925
926 void mutt_free_spam_list (SPAM_LIST ** list)
927 {
928   SPAM_LIST *p;
929
930   if (!list)
931     return;
932   while (*list) {
933     p = *list;
934     *list = (*list)->next;
935     rx_delete(&p->rx);
936     p_delete(&p->template);
937     p_delete(&p);
938   }
939 }
940
941 int mutt_match_spam_list (const char *s, SPAM_LIST * l, char *text, int x)
942 {
943   static regmatch_t *pmatch = NULL;
944   static int nmatch = 0;
945   int i, n, tlen;
946   char *p;
947
948   if (!s)
949     return 0;
950
951   tlen = 0;
952
953   for (; l; l = l->next) {
954     /* If this pattern needs more matches, expand pmatch. */
955     if (l->nmatch > nmatch) {
956       p_realloc(&pmatch, l->nmatch);
957       nmatch = l->nmatch;
958     }
959
960     /* Does this pattern match? */
961     if (regexec(l->rx->rx, s, l->nmatch, (regmatch_t *)pmatch, (int) 0) == 0)
962     {
963       /* Copy template into text, with substitutions. */
964       for (p = l->template; *p;) {
965         if (*p == '%') {
966           n = atoi (++p);       /* find pmatch index */
967           while (isdigit ((unsigned char) *p))
968             ++p;                /* skip subst token */
969           for (i = pmatch[n].rm_so; (i < pmatch[n].rm_eo) && (tlen < x); i++)
970             text[tlen++] = s[i];
971         }
972         else {
973           text[tlen++] = *p++;
974         }
975       }
976       text[tlen] = '\0';
977       return 1;
978     }
979   }
980
981   return 0;
982 }
983
984 int mutt_cmp_header (const HEADER * h1, const HEADER * h2) {
985   if (h1 && h2) {
986     if (h1->received != h2->received ||
987         h1->date_sent != h2->date_sent ||
988         h1->content->length != h2->content->length ||
989         h1->lines != h2->lines ||
990         h1->zhours != h2->zhours ||
991         h1->zminutes != h2->zminutes ||
992         h1->zoccident != h2->zoccident ||
993         h1->mime != h2->mime ||
994         !mutt_cmp_env (h1->env, h2->env) ||
995         !mutt_cmp_body (h1->content, h2->content))
996       return (0);
997     else
998       return (1);
999   }
1000   else {
1001     if (h1 == NULL && h2 == NULL)
1002       return (1);
1003     else
1004       return (0);
1005   }
1006 }
1007
1008 /* return 1 if address lists are strictly identical */
1009 int mutt_cmp_addr (const address_t * a, const address_t * b)
1010 {
1011   while (a && b) {
1012     if (m_strcmp(a->mailbox, b->mailbox) ||
1013         m_strcmp(a->personal, b->personal))
1014       return (0);
1015
1016     a = a->next;
1017     b = b->next;
1018   }
1019   if (a || b)
1020     return (0);
1021
1022   return (1);
1023 }
1024
1025 int mutt_cmp_list (const string_list_t * a, const string_list_t * b)
1026 {
1027   while (a && b) {
1028     if (m_strcmp(a->data, b->data))
1029       return (0);
1030
1031     a = a->next;
1032     b = b->next;
1033   }
1034   if (a || b)
1035     return (0);
1036
1037   return (1);
1038 }
1039
1040 int mutt_cmp_env (const ENVELOPE * e1, const ENVELOPE * e2)
1041 {
1042   if (e1 && e2) {
1043     if (m_strcmp(e1->message_id, e2->message_id) ||
1044         m_strcmp(e1->subject, e2->subject) ||
1045         !mutt_cmp_list (e1->references, e2->references) ||
1046         !mutt_cmp_addr (e1->from, e2->from) ||
1047         !mutt_cmp_addr (e1->sender, e2->sender) ||
1048         !mutt_cmp_addr (e1->reply_to, e2->reply_to) ||
1049         !mutt_cmp_addr (e1->to, e2->to) ||
1050         !mutt_cmp_addr (e1->cc, e2->cc) ||
1051         !mutt_cmp_addr (e1->return_path, e2->return_path))
1052       return (0);
1053     else
1054       return (1);
1055   }
1056   else {
1057     if (e1 == NULL && e2 == NULL)
1058       return (1);
1059     else
1060       return (0);
1061   }
1062 }
1063
1064 int mutt_cmp_body (const BODY * b1, const BODY * b2)
1065 {
1066   if (b1->type != b2->type ||
1067       b1->encoding != b2->encoding ||
1068       m_strcmp(b1->subtype, b2->subtype) ||
1069       m_strcmp(b1->description, b2->description) ||
1070       !parameter_equal(b1->parameter, b2->parameter) ||
1071       b1->length != b2->length)
1072     return (0);
1073   return (1);
1074 }
1075
1076 int mutt_extract_token(BUFFER *dest, BUFFER *tok, int flags)
1077 {
1078     char ch;
1079     char qc = 0;                  /* quote char */
1080     char *pc;
1081
1082     /* reset the destination pointer to the beginning of the buffer */
1083     dest->dptr = dest->data;
1084
1085     tok->dptr = vskipspaces(tok->dptr);
1086     while ((ch = *tok->dptr)) {
1087         if (!qc) {
1088             if ((ISSPACE(ch) && !(flags & M_TOKEN_SPACE))
1089             || (ch == '#' && !(flags & M_TOKEN_COMMENT))
1090             || (ch == '=' && (flags & M_TOKEN_EQUAL))
1091             || (ch == ';' && !(flags & M_TOKEN_SEMICOLON))
1092             || ((flags & M_TOKEN_PATTERN) && strchr("~=!|", ch)))
1093             {
1094                 break;
1095             }
1096         }
1097
1098         tok->dptr++;
1099
1100         if (ch == qc) {
1101             qc = 0;                     /* end of quote */
1102         } else
1103         if (!qc && (ch == '\'' || ch == '"') && !(flags & M_TOKEN_QUOTE)) {
1104             qc = ch;
1105         } else
1106         if (ch == '\\' && qc != '\'') {
1107             if (!*tok->dptr)
1108                 return -1;              /* premature end of token */
1109
1110             switch (ch = *tok->dptr++) {
1111               case 'c':
1112               case 'C':
1113                 if (!*tok->dptr)
1114                     return -1;          /* premature end of token */
1115                 mutt_buffer_addch(dest,
1116                                   (ascii_toupper(*tok->dptr) - 'A' + 1) & 0x7f);
1117                 tok->dptr++;
1118                 break;
1119               case 'r':
1120                 mutt_buffer_addch(dest, '\r');
1121                 break;
1122               case 'n':
1123                 mutt_buffer_addch(dest, '\n');
1124                 break;
1125               case 't':
1126                 mutt_buffer_addch(dest, '\t');
1127                 break;
1128               case 'f':
1129                 mutt_buffer_addch(dest, '\f');
1130                 break;
1131               case 'e':
1132                 mutt_buffer_addch(dest, '\033');
1133                 break;
1134               default:
1135                 if (isdigit((unsigned char)ch)
1136                 &&  isdigit((unsigned char)*tok->dptr)
1137                 &&  isdigit((unsigned char)*(tok->dptr + 1)))
1138                 {
1139                     mutt_buffer_addch(dest, (ch << 6) + (*tok->dptr << 3) +
1140                                             *(tok->dptr + 1) - 3504);
1141                     tok->dptr += 2;
1142                 } else {
1143                     mutt_buffer_addch(dest, ch);
1144                 }
1145             }
1146         } else
1147         if (ch == '^' && (flags & M_TOKEN_CONDENSE)) {
1148             if (!*tok->dptr)
1149                 return -1;              /* premature end of token */
1150             ch = *tok->dptr++;
1151             if (ch == '^') {
1152                 mutt_buffer_addch(dest, ch);
1153             } else
1154             if (ch == '[') {
1155                 mutt_buffer_addch(dest, '\033');
1156             } else
1157             if (isalpha((unsigned char)ch)) {
1158                 mutt_buffer_addch(dest, ascii_toupper(ch) - 'A' + 1);
1159             } else {
1160                 mutt_buffer_addch(dest, '^');
1161                 mutt_buffer_addch(dest, ch);
1162             }
1163         } else
1164         if (ch == '`' && (!qc || qc == '"')) {
1165             FILE *fp;
1166             pid_t pid;
1167             char *cmd, *ptr;
1168             ssize_t expnlen;
1169             BUFFER expn;
1170             int line = 0;
1171
1172             pc = tok->dptr;
1173             do {
1174                 if ((pc = strpbrk(pc, "\\`"))) {
1175                     /* skip any quoted chars */
1176                     if (*pc == '\\')
1177                         pc += 2;
1178                 }
1179             } while (pc && *pc != '`');
1180             if (!pc) {
1181                 return (-1);
1182             }
1183
1184             cmd = p_dupstr(tok->dptr, pc - tok->dptr);
1185             if ((pid = mutt_create_filter(cmd, NULL, &fp, NULL)) < 0) {
1186                 p_delete(&cmd);
1187                 return -1;
1188             }
1189             p_delete(&cmd);
1190
1191             tok->dptr = pc + 1;
1192
1193             /* read line */
1194             p_clear(&expn, 1);
1195             expn.data = mutt_read_line(NULL, &expn.dsize, fp, &line);
1196             fclose(fp);
1197             mutt_wait_filter(pid);
1198
1199             /* if we got output, make a new string consiting of the shell ouptput
1200                plus whatever else was left on the original line */
1201             /* BUT: If this is inside a quoted string, directly add output to 
1202              * the token */
1203             if (expn.data && qc) {
1204                 mutt_buffer_addstr(dest, expn.data);
1205                 p_delete(&expn.data);
1206             } else
1207             if (expn.data) {
1208                 expnlen = m_strlen(expn.data);
1209                 tok->dsize = expnlen + m_strlen(tok->dptr) + 1;
1210                 ptr = xmalloc(tok->dsize);
1211                 memcpy(ptr, expn.data, expnlen);
1212                 strcpy(ptr + expnlen, tok->dptr);      /* __STRCPY_CHECKED__ */
1213                 if (tok->destroy)
1214                     p_delete(&tok->data);
1215                 tok->data = ptr;
1216                 tok->dptr = ptr;
1217                 tok->destroy = 1;       /* mark that the caller should destroy this data */
1218                 ptr = NULL;
1219                 p_delete(&expn.data);
1220             }
1221         } else
1222         if (ch == '$' && (!qc || qc == '"')
1223         && (*tok->dptr == '{' || isalpha((unsigned char)*tok->dptr)))
1224         {
1225             char *env = NULL, *var = NULL;
1226
1227             if (*tok->dptr == '{') {
1228                 tok->dptr++;
1229                 if ((pc = strchr (tok->dptr, '}'))) {
1230                     var = p_dupstr(tok->dptr, pc - tok->dptr);
1231                     tok->dptr = pc + 1;
1232                 }
1233             } else {
1234                 for (pc = tok->dptr; isalnum((unsigned char)*pc) || *pc == '_';
1235                      pc++);
1236                 var = p_dupstr(tok->dptr, pc - tok->dptr);
1237                 tok->dptr = pc;
1238             }
1239             if (var) {
1240                 char tmp[STRING];
1241                 if ((env = getenv (var))
1242                 || (mutt_option_value (var, tmp, sizeof (tmp)) && (env = tmp)))
1243                 {
1244                     mutt_buffer_addstr (dest, env);
1245                 }
1246             }
1247             p_delete(&var);
1248         } else {
1249             mutt_buffer_addch(dest, ch);
1250         }
1251     }
1252     mutt_buffer_addch(dest, 0);  /* terminate the string */
1253     tok->dptr = vskipspaces(tok->dptr);
1254     return 0;
1255 }