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