]> git.llucax.com Git - software/mutt-debian.git/blob - postpone.c
Imported Upstream version 1.5.18
[software/mutt-debian.git] / postpone.c
1 /*
2  * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
3  * Copyright (C) 1999-2000 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_menu.h"
26 #include "rfc1524.h"
27 #include "mime.h"
28 #include "mailbox.h"
29 #include "mapping.h"
30 #include "sort.h"
31 #ifdef USE_IMAP
32 #include "mx.h"
33 #include "imap.h"
34 #endif
35 #include "mutt_crypt.h"
36
37 #include <ctype.h>
38 #include <unistd.h>
39 #include <string.h>
40 #include <sys/stat.h>
41
42 static struct mapping_t PostponeHelp[] = {
43   { N_("Exit"),  OP_EXIT },
44   { N_("Del"),   OP_DELETE },
45   { N_("Undel"), OP_UNDELETE },
46   { N_("Help"),  OP_HELP },
47   { NULL }
48 };
49
50
51
52 static short PostCount = 0;
53 static CONTEXT *PostContext = NULL;
54 static short UpdateNumPostponed = 0;
55
56 /* Return the number of postponed messages.
57  * if force is 0, use a cached value if it is costly to get a fresh
58  * count (IMAP) - else check.
59  */
60 int mutt_num_postponed (int force)
61 {
62   struct stat st;
63   CONTEXT ctx;
64
65   static time_t LastModify = 0;
66   static char *OldPostponed = NULL;
67
68   if (UpdateNumPostponed)
69   {
70     UpdateNumPostponed = 0;
71     force = 1;
72   }
73
74   if (Postponed != OldPostponed)
75   {
76     OldPostponed = Postponed;
77     LastModify = 0;
78     force = 1;
79   }
80
81   if (!Postponed)
82     return 0;
83
84 #ifdef USE_IMAP
85   /* LastModify is useless for IMAP */
86   if (mx_is_imap (Postponed))
87   {
88     if (force)
89     {
90       short newpc;
91
92       newpc = imap_status (Postponed, 0);
93       if (newpc >= 0)
94       {
95         PostCount = newpc;
96         dprint (2, (debugfile, "mutt_num_postponed: %d postponed IMAP messages found.\n", PostCount));
97       }
98       else
99         dprint (2, (debugfile, "mutt_num_postponed: using old IMAP postponed count.\n"));
100     }
101     return PostCount;
102   }
103 #endif
104
105   if (stat (Postponed, &st) == -1)
106   {
107      PostCount = 0;
108      LastModify = 0;
109      return (0);
110   }
111
112   if (S_ISDIR (st.st_mode))
113   {
114     /* if we have a maildir mailbox, we need to stat the "new" dir */
115
116     char buf[_POSIX_PATH_MAX];
117
118     snprintf (buf, sizeof (buf), "%s/new", Postponed);
119     if (access (buf, F_OK) == 0 && stat (buf, &st) == -1)
120     {
121       PostCount = 0;
122       LastModify = 0;
123       return 0;
124     }
125   }
126
127   if (LastModify < st.st_mtime)
128   {
129     LastModify = st.st_mtime;
130
131     if (access (Postponed, R_OK | F_OK) != 0)
132       return (PostCount = 0);
133     if (mx_open_mailbox (Postponed, M_NOSORT | M_QUIET, &ctx) == NULL)
134       PostCount = 0;
135     else
136       PostCount = ctx.msgcount;
137     mx_fastclose_mailbox (&ctx);
138   }
139
140   return (PostCount);
141 }
142
143 void mutt_update_num_postponed (void)
144 {
145   UpdateNumPostponed = 1;
146 }
147
148 static void post_entry (char *s, size_t slen, MUTTMENU *menu, int entry)
149 {
150   CONTEXT *ctx = (CONTEXT *) menu->data;
151
152   _mutt_make_string (s, slen, NONULL (HdrFmt), ctx, ctx->hdrs[entry],
153                      M_FORMAT_ARROWCURSOR);
154 }
155
156 static HEADER *select_msg (void)
157 {
158   MUTTMENU *menu;
159   int i, done=0, r=-1;
160   char helpstr[LONG_STRING];
161   short orig_sort;
162
163   menu = mutt_new_menu ();
164   menu->make_entry = post_entry;
165   menu->menu = MENU_POST;
166   menu->max = PostContext->msgcount;
167   menu->title = _("Postponed Messages");
168   menu->data = PostContext;
169   menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_POST, PostponeHelp);
170
171   /* The postponed mailbox is setup to have sorting disabled, but the global
172    * Sort variable may indicate something different.   Sorting has to be
173    * disabled while the postpone menu is being displayed. */
174   orig_sort = Sort;
175   Sort = SORT_ORDER;
176   
177   while (!done)
178   {
179     switch (i = mutt_menuLoop (menu))
180     {
181       case OP_DELETE:
182       case OP_UNDELETE:
183         mutt_set_flag (PostContext, PostContext->hdrs[menu->current], M_DELETE, (i == OP_DELETE) ? 1 : 0);
184         PostCount = PostContext->msgcount - PostContext->deleted;
185         if (option (OPTRESOLVE) && menu->current < menu->max - 1)
186         {
187           menu->oldcurrent = menu->current;
188           menu->current++;
189           if (menu->current >= menu->top + menu->pagelen)
190           {
191             menu->top = menu->current;
192             menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
193           }
194           else
195             menu->redraw |= REDRAW_MOTION_RESYNCH;
196         }
197         else
198           menu->redraw = REDRAW_CURRENT;
199         break;
200
201       case OP_GENERIC_SELECT_ENTRY:
202         r = menu->current;
203         done = 1;
204         break;
205
206       case OP_EXIT:
207         done = 1;
208         break;
209     }
210   }
211
212   Sort = orig_sort;
213   mutt_menuDestroy (&menu);
214   return (r > -1 ? PostContext->hdrs[r] : NULL);
215 }
216
217 /* args:
218  *      ctx     Context info, used when recalling a message to which
219  *              we reply.
220  *      hdr     envelope/attachment info for recalled message
221  *      cur     if message was a reply, `cur' is set to the message which
222  *              `hdr' is in reply to
223  *      fcc     fcc for the recalled message
224  *      fcclen  max length of fcc
225  *
226  * return vals:
227  *      -1              error/no messages
228  *      0               normal exit
229  *      SENDREPLY       recalled message is a reply
230  */
231 int mutt_get_postponed (CONTEXT *ctx, HEADER *hdr, HEADER **cur, char *fcc, size_t fcclen)
232 {
233   HEADER *h;
234   int code = SENDPOSTPONED;
235   LIST *tmp;
236   LIST *last = NULL;
237   LIST *next;
238   char *p;
239   int opt_delete;
240
241   if (!Postponed)
242     return (-1);
243
244   if ((PostContext = mx_open_mailbox (Postponed, M_NOSORT, NULL)) == NULL)
245   {
246     PostCount = 0;
247     mutt_error _("No postponed messages.");
248     return (-1);
249   }
250   
251   if (! PostContext->msgcount)
252   {
253     PostCount = 0;
254     mx_close_mailbox (PostContext, NULL);
255     FREE (&PostContext);
256     mutt_error _("No postponed messages.");
257     return (-1);
258   }
259
260   if (PostContext->msgcount == 1)
261   {
262     /* only one message, so just use that one. */
263     h = PostContext->hdrs[0];
264   }
265   else if ((h = select_msg ()) == NULL)
266   {
267     mx_close_mailbox (PostContext, NULL);
268     FREE (&PostContext);
269     return (-1);
270   }
271
272   if (mutt_prepare_template (NULL, PostContext, hdr, h, 0) < 0)
273   {
274     mx_fastclose_mailbox (PostContext);
275     FREE (&PostContext);
276     return (-1);
277   }
278
279   /* finished with this message, so delete it. */
280   mutt_set_flag (PostContext, h, M_DELETE, 1);
281
282   /* update the count for the status display */
283   PostCount = PostContext->msgcount - PostContext->deleted;
284
285   /* avoid the "purge deleted messages" prompt */
286   opt_delete = quadoption (OPT_DELETE);
287   set_quadoption (OPT_DELETE, M_YES);
288   mx_close_mailbox (PostContext, NULL);
289   set_quadoption (OPT_DELETE, opt_delete);
290
291   FREE (&PostContext);
292
293   for (tmp = hdr->env->userhdrs; tmp; )
294   {
295     if (ascii_strncasecmp ("X-Mutt-References:", tmp->data, 18) == 0)
296     {
297       if (ctx)
298       {
299         /* if a mailbox is currently open, look to see if the orignal message
300            the user attempted to reply to is in this mailbox */
301         p = tmp->data + 18;
302         SKIPWS (p);
303         if (!ctx->id_hash)
304           ctx->id_hash = mutt_make_id_hash (ctx);
305         *cur = hash_find (ctx->id_hash, p);
306       }
307
308       /* Remove the X-Mutt-References: header field. */
309       next = tmp->next;
310       if (last)
311         last->next = tmp->next;
312       else
313         hdr->env->userhdrs = tmp->next;
314       tmp->next = NULL;
315       mutt_free_list (&tmp);
316       tmp = next;
317       if (*cur)
318         code |= SENDREPLY;
319     }
320     else if (ascii_strncasecmp ("X-Mutt-Fcc:", tmp->data, 11) == 0)
321     {
322       p = tmp->data + 11;
323       SKIPWS (p);
324       strfcpy (fcc, p, fcclen);
325       mutt_pretty_mailbox (fcc);
326
327       /* remove the X-Mutt-Fcc: header field */
328       next = tmp->next;
329       if (last)
330         last->next = tmp->next;
331       else
332         hdr->env->userhdrs = tmp->next;
333       tmp->next = NULL;
334       mutt_free_list (&tmp);
335       tmp = next;
336     }
337     else if ((WithCrypto & APPLICATION_PGP)
338              && (mutt_strncmp ("Pgp:", tmp->data, 4) == 0 /* this is generated
339                                                        * by old mutt versions
340                                                        */
341                  || mutt_strncmp ("X-Mutt-PGP:", tmp->data, 11) == 0))
342     {
343       hdr->security = mutt_parse_crypt_hdr (strchr (tmp->data, ':') + 1, 1,
344                                             APPLICATION_PGP);
345       hdr->security |= APPLICATION_PGP;
346        
347       /* remove the pgp field */
348       next = tmp->next;
349       if (last)
350         last->next = tmp->next;
351       else
352         hdr->env->userhdrs = tmp->next;
353       tmp->next = NULL;
354       mutt_free_list (&tmp);
355       tmp = next;
356     }
357     else if ((WithCrypto & APPLICATION_SMIME)
358              && mutt_strncmp ("X-Mutt-SMIME:", tmp->data, 13) == 0)
359     {
360       hdr->security = mutt_parse_crypt_hdr (strchr (tmp->data, ':') + 1, 1,
361                                             APPLICATION_SMIME);
362       hdr->security |= APPLICATION_SMIME;
363        
364       /* remove the smime field */
365       next = tmp->next;
366       if (last)
367         last->next = tmp->next;
368       else
369         hdr->env->userhdrs = tmp->next;
370       tmp->next = NULL;
371       mutt_free_list (&tmp);
372       tmp = next;
373     }
374
375 #ifdef MIXMASTER
376     else if (mutt_strncmp ("X-Mutt-Mix:", tmp->data, 11) == 0)
377     {
378       char *t;
379       mutt_free_list (&hdr->chain);
380       
381       t = strtok (tmp->data + 11, " \t\n");
382       while (t)
383       {
384         hdr->chain = mutt_add_list (hdr->chain, t);
385         t = strtok (NULL, " \t\n");
386       }
387       
388       next = tmp->next;
389       if (last) 
390         last->next = tmp->next;
391       else
392         hdr->env->userhdrs = tmp->next;
393       tmp->next = NULL;
394       mutt_free_list (&tmp);
395       tmp = next;
396     }
397 #endif
398
399     else
400     {
401       last = tmp;
402       tmp = tmp->next;
403     }
404   }
405   return (code);
406 }
407
408
409
410 int mutt_parse_crypt_hdr (char *p, int set_signas, int crypt_app)
411 {
412   char smime_cryptalg[LONG_STRING] = "\0";
413   char sign_as[LONG_STRING] = "\0", *q;
414   int flags = 0;
415
416   if (!WithCrypto)
417     return 0;
418    
419   SKIPWS (p);
420   for (; *p; p++)
421   {    
422      
423     switch (*p)
424     {
425       case 'e':
426       case 'E':
427         flags |= ENCRYPT;
428         break;
429
430       case 's':    
431       case 'S':
432         flags |= SIGN;
433         q = sign_as;
434       
435         if (*(p+1) == '<')
436         {
437           for (p += 2; 
438                *p && *p != '>' && q < sign_as + sizeof (sign_as) - 1;
439                *q++ = *p++)
440             ;
441
442           if (*p!='>')
443           {
444             mutt_error _("Illegal crypto header");
445             return 0;
446           }
447         }
448        
449         *q = '\0';
450         break;
451
452       /* This used to be the micalg parameter.
453        * 
454        * It's no longer needed, so we just skip the parameter in order
455        * to be able to recall old messages.
456        */
457       case 'm':
458       case 'M':
459         if(*(p+1) == '<')
460         {
461           for (p += 2; *p && *p != '>'; p++)
462             ;
463           if(*p != '>')
464           {
465             mutt_error _("Illegal crypto header");
466             return 0;
467           }
468         }
469
470         break;
471           
472           
473       case 'c':
474       case 'C':
475         q = smime_cryptalg;
476         
477         if(*(p+1) == '<')
478         {
479           for(p += 2; *p && *p != '>' && q < smime_cryptalg + sizeof(smime_cryptalg) - 1;
480               *q++ = *p++)
481             ;
482           
483           if(*p != '>')
484           {
485             mutt_error _("Illegal S/MIME header");
486             return 0;
487           }
488         }
489
490         *q = '\0';
491         break;
492
493       case 'i':
494       case 'I':
495         flags |= INLINE;
496         break;
497
498       default:
499         mutt_error _("Illegal crypto header");
500         return 0;
501     }
502      
503   }
504
505   /* the cryptalg field must not be empty */
506   if ((WithCrypto & APPLICATION_SMIME) && *smime_cryptalg)
507     mutt_str_replace (&SmimeCryptAlg, smime_cryptalg);
508
509   /* Set {Smime,Pgp}SignAs, if desired. */
510
511   if ((WithCrypto & APPLICATION_PGP) && (crypt_app == APPLICATION_PGP)
512       && (set_signas || *sign_as))
513     mutt_str_replace (&PgpSignAs, sign_as);
514
515   if ((WithCrypto & APPLICATION_SMIME) && (crypt_app == APPLICATION_SMIME)
516       && (set_signas || *sign_as))
517     mutt_str_replace (&SmimeDefaultKey, sign_as);
518
519   return flags;
520 }
521
522
523
524 int mutt_prepare_template (FILE *fp, CONTEXT *ctx, HEADER *newhdr, HEADER *hdr,
525                                short weed)
526 {
527   MESSAGE *msg = NULL;
528   char file[_POSIX_PATH_MAX];
529   BODY *b;
530   FILE *bfp;
531   
532   int rv = -1;
533   STATE s;
534   
535   memset (&s, 0, sizeof (s));
536   
537   if (!fp && (msg = mx_open_message (ctx, hdr->msgno)) == NULL)
538     return (-1);
539
540   if (!fp) fp = msg->fp;
541
542   bfp = fp;
543
544   /* parse the message header and MIME structure */
545
546   fseeko (fp, hdr->offset, 0);
547   newhdr->offset = hdr->offset;
548   newhdr->env = mutt_read_rfc822_header (fp, newhdr, 1, weed);
549   newhdr->content->length = hdr->content->length;
550   mutt_parse_part (fp, newhdr->content);
551
552   FREE (&newhdr->env->message_id);
553   FREE (&newhdr->env->mail_followup_to); /* really? */
554
555   /* decrypt pgp/mime encoded messages */
556
557   if ((WithCrypto & (APPLICATION_PGP|APPLICATION_SMIME) & hdr->security)
558       && mutt_is_multipart_encrypted (newhdr->content))
559   {
560     int ccap = WithCrypto & (APPLICATION_PGP|APPLICATION_SMIME) & hdr->security;
561     newhdr->security |= ENCRYPT | ccap;
562     if (!crypt_valid_passphrase (ccap))
563       goto err;
564
565     mutt_message _("Decrypting message...");
566     if (((ccap & APPLICATION_PGP) && crypt_pgp_decrypt_mime (fp, &bfp, newhdr->content, &b) == -1) 
567         || ((ccap & APPLICATION_SMIME) && crypt_smime_decrypt_mime (fp, &bfp, newhdr->content, &b) == -1) 
568         || b == NULL)
569     {
570  err:
571       mx_close_message (&msg);
572       mutt_free_envelope (&newhdr->env);
573       mutt_free_body (&newhdr->content);
574       mutt_error _("Decryption failed.");
575       return -1;
576     }
577
578     mutt_free_body (&newhdr->content);
579     newhdr->content = b;
580
581     mutt_clear_error ();
582   }
583
584   /* 
585    * remove a potential multipart/signed layer - useful when
586    * resending messages 
587    */
588   
589   if (WithCrypto && mutt_is_multipart_signed (newhdr->content))
590   {
591     newhdr->security |= SIGN;
592     if ((WithCrypto & APPLICATION_PGP)
593         && ascii_strcasecmp (mutt_get_parameter ("protocol", newhdr->content->parameter), "application/pgp-signature") == 0)
594       newhdr->security |= APPLICATION_PGP;
595     else if ((WithCrypto & APPLICATION_SMIME))
596       newhdr->security |= APPLICATION_SMIME;
597     
598     /* destroy the signature */
599     mutt_free_body (&newhdr->content->parts->next);
600     newhdr->content = mutt_remove_multipart (newhdr->content);
601   }
602
603
604   /* 
605    * We don't need no primary multipart.
606    * Note: We _do_ preserve messages!
607    * 
608    * XXX - we don't handle multipart/alternative in any 
609    * smart way when sending messages.  However, one may
610    * consider this a feature.
611    * 
612    */
613
614   if (newhdr->content->type == TYPEMULTIPART)
615     newhdr->content = mutt_remove_multipart (newhdr->content);
616
617   s.fpin = bfp;
618   
619   /* create temporary files for all attachments */
620   for (b = newhdr->content; b; b = b->next)
621   {
622     
623     /* what follows is roughly a receive-mode variant of
624      * mutt_get_tmp_attachment () from muttlib.c
625      */
626
627     file[0] = '\0';
628     if (b->filename)
629     {
630       strfcpy (file, b->filename, sizeof (file));
631       b->d_filename = safe_strdup (b->filename);
632     }
633     else
634     {
635       /* avoid Content-Disposition: header with temporary filename */
636       b->use_disp = 0;
637     }
638
639     /* set up state flags */
640
641     s.flags = 0;
642
643     if (b->type == TYPETEXT)
644     {
645       if (!ascii_strcasecmp ("yes", mutt_get_parameter ("x-mutt-noconv", b->parameter)))
646         b->noconv = 1;
647       else
648       {
649         s.flags |= M_CHARCONV;
650         b->noconv = 0;
651       }
652
653       mutt_delete_parameter ("x-mutt-noconv", &b->parameter);
654     }
655
656     mutt_adv_mktemp (file, sizeof(file));
657     if ((s.fpout = safe_fopen (file, "w")) == NULL)
658       goto bail;
659
660     
661     if ((WithCrypto & APPLICATION_PGP) 
662         && (mutt_is_application_pgp (b) & (ENCRYPT|SIGN)))
663     {
664       
665       mutt_body_handler (b, &s);
666
667       newhdr->security |= mutt_is_application_pgp (newhdr->content);
668
669       b->type = TYPETEXT;
670       mutt_str_replace (&b->subtype, "plain");
671       mutt_delete_parameter ("x-action", &b->parameter);
672     }
673     else
674       mutt_decode_attachment (b, &s);
675
676     if (safe_fclose (&s.fpout) != 0)
677       goto bail;
678
679     mutt_str_replace (&b->filename, file);
680     b->unlink = 1;
681
682     mutt_stamp_attachment (b);
683
684     mutt_free_body (&b->parts);
685     if (b->hdr) b->hdr->content = NULL; /* avoid dangling pointer */
686   }
687
688   /* Fix encryption flags. */
689   
690   /* No inline if multipart. */
691   if (WithCrypto && (newhdr->security & INLINE) && newhdr->content->next)
692     newhdr->security &= ~INLINE;
693   
694   /* Do we even support multiple mechanisms? */
695   newhdr->security &= WithCrypto | ~(APPLICATION_PGP|APPLICATION_SMIME);
696   
697   /* Theoretically, both could be set. Take the one the user wants to set by default. */
698   if ((newhdr->security & APPLICATION_PGP) && (newhdr->security & APPLICATION_SMIME))
699   {
700     if (option (OPTSMIMEISDEFAULT))
701       newhdr->security &= ~APPLICATION_PGP;
702     else
703       newhdr->security &= ~APPLICATION_SMIME;
704   }
705
706   rv = 0;
707   
708   bail:
709   
710   /* that's it. */
711   if (bfp != fp) fclose (bfp);
712   if (msg) mx_close_message (&msg);
713   
714   if (rv == -1)
715   {
716     mutt_free_envelope (&newhdr->env);
717     mutt_free_body (&newhdr->content);
718   }
719   
720   return rv;
721 }