]> git.llucax.com Git - software/mutt-debian.git/blob - compose.c
removing an article form the Description of mutt-patched to make lintian happy
[software/mutt-debian.git] / compose.c
1 /*
2  * Copyright (C) 1996-2000,2002,2007 Michael R. Elkins <me@mutt.org>
3  * Copyright (C) 2004 g10 Code GmbH
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 "mutt_idna.h"
27 #include "mutt_menu.h"
28 #include "rfc1524.h"
29 #include "mime.h"
30 #include "attach.h"
31 #include "mapping.h"
32 #include "mailbox.h"
33 #include "sort.h"
34 #include "charset.h"
35
36 #ifdef MIXMASTER
37 #include "remailer.h"
38 #endif
39
40 #include <errno.h>
41 #include <string.h>
42 #include <sys/stat.h>
43 #include <sys/wait.h>
44 #include <unistd.h>
45 #include <stdlib.h>
46
47 static const char* There_are_no_attachments = N_("There are no attachments.");
48
49 #define CHECK_COUNT if (idxlen == 0) { mutt_error _(There_are_no_attachments); break; }
50
51
52
53 enum
54 {
55   HDR_FROM  = 1,
56   HDR_TO,
57   HDR_CC,
58   HDR_BCC,
59   HDR_SUBJECT,
60   HDR_REPLYTO,
61   HDR_FCC,
62
63 #ifdef MIXMASTER
64   HDR_MIX,
65 #endif
66
67   HDR_CRYPT,
68   HDR_CRYPTINFO,
69
70   HDR_ATTACH  = (HDR_FCC + 5) /* where to start printing the attachments */
71 };
72
73 #define HDR_XOFFSET 10
74 #define TITLE_FMT "%10s" /* Used for Prompts, which are ASCII */
75 #define W (COLS - HDR_XOFFSET)
76
77 static char *Prompts[] =
78 {
79   "From: ",
80   "To: ",
81   "Cc: ",
82   "Bcc: ",
83   "Subject: ",
84   "Reply-To: ",
85   "Fcc: "
86 };
87
88 static struct mapping_t ComposeHelp[] = {
89   { N_("Send"),    OP_COMPOSE_SEND_MESSAGE },
90   { N_("Abort"),   OP_EXIT },
91   { "To",      OP_COMPOSE_EDIT_TO },
92   { "CC",      OP_COMPOSE_EDIT_CC },
93   { "Subj",    OP_COMPOSE_EDIT_SUBJECT },
94   { N_("Attach file"),  OP_COMPOSE_ATTACH_FILE },
95   { N_("Descrip"), OP_COMPOSE_EDIT_DESCRIPTION },
96   { N_("Help"),    OP_HELP },
97   { NULL,       0 }
98 };
99
100 static void snd_entry (char *b, size_t blen, MUTTMENU *menu, int num)
101 {
102     mutt_FormatString (b, blen, 0, NONULL (AttachFormat), mutt_attach_fmt,
103             (unsigned long)(((ATTACHPTR **) menu->data)[num]),
104             M_FORMAT_STAT_FILE | M_FORMAT_ARROWCURSOR);
105 }
106
107
108
109 #include "mutt_crypt.h"
110
111 static void redraw_crypt_lines (HEADER *msg)
112 {
113   int off = 0;
114
115   mvaddstr (HDR_CRYPT, 0, "Security: ");
116
117   if ((WithCrypto & (APPLICATION_PGP | APPLICATION_SMIME)) == 0)
118   {
119     addstr(_("Not supported"));
120     return;
121   }
122
123   if ((msg->security & (ENCRYPT | SIGN)) == (ENCRYPT | SIGN))
124     addstr (_("Sign, Encrypt"));
125   else if (msg->security & ENCRYPT)
126     addstr (_("Encrypt"));
127   else if (msg->security & SIGN)
128     addstr (_("Sign"));
129   else
130     addstr (_("None"));
131
132   if ((msg->security & (ENCRYPT | SIGN)))
133   {
134     if ((WithCrypto & APPLICATION_PGP) && (msg->security & APPLICATION_PGP))
135     {
136       if ((msg->security & INLINE))
137         addstr (_(" (inline PGP)"));
138       else
139         addstr (_(" (PGP/MIME)"));
140     }
141     else if ((WithCrypto & APPLICATION_SMIME) &&
142              (msg->security & APPLICATION_SMIME))
143       addstr (_(" (S/MIME)"));
144   }
145
146   clrtoeol ();
147   move (HDR_CRYPTINFO, 0);
148   clrtoeol ();
149
150   if ((WithCrypto & APPLICATION_PGP)
151       && msg->security & APPLICATION_PGP  && msg->security & SIGN)
152     printw ("%s%s", _(" sign as: "), PgpSignAs ? PgpSignAs : _("<default>"));
153
154   if ((WithCrypto & APPLICATION_SMIME)
155       && msg->security & APPLICATION_SMIME  && msg->security & SIGN) {
156       printw ("%s%s", _(" sign as: "), SmimeDefaultKey ? SmimeDefaultKey : _("<default>"));
157   }
158
159   if ((WithCrypto & APPLICATION_SMIME)
160       && (msg->security & APPLICATION_SMIME)
161       && (msg->security & ENCRYPT)
162       && SmimeCryptAlg
163       && *SmimeCryptAlg) {
164       mvprintw (HDR_CRYPTINFO, 40, "%s%s", _("Encrypt with: "),
165                 NONULL(SmimeCryptAlg));
166       off = 20;
167   }
168 }
169
170
171 #ifdef MIXMASTER
172
173 static void redraw_mix_line (LIST *chain)
174 {
175   int c;
176   char *t;
177
178   mvaddstr (HDR_MIX, 0,     "     Mix: ");
179
180   if (!chain)
181   {
182     addstr ("<no chain defined>");
183     clrtoeol ();
184     return;
185   }
186   
187   for (c = 12; chain; chain = chain->next)
188   {
189     t = chain->data;
190     if (t && t[0] == '0' && t[1] == '\0')
191       t = "<random>";
192     
193     if (c + mutt_strlen (t) + 2 >= COLS)
194       break;
195
196     addstr (NONULL(t));
197     if (chain->next)
198       addstr (", ");
199
200     c += mutt_strlen (t) + 2;
201   }
202 }
203 #endif /* MIXMASTER */
204
205 static int
206 check_attachments(ATTACHPTR **idx, short idxlen)
207 {
208   int i, r;
209   struct stat st;
210   char pretty[_POSIX_PATH_MAX], msg[_POSIX_PATH_MAX + SHORT_STRING];
211
212   for (i = 0; i < idxlen; i++)
213   {
214     strfcpy(pretty, idx[i]->content->filename, sizeof(pretty));
215     if(stat(idx[i]->content->filename, &st) != 0)
216     {
217       mutt_pretty_mailbox(pretty, sizeof (pretty));
218       mutt_error(_("%s [#%d] no longer exists!"),
219                  pretty, i+1);
220       return -1;
221     }
222     
223     if(idx[i]->content->stamp < st.st_mtime)
224     {
225       mutt_pretty_mailbox(pretty, sizeof (pretty));
226       snprintf(msg, sizeof(msg), _("%s [#%d] modified. Update encoding?"),
227                pretty, i+1);
228       
229       if((r = mutt_yesorno(msg, M_YES)) == M_YES)
230         mutt_update_encoding(idx[i]->content);
231       else if(r == -1)
232         return -1;
233     }
234   }
235
236   return 0;
237 }
238
239 static void draw_envelope_addr (int line, ADDRESS *addr)
240 {
241   char buf[LONG_STRING];
242
243   buf[0] = 0;
244   rfc822_write_address (buf, sizeof (buf), addr, 1);
245   mvprintw (line, 0, TITLE_FMT, Prompts[line - 1]);
246   mutt_paddstr (W, buf);
247 }
248
249 static void draw_envelope (HEADER *msg, char *fcc)
250 {
251   draw_envelope_addr (HDR_FROM, msg->env->from);
252   draw_envelope_addr (HDR_TO, msg->env->to);
253   draw_envelope_addr (HDR_CC, msg->env->cc);
254   draw_envelope_addr (HDR_BCC, msg->env->bcc);
255   mvprintw (HDR_SUBJECT, 0, TITLE_FMT, Prompts[HDR_SUBJECT - 1]);
256   mutt_paddstr (W, NONULL (msg->env->subject));
257   draw_envelope_addr (HDR_REPLYTO, msg->env->reply_to);
258   mvprintw (HDR_FCC, 0, TITLE_FMT, Prompts[HDR_FCC - 1]);
259   mutt_paddstr (W, fcc);
260
261   if (WithCrypto)
262     redraw_crypt_lines (msg);
263
264 #ifdef MIXMASTER
265   redraw_mix_line (msg->chain);
266 #endif
267
268   SETCOLOR (MT_COLOR_STATUS);
269   mvaddstr (HDR_ATTACH - 1, 0, _("-- Attachments"));
270   BKGDSET (MT_COLOR_STATUS);
271   clrtoeol ();
272
273   BKGDSET (MT_COLOR_NORMAL);
274   SETCOLOR (MT_COLOR_NORMAL);
275 }
276
277 static int edit_address_list (int line, ADDRESS **addr)
278 {
279   char buf[HUGE_STRING] = ""; /* needs to be large for alias expansion */
280   char *err = NULL;
281   
282   mutt_addrlist_to_local (*addr);
283   rfc822_write_address (buf, sizeof (buf), *addr, 0);
284   if (mutt_get_field (Prompts[line - 1], buf, sizeof (buf), M_ALIAS) == 0)
285   {
286     rfc822_free_address (addr);
287     *addr = mutt_parse_adrlist (*addr, buf);
288     *addr = mutt_expand_aliases (*addr);
289   }
290
291   if (option (OPTNEEDREDRAW))
292   {
293     unset_option (OPTNEEDREDRAW);
294     return (REDRAW_FULL);
295   }
296
297   if (mutt_addrlist_to_idna (*addr, &err) != 0)
298   {
299     mutt_error (_("Warning: '%s' is a bad IDN."), err);
300     mutt_refresh();
301     FREE (&err);
302   }
303
304   /* redraw the expanded list so the user can see the result */
305   buf[0] = 0;
306   rfc822_write_address (buf, sizeof (buf), *addr, 1);
307   move (line, HDR_XOFFSET);
308   mutt_paddstr (W, buf);
309   
310   return 0;
311 }
312
313 static int delete_attachment (MUTTMENU *menu, short *idxlen, int x)
314 {
315   ATTACHPTR **idx = (ATTACHPTR **) menu->data;
316   int y;
317
318   menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
319
320   if (x == 0 && menu->max == 1)
321   {
322     mutt_error _("You may not delete the only attachment.");
323     idx[x]->content->tagged = 0;
324     return (-1);
325   }
326
327   for (y = 0; y < *idxlen; y++)
328   {
329     if (idx[y]->content->next == idx[x]->content)
330     {
331       idx[y]->content->next = idx[x]->content->next;
332       break;
333     }
334   }
335
336   idx[x]->content->next = NULL;
337   idx[x]->content->parts = NULL;
338   mutt_free_body (&(idx[x]->content));
339   FREE (&idx[x]->tree);
340   FREE (&idx[x]);
341   for (; x < *idxlen - 1; x++)
342     idx[x] = idx[x+1];
343   menu->max = --(*idxlen);
344   
345   return (0);
346 }
347
348 static void update_idx (MUTTMENU *menu, ATTACHPTR **idx, short idxlen)
349 {
350   idx[idxlen]->level = (idxlen > 0) ? idx[idxlen-1]->level : 0;
351   if (idxlen)
352     idx[idxlen - 1]->content->next = idx[idxlen]->content;
353   idx[idxlen]->content->aptr = idx[idxlen];
354   menu->current = idxlen++;
355   mutt_update_tree (idx, idxlen);
356   menu->max = idxlen;
357   return;
358 }
359
360
361 /* 
362  * cum_attachs_size: Cumulative Attachments Size
363  *
364  * Returns the total number of bytes used by the attachments in the
365  * attachment list _after_ content-transfer-encodings have been
366  * applied.
367  * 
368  */
369
370 static unsigned long cum_attachs_size (MUTTMENU *menu)
371 {
372   size_t s;
373   unsigned short i;
374   ATTACHPTR **idx = menu->data;
375   CONTENT *info;
376   BODY *b;
377   
378   for (i = 0, s = 0; i < menu->max; i++)
379   {
380     b = idx[i]->content;
381
382     if (!b->content)
383       b->content = mutt_get_content_info (b->filename, b);
384
385     if ((info = b->content))
386     {
387       switch (b->encoding)
388       {
389         case ENCQUOTEDPRINTABLE:
390           s += 3 * (info->lobin + info->hibin) + info->ascii + info->crlf;
391           break;
392         case ENCBASE64:
393           s += (4 * (info->lobin + info->hibin + info->ascii + info->crlf)) / 3;
394           break;
395         default:
396           s += info->lobin + info->hibin + info->ascii + info->crlf;
397           break;
398       }
399     }
400   }
401
402   return s;
403 }
404
405 /* prototype for use below */
406 static void compose_status_line (char *buf, size_t buflen, size_t col, MUTTMENU *menu, 
407       const char *p);
408
409 /*
410  * compose_format_str()
411  *
412  * %a = total number of attachments 
413  * %h = hostname  [option]
414  * %l = approx. length of current message (in bytes) 
415  * %v = Mutt version 
416  *
417  * This function is similar to status_format_str().  Look at that function for
418  * help when modifying this function.
419  */
420
421 static const char *
422 compose_format_str (char *buf, size_t buflen, size_t col, char op, const char *src,
423                    const char *prefix, const char *ifstring,
424                    const char *elsestring,
425                    unsigned long data, format_flag flags)
426 {
427   char fmt[SHORT_STRING], tmp[SHORT_STRING];
428   int optional = (flags & M_FORMAT_OPTIONAL);
429   MUTTMENU *menu = (MUTTMENU *) data;
430
431   *buf = 0;
432   switch (op)
433   {
434     case 'a': /* total number of attachments */
435         snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
436         snprintf (buf, buflen, fmt, menu->max);
437       break;
438
439     case 'h':  /* hostname */
440       snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
441       snprintf (buf, buflen, fmt, NONULL(Hostname));
442       break;
443
444     case 'l': /* approx length of current message in bytes */
445         snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
446         mutt_pretty_size (tmp, sizeof (tmp), menu ? cum_attachs_size(menu) : 0);
447         snprintf (buf, buflen, fmt, tmp);
448       break;
449
450     case 'v':
451       snprintf (fmt, sizeof (fmt), "Mutt %%s");
452       snprintf (buf, buflen, fmt, MUTT_VERSION);
453       break;
454
455     case 0:
456       *buf = 0;
457       return (src);
458
459     default:
460       snprintf (buf, buflen, "%%%s%c", prefix, op);
461       break;
462   }
463
464   if (optional)
465     compose_status_line (buf, buflen, col, menu, ifstring);
466   else if (flags & M_FORMAT_OPTIONAL)
467     compose_status_line (buf, buflen, col, menu, elsestring);
468
469   return (src);
470 }
471
472 static void compose_status_line (char *buf, size_t buflen, size_t col, MUTTMENU *menu, 
473       const char *p)
474 {
475   mutt_FormatString (buf, buflen, col, p, compose_format_str, 
476         (unsigned long) menu, 0);
477 }
478
479
480 /* return values:
481  *
482  * 1    message should be postponed
483  * 0    normal exit
484  * -1   abort message
485  */
486 int mutt_compose_menu (HEADER *msg,   /* structure for new message */
487                     char *fcc,     /* where to save a copy of the message */
488                     size_t fcclen,
489                     HEADER *cur)   /* current message */
490 {
491   char helpstr[LONG_STRING];
492   char buf[LONG_STRING];
493   char fname[_POSIX_PATH_MAX];
494   MUTTMENU *menu;
495   ATTACHPTR **idx = NULL;
496   short idxlen = 0;
497   short idxmax = 0;
498   int i, close = 0;
499   int r = -1;           /* return value */
500   int op = 0;
501   int loop = 1;
502   int fccSet = 0;       /* has the user edited the Fcc: field ? */
503   CONTEXT *ctx = NULL, *this = NULL;
504   /* Sort, SortAux could be changed in mutt_index_menu() */
505   int oldSort, oldSortAux;
506   struct stat st;
507
508   mutt_attach_init (msg->content);
509   idx = mutt_gen_attach_list (msg->content, -1, idx, &idxlen, &idxmax, 0, 1);
510
511   menu = mutt_new_menu (MENU_COMPOSE);
512   menu->offset = HDR_ATTACH;
513   menu->max = idxlen;
514   menu->make_entry = snd_entry;
515   menu->tag = mutt_tag_attach;
516   menu->data = idx;
517   menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_COMPOSE, ComposeHelp);
518   
519   while (loop)
520   {
521     switch (op = mutt_menuLoop (menu))
522     {
523       case OP_REDRAW:
524         draw_envelope (msg, fcc);
525         menu->offset = HDR_ATTACH;
526         menu->pagelen = LINES - HDR_ATTACH - 2;
527         break;
528       case OP_COMPOSE_EDIT_FROM:
529         menu->redraw = edit_address_list (HDR_FROM, &msg->env->from);
530         mutt_message_hook (NULL, msg, M_SEND2HOOK);
531         break;
532       case OP_COMPOSE_EDIT_TO:
533         menu->redraw = edit_address_list (HDR_TO, &msg->env->to);
534         mutt_message_hook (NULL, msg, M_SEND2HOOK);
535         break;
536       case OP_COMPOSE_EDIT_BCC:
537         menu->redraw = edit_address_list (HDR_BCC, &msg->env->bcc);
538         mutt_message_hook (NULL, msg, M_SEND2HOOK);
539         break;
540       case OP_COMPOSE_EDIT_CC:
541         menu->redraw = edit_address_list (HDR_CC, &msg->env->cc);
542         mutt_message_hook (NULL, msg, M_SEND2HOOK);     
543         break;
544       case OP_COMPOSE_EDIT_SUBJECT:
545         if (msg->env->subject)
546           strfcpy (buf, msg->env->subject, sizeof (buf));
547         else
548           buf[0] = 0;
549         if (mutt_get_field ("Subject: ", buf, sizeof (buf), 0) == 0)
550         {
551           mutt_str_replace (&msg->env->subject, buf);
552           move (HDR_SUBJECT, HDR_XOFFSET);
553           clrtoeol ();
554           if (msg->env->subject)
555             mutt_paddstr (W, msg->env->subject);
556         }
557         mutt_message_hook (NULL, msg, M_SEND2HOOK);
558         break;
559       case OP_COMPOSE_EDIT_REPLY_TO:
560         menu->redraw = edit_address_list (HDR_REPLYTO, &msg->env->reply_to);
561         mutt_message_hook (NULL, msg, M_SEND2HOOK);
562         break;
563       case OP_COMPOSE_EDIT_FCC:
564         strfcpy (buf, fcc, sizeof (buf));
565         if (mutt_get_field ("Fcc: ", buf, sizeof (buf), M_FILE | M_CLEAR) == 0)
566         {
567           strfcpy (fcc, buf, fcclen);
568           mutt_pretty_mailbox (fcc, fcclen);
569           move (HDR_FCC, HDR_XOFFSET);
570           mutt_paddstr (W, fcc);
571           fccSet = 1;
572         }
573         MAYBE_REDRAW (menu->redraw);
574         mutt_message_hook (NULL, msg, M_SEND2HOOK);
575         break;
576       case OP_COMPOSE_EDIT_MESSAGE:
577         if (Editor && (mutt_strcmp ("builtin", Editor) != 0) && !option (OPTEDITHDRS))
578         {
579           mutt_edit_file (Editor, msg->content->filename);
580           mutt_update_encoding (msg->content);
581           menu->redraw = REDRAW_FULL;
582           mutt_message_hook (NULL, msg, M_SEND2HOOK);
583           break;
584         }
585         /* fall through */
586       case OP_COMPOSE_EDIT_HEADERS:
587         if (mutt_strcmp ("builtin", Editor) != 0 &&
588             (op == OP_COMPOSE_EDIT_HEADERS ||
589             (op == OP_COMPOSE_EDIT_MESSAGE && option (OPTEDITHDRS))))
590         {
591           char *tag = NULL, *err = NULL;
592           mutt_env_to_local (msg->env);
593           mutt_edit_headers (NONULL (Editor), msg->content->filename, msg,
594                              fcc, fcclen);
595           if (mutt_env_to_idna (msg->env, &tag, &err))
596           {
597             mutt_error (_("Bad IDN in \"%s\": '%s'"), tag, err);
598             FREE (&err);
599           }
600         }
601         else
602         {
603           /* this is grouped with OP_COMPOSE_EDIT_HEADERS because the
604              attachment list could change if the user invokes ~v to edit
605              the message with headers, in which we need to execute the
606              code below to regenerate the index array */
607           mutt_builtin_editor (msg->content->filename, msg, cur);
608         }
609         mutt_update_encoding (msg->content);
610
611         /* attachments may have been added */
612         if (idxlen && idx[idxlen - 1]->content->next)
613         {
614           for (i = 0; i < idxlen; i++)
615             FREE (&idx[i]);
616           idxlen = 0;
617           idx = mutt_gen_attach_list (msg->content, -1, idx, &idxlen, &idxmax, 0, 1);
618           menu->data = idx;
619           menu->max = idxlen;
620         }
621
622         menu->redraw = REDRAW_FULL;
623         mutt_message_hook (NULL, msg, M_SEND2HOOK);
624         break;
625
626
627
628       case OP_COMPOSE_ATTACH_KEY:
629         if (!(WithCrypto & APPLICATION_PGP))
630           break;       
631         if (idxlen == idxmax)
632         {
633           safe_realloc (&idx, sizeof (ATTACHPTR *) * (idxmax += 5));
634           menu->data = idx;
635         }
636         
637         idx[idxlen] = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR));
638         if ((idx[idxlen]->content = crypt_pgp_make_key_attachment(NULL)) != NULL)
639         {
640           update_idx (menu, idx, idxlen++);
641           menu->redraw |= REDRAW_INDEX;
642         }
643         else
644           FREE (&idx[idxlen]);
645
646         menu->redraw |= REDRAW_STATUS;
647
648         if (option(OPTNEEDREDRAW))
649         {
650           menu->redraw = REDRAW_FULL;
651           unset_option(OPTNEEDREDRAW);
652         }
653         
654         mutt_message_hook (NULL, msg, M_SEND2HOOK);
655         break;
656
657
658       case OP_COMPOSE_ATTACH_FILE:
659         {
660           char *prompt, **files;
661           int error, numfiles;
662
663           fname[0] = 0;
664           prompt = _("Attach file");
665           numfiles = 0;
666           files = NULL;
667
668           if (_mutt_enter_fname (prompt, fname, sizeof (fname), &menu->redraw, 0, 1, &files, &numfiles) == -1 ||
669               *fname == '\0')
670             break;
671
672           if (idxlen + numfiles >= idxmax)
673           {
674             safe_realloc (&idx, sizeof (ATTACHPTR *) * (idxmax += 5 + numfiles));
675             menu->data = idx;
676           }
677
678           error = 0;
679           if (numfiles > 1)
680             mutt_message _("Attaching selected files...");
681           for (i = 0; i < numfiles; i++)
682           {
683             char *att = files[i];
684             idx[idxlen] = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR));
685             idx[idxlen]->unowned = 1;
686             idx[idxlen]->content = mutt_make_file_attach (att);
687             if (idx[idxlen]->content != NULL)
688               update_idx (menu, idx, idxlen++);
689             else
690             {
691               error = 1;
692               mutt_error (_("Unable to attach %s!"), att);
693               FREE (&idx[idxlen]);
694             }
695           }
696           
697           FREE (&files);
698           if (!error) mutt_clear_error ();
699
700           menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
701         }
702         mutt_message_hook (NULL, msg, M_SEND2HOOK);
703         break;
704
705       case OP_COMPOSE_ATTACH_MESSAGE:
706         {
707           char *prompt;
708           HEADER *h;
709
710           fname[0] = 0;
711           prompt = _("Open mailbox to attach message from");
712
713           if (Context)
714           {
715             strfcpy (fname, NONULL (Context->path), sizeof (fname));
716             mutt_pretty_mailbox (fname, sizeof (fname));
717           }
718
719           if (mutt_enter_fname (prompt, fname, sizeof (fname), &menu->redraw, 1) == -1 || !fname[0])
720             break;
721
722           mutt_expand_path (fname, sizeof (fname));
723 #ifdef USE_IMAP
724           if (!mx_is_imap (fname))
725 #endif
726 #ifdef USE_POP
727           if (!mx_is_pop (fname))
728 #endif
729           /* check to make sure the file exists and is readable */
730           if (access (fname, R_OK) == -1)
731           {
732             mutt_perror (fname);
733             break;
734           }
735
736           menu->redraw = REDRAW_FULL;
737
738           ctx = mx_open_mailbox (fname, M_READONLY, NULL);
739           if (ctx == NULL)
740           {
741             mutt_perror (fname);
742             break;
743           }
744
745           if (!ctx->msgcount)
746           {
747             mx_close_mailbox (ctx, NULL);
748             FREE (&ctx);
749             mutt_error _("No messages in that folder.");
750             break;
751           }
752
753           this = Context; /* remember current folder and sort methods*/
754           oldSort = Sort; oldSortAux = SortAux;
755           
756           Context = ctx;
757           set_option(OPTATTACHMSG);
758           mutt_message _("Tag the messages you want to attach!");
759           close = mutt_index_menu ();
760           unset_option(OPTATTACHMSG);
761
762           if (!Context)
763           {
764             /* go back to the folder we started from */
765             Context = this;
766             /* Restore old $sort and $sort_aux */
767             Sort = oldSort;
768             SortAux = oldSortAux;
769             menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
770             break;
771           }
772
773           if (idxlen + Context->tagged >= idxmax)
774           {
775             safe_realloc (&idx, sizeof (ATTACHPTR *) * (idxmax += 5 + Context->tagged));
776             menu->data = idx;
777           }
778
779           for (i = 0; i < Context->msgcount; i++)
780           {
781             h = Context->hdrs[i];
782             if (h->tagged)
783             {
784               idx[idxlen] = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR));
785               idx[idxlen]->content = mutt_make_message_attach (Context, h, 1);
786               if (idx[idxlen]->content != NULL)
787                 update_idx (menu, idx, idxlen++);
788               else
789               {
790                 mutt_error _("Unable to attach!");
791                 FREE (&idx[idxlen]);
792               }
793             }
794           }
795           menu->redraw |= REDRAW_FULL;
796
797           if (close == OP_QUIT) 
798             mx_close_mailbox (Context, NULL);
799           else
800             mx_fastclose_mailbox (Context);
801           FREE (&Context);
802
803           /* go back to the folder we started from */
804           Context = this;
805           /* Restore old $sort and $sort_aux */
806           Sort = oldSort;
807           SortAux = oldSortAux;
808         }
809         mutt_message_hook (NULL, msg, M_SEND2HOOK);
810         break;
811
812       case OP_DELETE:
813         CHECK_COUNT;
814         if (idx[menu->current]->unowned)
815           idx[menu->current]->content->unlink = 0;
816         if (delete_attachment (menu, &idxlen, menu->current) == -1)
817           break;
818         mutt_update_tree (idx, idxlen);
819         if (idxlen)
820         {
821           if (menu->current > idxlen - 1)
822             menu->current = idxlen - 1;
823         }
824         else
825           menu->current = 0;
826
827         if (menu->current == 0)
828           msg->content = idx[0]->content;
829
830         menu->redraw |= REDRAW_STATUS;
831         mutt_message_hook (NULL, msg, M_SEND2HOOK);
832         break;
833
834 #define CURRENT idx[menu->current]->content
835       
836       case OP_COMPOSE_TOGGLE_RECODE:
837       {      
838         CHECK_COUNT;
839         if (!mutt_is_text_part (CURRENT))
840         {
841           mutt_error (_("Recoding only affects text attachments."));
842           break;
843         }
844         CURRENT->noconv = !CURRENT->noconv;
845         if (CURRENT->noconv)
846           mutt_message (_("The current attachment won't be converted."));
847         else
848           mutt_message (_("The current attachment will be converted."));
849         menu->redraw = REDRAW_CURRENT;
850         mutt_message_hook (NULL, msg, M_SEND2HOOK);
851         break;
852       }
853 #undef CURRENT
854
855       case OP_COMPOSE_EDIT_DESCRIPTION:
856         CHECK_COUNT;
857         strfcpy (buf,
858                  idx[menu->current]->content->description ?
859                  idx[menu->current]->content->description : "",
860                  sizeof (buf));
861         /* header names should not be translated */
862         if (mutt_get_field ("Description: ", buf, sizeof (buf), 0) == 0)
863         {
864           mutt_str_replace (&idx[menu->current]->content->description, buf);
865           menu->redraw = REDRAW_CURRENT;
866         }
867         mutt_message_hook (NULL, msg, M_SEND2HOOK);
868         break;
869
870       case OP_COMPOSE_UPDATE_ENCODING:
871         CHECK_COUNT;
872         if (menu->tagprefix)
873         {
874           BODY *top;
875           for (top = msg->content; top; top = top->next)
876           {
877             if (top->tagged)
878               mutt_update_encoding (top);
879           }
880           menu->redraw = REDRAW_FULL;
881         }
882         else
883         {
884           mutt_update_encoding(idx[menu->current]->content);
885           menu->redraw = REDRAW_CURRENT | REDRAW_STATUS;
886         }
887         mutt_message_hook (NULL, msg, M_SEND2HOOK);
888         break;
889       
890       case OP_COMPOSE_TOGGLE_DISPOSITION:
891         /* toggle the content-disposition between inline/attachment */
892         idx[menu->current]->content->disposition = (idx[menu->current]->content->disposition == DISPINLINE) ? DISPATTACH : DISPINLINE;
893         menu->redraw = REDRAW_CURRENT;
894         break;
895
896       case OP_EDIT_TYPE:
897         CHECK_COUNT;
898         {
899           mutt_edit_content_type (NULL, idx[menu->current]->content, NULL);
900
901           /* this may have been a change to text/something */
902           mutt_update_encoding (idx[menu->current]->content);
903
904           menu->redraw = REDRAW_CURRENT;
905         }
906         mutt_message_hook (NULL, msg, M_SEND2HOOK);
907         break;
908
909       case OP_COMPOSE_EDIT_ENCODING:
910         CHECK_COUNT;
911         strfcpy (buf, ENCODING (idx[menu->current]->content->encoding),
912                                                               sizeof (buf));
913         if (mutt_get_field ("Content-Transfer-Encoding: ", buf,
914                                             sizeof (buf), 0) == 0 && buf[0])
915         {
916           if ((i = mutt_check_encoding (buf)) != ENCOTHER && i != ENCUUENCODED)
917           {
918             idx[menu->current]->content->encoding = i;
919             menu->redraw = REDRAW_CURRENT | REDRAW_STATUS;
920             mutt_clear_error();
921           }
922           else
923             mutt_error _("Invalid encoding.");
924         }
925         mutt_message_hook (NULL, msg, M_SEND2HOOK);
926         break;
927
928       case OP_COMPOSE_SEND_MESSAGE:
929
930         /* Note: We don't invoke send2-hook here, since we want to leave
931          * users an opportunity to change settings from the ":" prompt.
932          */
933       
934         if(check_attachments(idx, idxlen) != 0)
935         {
936           menu->redraw = REDRAW_FULL;
937           break;
938         }
939
940       
941 #ifdef MIXMASTER
942         if (msg->chain && mix_check_message (msg) != 0)
943           break;
944 #endif
945       
946         if (!fccSet && *fcc)
947         {
948           if ((i = query_quadoption (OPT_COPY,
949                                 _("Save a copy of this message?"))) == -1)
950             break;
951           else if (i == M_NO)
952             *fcc = 0;
953         }
954
955         loop = 0;
956         r = 0;
957         break;
958
959       case OP_COMPOSE_EDIT_FILE:
960         CHECK_COUNT;
961         mutt_edit_file (NONULL(Editor), idx[menu->current]->content->filename);
962         mutt_update_encoding (idx[menu->current]->content);
963         menu->redraw = REDRAW_CURRENT | REDRAW_STATUS;
964         mutt_message_hook (NULL, msg, M_SEND2HOOK);
965         break;
966
967       case OP_COMPOSE_TOGGLE_UNLINK:
968         CHECK_COUNT;
969         idx[menu->current]->content->unlink = !idx[menu->current]->content->unlink;
970
971 #if 0
972         /* OPTRESOLVE is otherwise ignored on this menu.
973          * Where's the bug?
974          */
975
976         if (option (OPTRESOLVE) && menu->current + 1 < menu->max)
977           menu->current++;
978 # endif
979         menu->redraw = REDRAW_INDEX;
980         /* No send2hook since this doesn't change the message. */
981         break;
982
983       case OP_COMPOSE_GET_ATTACHMENT:
984         CHECK_COUNT;
985         if(menu->tagprefix)
986         {
987           BODY *top;
988           for(top = msg->content; top; top = top->next)
989           {
990             if(top->tagged)
991               mutt_get_tmp_attachment(top);
992           }
993           menu->redraw = REDRAW_FULL;
994         }
995         else if (mutt_get_tmp_attachment(idx[menu->current]->content) == 0)
996           menu->redraw = REDRAW_CURRENT;
997
998         /* No send2hook since this doesn't change the message. */
999         break;
1000       
1001       case OP_COMPOSE_RENAME_FILE:
1002         CHECK_COUNT;
1003         strfcpy (fname, idx[menu->current]->content->filename, sizeof (fname));
1004         mutt_pretty_mailbox (fname, sizeof (fname));
1005         if (mutt_get_field (_("Rename to: "), fname, sizeof (fname), M_FILE)
1006                                                         == 0 && fname[0])
1007         {
1008           if (stat(idx[menu->current]->content->filename, &st) == -1)
1009           {
1010             mutt_error (_("Can't stat %s: %s"), fname, strerror (errno));
1011             break;
1012           }
1013
1014           mutt_expand_path (fname, sizeof (fname));
1015           if(mutt_rename_file (idx[menu->current]->content->filename, fname))
1016             break;
1017           
1018           mutt_str_replace (&idx[menu->current]->content->filename, fname);
1019           menu->redraw = REDRAW_CURRENT;
1020
1021           if(idx[menu->current]->content->stamp >= st.st_mtime)
1022             mutt_stamp_attachment(idx[menu->current]->content);
1023           
1024         }
1025         mutt_message_hook (NULL, msg, M_SEND2HOOK);
1026         break;
1027
1028       case OP_COMPOSE_NEW_MIME:
1029         {
1030           char type[STRING];
1031           char *p;
1032           int itype;
1033           FILE *fp;
1034
1035           CLEARLINE (LINES-1);
1036           fname[0] = 0;
1037           if (mutt_get_field (_("New file: "), fname, sizeof (fname), M_FILE)
1038               != 0 || !fname[0])
1039             continue;
1040           mutt_expand_path (fname, sizeof (fname));
1041
1042           /* Call to lookup_mime_type () ?  maybe later */
1043           type[0] = 0;
1044           if (mutt_get_field ("Content-Type: ", type, sizeof (type), 0) != 0 
1045               || !type[0])
1046             continue;
1047
1048           if (!(p = strchr (type, '/')))
1049           {
1050             mutt_error _("Content-Type is of the form base/sub");
1051             continue;
1052           }
1053           *p++ = 0;
1054           if ((itype = mutt_check_mime_type (type)) == TYPEOTHER)
1055           {
1056             mutt_error (_("Unknown Content-Type %s"), type);
1057             continue;
1058           }
1059           if (idxlen == idxmax)
1060           {
1061             safe_realloc (&idx, sizeof (ATTACHPTR *) * (idxmax += 5));
1062             menu->data = idx;
1063           }
1064
1065           idx[idxlen] = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR));
1066           /* Touch the file */
1067           if (!(fp = safe_fopen (fname, "w")))
1068           {
1069             mutt_error (_("Can't create file %s"), fname);
1070             FREE (&idx[idxlen]);
1071             continue;
1072           }
1073           safe_fclose (&fp);
1074
1075           if ((idx[idxlen]->content = mutt_make_file_attach (fname)) == NULL)
1076           {
1077             mutt_error _("What we have here is a failure to make an attachment");
1078             continue;
1079           }
1080           update_idx (menu, idx, idxlen++);
1081
1082           idx[menu->current]->content->type = itype;
1083           mutt_str_replace (&idx[menu->current]->content->subtype, p);
1084           idx[menu->current]->content->unlink = 1;
1085           menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
1086
1087           if (mutt_compose_attachment (idx[menu->current]->content))
1088           {
1089             mutt_update_encoding (idx[menu->current]->content);
1090             menu->redraw = REDRAW_FULL;
1091           }
1092         }
1093         mutt_message_hook (NULL, msg, M_SEND2HOOK);    
1094         break;
1095
1096       case OP_COMPOSE_EDIT_MIME:
1097         CHECK_COUNT;
1098         if (mutt_edit_attachment (idx[menu->current]->content))
1099         {
1100           mutt_update_encoding (idx[menu->current]->content);
1101           menu->redraw = REDRAW_FULL;
1102         }
1103         mutt_message_hook (NULL, msg, M_SEND2HOOK);
1104         break;
1105
1106       case OP_VIEW_ATTACH:
1107       case OP_DISPLAY_HEADERS:
1108         CHECK_COUNT;
1109         mutt_attach_display_loop (menu, op, NULL, NULL, NULL, &idx, &idxlen, NULL, 0);
1110         menu->redraw = REDRAW_FULL;
1111         /* no send2hook, since this doesn't modify the message */
1112         break;
1113
1114       case OP_SAVE:
1115         CHECK_COUNT;
1116         mutt_save_attachment_list (NULL, menu->tagprefix, menu->tagprefix ?  msg->content : idx[menu->current]->content, NULL, menu);
1117         MAYBE_REDRAW (menu->redraw);
1118         /* no send2hook, since this doesn't modify the message */
1119         break;
1120
1121       case OP_PRINT:
1122         CHECK_COUNT;
1123         mutt_print_attachment_list (NULL, menu->tagprefix, menu->tagprefix ? msg->content : idx[menu->current]->content);
1124         /* no send2hook, since this doesn't modify the message */
1125         break;
1126
1127       case OP_PIPE:
1128       case OP_FILTER:
1129         CHECK_COUNT;
1130         mutt_pipe_attachment_list (NULL, menu->tagprefix, menu->tagprefix ? msg->content : idx[menu->current]->content, op == OP_FILTER);
1131         if (op == OP_FILTER) /* cte might have changed */
1132           menu->redraw = menu->tagprefix ? REDRAW_FULL : REDRAW_CURRENT;
1133         menu->redraw |= REDRAW_STATUS;
1134         mutt_message_hook (NULL, msg, M_SEND2HOOK);
1135         break;
1136
1137       case OP_EXIT:
1138         if ((i = query_quadoption (OPT_POSTPONE, _("Postpone this message?"))) == M_NO)
1139         {
1140           while (idxlen-- > 0)
1141           {
1142             /* avoid freeing other attachments */
1143             idx[idxlen]->content->next = NULL;
1144             idx[idxlen]->content->parts = NULL;
1145             if (idx[idxlen]->unowned)
1146               idx[idxlen]->content->unlink = 0;
1147             mutt_free_body (&idx[idxlen]->content);
1148             FREE (&idx[idxlen]->tree);
1149             FREE (&idx[idxlen]);
1150           }
1151           FREE (&idx);
1152           idxlen = 0;
1153           idxmax = 0;
1154           r = -1;
1155           loop = 0;
1156           break;
1157         }
1158         else if (i == -1)
1159           break; /* abort */
1160
1161         /* fall through to postpone! */
1162
1163       case OP_COMPOSE_POSTPONE_MESSAGE:
1164
1165         if(check_attachments(idx, idxlen) != 0)
1166         {
1167           menu->redraw = REDRAW_FULL;
1168           break;
1169         }
1170       
1171         loop = 0;
1172         r = 1;
1173         break;
1174
1175       case OP_COMPOSE_ISPELL:
1176         endwin ();
1177         snprintf (buf, sizeof (buf), "%s -x %s", NONULL(Ispell), msg->content->filename);
1178         if (mutt_system (buf) == -1)
1179           mutt_error (_("Error running \"%s\"!"), buf);
1180         else
1181         {
1182           mutt_update_encoding (msg->content);
1183           menu->redraw |= REDRAW_STATUS;
1184         }
1185         break;
1186
1187       case OP_COMPOSE_WRITE_MESSAGE:
1188
1189        fname[0] = '\0';
1190        if (Context)
1191        {
1192          strfcpy (fname, NONULL (Context->path), sizeof (fname));
1193          mutt_pretty_mailbox (fname, sizeof (fname));
1194        }
1195        if (idxlen)
1196          msg->content = idx[0]->content;
1197        if (mutt_enter_fname (_("Write message to mailbox"), fname, sizeof (fname),
1198                              &menu->redraw, 1) != -1 && fname[0])
1199        {
1200          mutt_message (_("Writing message to %s ..."), fname);
1201          mutt_expand_path (fname, sizeof (fname));
1202
1203          if (msg->content->next)
1204            msg->content = mutt_make_multipart (msg->content);
1205
1206          if (mutt_write_fcc (fname, msg, NULL, 0, NULL) < 0)
1207            msg->content = mutt_remove_multipart (msg->content);
1208          else
1209            mutt_message _("Message written.");
1210        }
1211        break;
1212
1213
1214
1215       case OP_COMPOSE_PGP_MENU:
1216         if (!(WithCrypto & APPLICATION_PGP))
1217           break;
1218         if ((WithCrypto & APPLICATION_SMIME)
1219             && msg->security & APPLICATION_SMIME)
1220         {
1221           if (mutt_yesorno (_("S/MIME already selected. Clear & continue ? "),
1222                              M_YES) != M_YES)
1223           {
1224             mutt_clear_error ();
1225             break;
1226           }
1227           msg->security = 0;
1228         }
1229         msg->security = crypt_pgp_send_menu (msg, &menu->redraw);
1230         redraw_crypt_lines (msg);
1231         mutt_message_hook (NULL, msg, M_SEND2HOOK);
1232         break;
1233
1234
1235       case OP_FORGET_PASSPHRASE:
1236         crypt_forget_passphrase ();
1237         break;
1238
1239
1240       case OP_COMPOSE_SMIME_MENU:
1241         if (!(WithCrypto & APPLICATION_SMIME))
1242           break;
1243
1244         if ((WithCrypto & APPLICATION_PGP)
1245             && msg->security & APPLICATION_PGP)
1246         {
1247           if (mutt_yesorno (_("PGP already selected. Clear & continue ? "),
1248                               M_YES) != M_YES)
1249           {
1250              mutt_clear_error ();
1251              break;
1252           }
1253           msg->security = 0;
1254         }
1255         msg->security = crypt_smime_send_menu(msg, &menu->redraw);
1256         redraw_crypt_lines (msg);
1257         mutt_message_hook (NULL, msg, M_SEND2HOOK);
1258         break;
1259
1260
1261 #ifdef MIXMASTER
1262       case OP_COMPOSE_MIX:
1263       
1264         mix_make_chain (&msg->chain, &menu->redraw);
1265         mutt_message_hook (NULL, msg, M_SEND2HOOK);
1266         break;
1267 #endif
1268
1269     }
1270
1271     /* Draw formated compose status line */
1272     if (menu->redraw & REDRAW_STATUS) 
1273     {
1274         compose_status_line (buf, sizeof (buf), 0, menu, NONULL(ComposeFormat));
1275         CLEARLINE (option (OPTSTATUSONTOP) ? 0 : LINES-2);
1276         SETCOLOR (MT_COLOR_STATUS);
1277         BKGDSET (MT_COLOR_STATUS);
1278         mutt_paddstr (COLS, buf);
1279         SETCOLOR (MT_COLOR_NORMAL);
1280         BKGDSET (MT_COLOR_NORMAL);
1281         menu->redraw &= ~REDRAW_STATUS;
1282     }
1283   }
1284
1285   mutt_menuDestroy (&menu);
1286
1287   if (idxlen)
1288   {
1289     msg->content = idx[0]->content;
1290     for (i = 0; i < idxlen; i++)
1291     {
1292       idx[i]->content->aptr = NULL;
1293       FREE (&idx[i]);
1294     }
1295   }
1296   else
1297     msg->content = NULL;
1298
1299   FREE (&idx);
1300
1301   return (r);
1302 }
1303