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