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 
snd_entry(char * b,size_t blen,MUTTMENU * menu,int num)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 
redraw_crypt_lines(HEADER * msg)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 
redraw_mix_line(LIST * chain)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
check_attachments(ATTACHPTR ** idx,short idxlen)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 
draw_envelope_addr(int line,ADDRESS * addr)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 
draw_envelope(HEADER * msg,char * fcc)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 
edit_address_list(int line,ADDRESS ** addr)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 
delete_attachment(MUTTMENU * menu,short * idxlen,int x)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 
update_idx(MUTTMENU * menu,ATTACHPTR ** idx,short idxlen)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 
cum_attachs_size(MUTTMENU * menu)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 *
compose_format_str(char * buf,size_t buflen,size_t col,char op,const char * src,const char * prefix,const char * ifstring,const char * elsestring,unsigned long data,format_flag flags)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 
compose_status_line(char * buf,size_t buflen,size_t col,MUTTMENU * menu,const char * p)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  */
mutt_compose_menu(HEADER * msg,char * fcc,size_t fcclen,HEADER * cur)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 
1304