Rocco Rutte:
[apps/madmutt.git] / send.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
4  *
5  * This file is part of mutt-ng, see http://www.muttng.org/.
6  * It's licensed under the GNU General Public License,
7  * please see the file GPL in the top level source directory.
8  */
9
10 #if HAVE_CONFIG_H
11 # include "config.h"
12 #endif
13
14 #include "mutt.h"
15 #include "enter.h"
16 #include "ascii.h"
17 #include "mutt_curses.h"
18 #include "rfc2047.h"
19 #include "rfc3676.h"
20 #include "keymap.h"
21 #include "mime.h"
22 #include "copy.h"
23 #include "mx.h"
24 #include "mutt_crypt.h"
25 #include "mutt_idna.h"
26 #include "url.h"
27 #include "attach.h"
28
29 #include "lib/mem.h"
30 #include "lib/intl.h"
31 #include "lib/str.h"
32 #include "lib/debug.h"
33
34 #include <ctype.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <string.h>
38 #include <errno.h>
39 #include <sys/stat.h>
40 #include <sys/wait.h>
41 #include <dirent.h>
42 #include <time.h>
43 #include <sys/types.h>
44 #include <utime.h>
45
46 #ifdef USE_NNTP
47 #include "nntp.h"
48 #endif
49
50 #ifdef MIXMASTER
51 #include "remailer.h"
52 #endif
53
54
55 static void append_signature (FILE * f)
56 {
57   FILE *tmpfp;
58   pid_t thepid;
59
60   if (SignOffString) {
61     fprintf (f, "\n%s", SignOffString);
62   }
63
64   if (Signature && (tmpfp = mutt_open_read (Signature, &thepid))) {
65     if (option (OPTSIGDASHES))
66       fputs ("\n-- \n", f);
67     else if (SignOffString)
68       fputs ("\n", f);
69     mutt_copy_stream (tmpfp, f);
70     fclose (tmpfp);
71     if (thepid != -1)
72       mutt_wait_filter (thepid);
73   }
74 }
75
76 /* compare two e-mail addresses and return 1 if they are equivalent */
77 static int mutt_addrcmp (ADDRESS * a, ADDRESS * b)
78 {
79   if (!a->mailbox || !b->mailbox)
80     return 0;
81   if (ascii_strcasecmp (a->mailbox, b->mailbox))
82     return 0;
83   return 1;
84 }
85
86 /* search an e-mail address in a list */
87 static int mutt_addrsrc (ADDRESS * a, ADDRESS * lst)
88 {
89   for (; lst; lst = lst->next) {
90     if (mutt_addrcmp (a, lst))
91       return (1);
92   }
93   return (0);
94 }
95
96 /* removes addresses from "b" which are contained in "a" */
97 static ADDRESS *mutt_remove_xrefs (ADDRESS * a, ADDRESS * b)
98 {
99   ADDRESS *top, *p, *prev = NULL;
100
101   top = b;
102   while (b) {
103     for (p = a; p; p = p->next) {
104       if (mutt_addrcmp (p, b))
105         break;
106     }
107     if (p) {
108       if (prev) {
109         prev->next = b->next;
110         b->next = NULL;
111         rfc822_free_address (&b);
112         b = prev;
113       }
114       else {
115         top = top->next;
116         b->next = NULL;
117         rfc822_free_address (&b);
118         b = top;
119       }
120     }
121     else {
122       prev = b;
123       b = b->next;
124     }
125   }
126   return top;
127 }
128
129 /* remove any address which matches the current user.  if `leave_only' is
130  * nonzero, don't remove the user's address if it is the only one in the list
131  */
132 static ADDRESS *remove_user (ADDRESS * a, int leave_only)
133 {
134   ADDRESS *top = NULL, *last = NULL;
135
136   while (a) {
137     if (!mutt_addr_is_user (a)) {
138       if (top) {
139         last->next = a;
140         last = last->next;
141       }
142       else
143         last = top = a;
144       a = a->next;
145       last->next = NULL;
146     }
147     else {
148       ADDRESS *tmp = a;
149
150       a = a->next;
151       if (!leave_only || a || last) {
152         tmp->next = NULL;
153         rfc822_free_address (&tmp);
154       }
155       else
156         last = top = tmp;
157     }
158   }
159   return top;
160 }
161
162 static ADDRESS *find_mailing_lists (ADDRESS * t, ADDRESS * c)
163 {
164   ADDRESS *top = NULL, *ptr = NULL;
165
166   for (; t || c; t = c, c = NULL) {
167     for (; t; t = t->next) {
168       if (mutt_is_mail_list (t) && !t->group) {
169         if (top) {
170           ptr->next = rfc822_cpy_adr_real (t);
171           ptr = ptr->next;
172         }
173         else
174           ptr = top = rfc822_cpy_adr_real (t);
175       }
176     }
177   }
178   return top;
179 }
180
181 static int edit_address (ADDRESS ** a, /* const */ char *field)
182 {
183   char buf[HUGE_STRING];
184   char *err = NULL;
185   int idna_ok = 0;
186
187   do {
188     buf[0] = 0;
189     mutt_addrlist_to_local (*a);
190     rfc822_write_address (buf, sizeof (buf), *a, 0);
191     if (mutt_get_field (field, buf, sizeof (buf), M_ALIAS) != 0)
192       return (-1);
193     rfc822_free_address (a);
194     *a = mutt_expand_aliases (mutt_parse_adrlist (NULL, buf));
195     if ((idna_ok = mutt_addrlist_to_idna (*a, &err)) != 0) {
196       mutt_error (_("Error: '%s' is a bad IDN."), err);
197       mutt_refresh ();
198       mutt_sleep (2);
199       mem_free (&err);
200     }
201   }
202   while (idna_ok != 0);
203   return 0;
204 }
205
206 static int edit_envelope (ENVELOPE * en, int flags)
207 {
208   char buf[HUGE_STRING];
209   LIST *uh = UserHeader;
210   regmatch_t pat_match[1];
211
212 #ifdef USE_NNTP
213   if (option (OPTNEWSSEND)) {
214     if (en->newsgroups)
215       strfcpy (buf, en->newsgroups, sizeof (buf));
216     else
217       buf[0] = 0;
218     if (mutt_get_field ("Newsgroups: ", buf, sizeof (buf), 0) != 0)
219       return (-1);
220     mem_free (&en->newsgroups);
221     en->newsgroups = str_dup (buf);
222
223     if (en->followup_to)
224       strfcpy (buf, en->followup_to, sizeof (buf));
225     else
226       buf[0] = 0;
227     if (option (OPTASKFOLLOWUP)
228         && mutt_get_field ("Followup-To: ", buf, sizeof (buf), 0) != 0)
229       return (-1);
230     mem_free (&en->followup_to);
231     en->followup_to = str_dup (buf);
232
233     if (en->x_comment_to)
234       strfcpy (buf, en->x_comment_to, sizeof (buf));
235     else
236       buf[0] = 0;
237     if (option (OPTXCOMMENTTO) && option (OPTASKXCOMMENTTO)
238         && mutt_get_field ("X-Comment-To: ", buf, sizeof (buf), 0) != 0)
239       return (-1);
240     mem_free (&en->x_comment_to);
241     en->x_comment_to = str_dup (buf);
242   }
243   else
244 #endif
245   {
246     if (edit_address (&en->to, "To: ") == -1 || en->to == NULL)
247       return (-1);
248     if (option (OPTASKCC) && edit_address (&en->cc, "Cc: ") == -1)
249       return (-1);
250     if (option (OPTASKBCC) && edit_address (&en->bcc, "Bcc: ") == -1)
251       return (-1);
252   }
253
254   if (en->subject) {
255     if (option (OPTFASTREPLY))
256       return (0);
257     else
258       strfcpy (buf, en->subject, sizeof (buf));
259   }
260   else {
261     char *p;
262
263     buf[0] = 0;
264     for (; uh; uh = uh->next) {
265       if (ascii_strncasecmp ("subject:", uh->data, 8) == 0) {
266         p = uh->data + 8;
267         SKIPWS (p);
268         strncpy (buf, p, sizeof (buf));
269       }
270     }
271   }
272
273   if ((flags & (SENDREPLY)) && option (OPTSTRIPWAS) && StripWasRegexp.rx &&
274       regexec (StripWasRegexp.rx, buf, 1, pat_match, 0) == 0) {
275     unsigned int pos = pat_match->rm_so;
276
277     if (ascii_strncasecmp (buf, "re: ", pos) != 0) {
278       buf[pos] = '\0';          /* kill match */
279       while (pos-- && buf[pos] == ' ')
280         buf[pos] = '\0';        /* remove trailing spaces */
281     }
282     else {
283       mutt_error (_("Ignoring $strip_was: Subject would be empty."));
284       sleep (2);
285     }
286   }
287   if (mutt_get_field ("Subject: ", buf, sizeof (buf), 0) != 0 || (!buf[0]
288                                                                   &&
289                                                                   query_quadoption
290                                                                   (OPT_SUBJECT,
291                                                                    _
292                                                                    ("No subject, abort?"))
293                                                                   != M_NO)) {
294     mutt_message _("No subject, aborting.");
295
296     return (-1);
297   }
298   str_replace (&en->subject, buf);
299
300   return 0;
301 }
302
303 #ifdef USE_NNTP
304 char *nntp_get_header (const char *s)
305 {
306   SKIPWS (s);
307   return str_dup (s);
308 }
309 #endif
310
311 static void process_user_recips (ENVELOPE * env)
312 {
313   LIST *uh = UserHeader;
314
315   for (; uh; uh = uh->next) {
316     if (ascii_strncasecmp ("to:", uh->data, 3) == 0)
317       env->to = rfc822_parse_adrlist (env->to, uh->data + 3);
318     else if (ascii_strncasecmp ("cc:", uh->data, 3) == 0)
319       env->cc = rfc822_parse_adrlist (env->cc, uh->data + 3);
320     else if (ascii_strncasecmp ("bcc:", uh->data, 4) == 0)
321       env->bcc = rfc822_parse_adrlist (env->bcc, uh->data + 4);
322 #ifdef USE_NNTP
323     else if (ascii_strncasecmp ("newsgroups:", uh->data, 11) == 0)
324       env->newsgroups = nntp_get_header (uh->data + 11);
325     else if (ascii_strncasecmp ("followup-to:", uh->data, 12) == 0)
326       env->followup_to = nntp_get_header (uh->data + 12);
327     else if (ascii_strncasecmp ("x-comment-to:", uh->data, 13) == 0)
328       env->x_comment_to = nntp_get_header (uh->data + 13);
329 #endif
330   }
331 }
332
333 static void process_user_header (ENVELOPE * env)
334 {
335   LIST *uh = UserHeader;
336   LIST *last = env->userhdrs;
337
338   if (last)
339     while (last->next)
340       last = last->next;
341
342   for (; uh; uh = uh->next) {
343     if (ascii_strncasecmp ("from:", uh->data, 5) == 0) {
344       /* User has specified a default From: address.  Remove default address */
345       rfc822_free_address (&env->from);
346       env->from = rfc822_parse_adrlist (env->from, uh->data + 5);
347     }
348     else if (ascii_strncasecmp ("reply-to:", uh->data, 9) == 0) {
349       rfc822_free_address (&env->reply_to);
350       env->reply_to = rfc822_parse_adrlist (env->reply_to, uh->data + 9);
351     }
352     else if (ascii_strncasecmp ("message-id:", uh->data, 11) == 0)
353       str_replace (&env->message_id, uh->data + 11);
354     else if (ascii_strncasecmp ("to:", uh->data, 3) != 0 &&
355              ascii_strncasecmp ("cc:", uh->data, 3) != 0 &&
356              ascii_strncasecmp ("bcc:", uh->data, 4) != 0 &&
357 #ifdef USE_NNTP
358              ascii_strncasecmp ("newsgroups:", uh->data, 11) != 0 &&
359              ascii_strncasecmp ("followup-to:", uh->data, 12) != 0 &&
360              ascii_strncasecmp ("x-comment-to:", uh->data, 13) != 0 &&
361 #endif
362              ascii_strncasecmp ("supersedes:", uh->data, 11) != 0 &&
363              ascii_strncasecmp ("subject:", uh->data, 8) != 0) {
364       if (last) {
365         last->next = mutt_new_list ();
366         last = last->next;
367       }
368       else
369         last = env->userhdrs = mutt_new_list ();
370       last->data = str_dup (uh->data);
371     }
372   }
373 }
374
375 void mutt_forward_intro (FILE * fp, HEADER * cur)
376 {
377   char buffer[STRING];
378
379   fputs ("----- Forwarded message from ", fp);
380   buffer[0] = 0;
381   rfc822_write_address (buffer, sizeof (buffer), cur->env->from, 1);
382   fputs (buffer, fp);
383   fputs (" -----\n\n", fp);
384 }
385
386 void mutt_forward_trailer (FILE * fp)
387 {
388   fputs ("\n----- End forwarded message -----\n", fp);
389 }
390
391
392 static int include_forward (CONTEXT * ctx, HEADER * cur, FILE * out)
393 {
394   int chflags = CH_DECODE, cmflags = 0;
395
396   mutt_parse_mime_message (ctx, cur);
397   mutt_message_hook (ctx, cur, M_MESSAGEHOOK);
398
399   if (WithCrypto && (cur->security & ENCRYPT) && option (OPTFORWDECODE)) {
400     /* make sure we have the user's passphrase before proceeding... */
401     crypt_valid_passphrase (cur->security);
402   }
403
404   mutt_forward_intro (out, cur);
405
406   if (option (OPTFORWDECODE)) {
407     cmflags |= M_CM_DECODE | M_CM_CHARCONV;
408     if (option (OPTWEED)) {
409       chflags |= CH_WEED | CH_REORDER;
410       cmflags |= M_CM_WEED;
411     }
412   }
413   if (option (OPTFORWQUOTE))
414     cmflags |= M_CM_PREFIX;
415
416   mutt_copy_message (out, ctx, cur, cmflags, chflags);
417   mutt_forward_trailer (out);
418   return 0;
419 }
420
421 void mutt_make_attribution (CONTEXT * ctx, HEADER * cur, FILE * out)
422 {
423   char buffer[STRING];
424
425   if (Attribution) {
426     mutt_make_string (buffer, sizeof (buffer), Attribution, ctx, cur);
427     fputs (buffer, out);
428     fputc ('\n', out);
429   }
430 }
431
432 void mutt_make_post_indent (CONTEXT * ctx, HEADER * cur, FILE * out)
433 {
434   char buffer[STRING];
435
436   if (PostIndentString) {
437     mutt_make_string (buffer, sizeof (buffer), PostIndentString, ctx, cur);
438     fputs (buffer, out);
439     fputc ('\n', out);
440   }
441 }
442
443 static int include_reply (CONTEXT * ctx, HEADER * cur, FILE * out)
444 {
445   int cmflags = M_CM_PREFIX | M_CM_DECODE | M_CM_CHARCONV | M_CM_REPLYING;
446   int chflags = CH_DECODE;
447
448   if (WithCrypto && (cur->security & ENCRYPT)) {
449     /* make sure we have the user's passphrase before proceeding... */
450     crypt_valid_passphrase (cur->security);
451   }
452
453   mutt_parse_mime_message (ctx, cur);
454   mutt_message_hook (ctx, cur, M_MESSAGEHOOK);
455
456   mutt_make_attribution (ctx, cur, out);
457
458   if (!option (OPTHEADER))
459     cmflags |= M_CM_NOHEADER;
460   if (option (OPTWEED)) {
461     chflags |= CH_WEED | CH_REORDER;
462     cmflags |= M_CM_WEED;
463   }
464
465   mutt_copy_message (out, ctx, cur, cmflags, chflags);
466
467   mutt_make_post_indent (ctx, cur, out);
468
469   return 0;
470 }
471
472 static int default_to (ADDRESS ** to, ENVELOPE * env, int flags, int hmfupto)
473 {
474   char prompt[STRING];
475   ADDRESS *tmp;
476
477   if (flags && env->mail_followup_to && hmfupto == M_YES) {
478     rfc822_append (to, env->mail_followup_to);
479     return 0;
480   }
481
482   /* Exit now if we're setting up the default Cc list for list-reply
483    * (only set if Mail-Followup-To is present and honoured).
484    */
485   if (flags & SENDLISTREPLY)
486     return 0;
487
488   /* If this message came from a mailing list, ask the user if he really
489    * intended to reply to the author only.
490    */
491   if (!(flags & SENDGROUPREPLY) && mutt_is_list_cc (0, env->to, env->cc)) {
492     switch (query_quadoption (OPT_LISTREPLY,
493                               _
494                               ("Message came from a mailing list. Reply to author only?")))
495     {
496     case M_NO:
497       tmp = find_mailing_lists (env->to, env->cc);
498       rfc822_append (to, tmp);
499       rfc822_free_address (&tmp);
500       return 0;
501     case -1:
502       return -1;                /* abort */
503     }
504   }
505
506   if (!option (OPTREPLYSELF) && mutt_addr_is_user (env->from)) {
507     /* mail is from the user, assume replying to recipients */
508     rfc822_append (to, env->to);
509   }
510   else if (env->reply_to) {
511     if ((mutt_addrcmp (env->from, env->reply_to) && !env->reply_to->next) ||
512         (option (OPTIGNORELISTREPLYTO) &&
513          mutt_is_mail_list (env->reply_to) &&
514          (mutt_addrsrc (env->reply_to, env->to) ||
515           mutt_addrsrc (env->reply_to, env->cc)))) {
516       /* If the Reply-To: address is a mailing list, assume that it was
517        * put there by the mailing list, and use the From: address
518        * 
519        * We also take the from header if our correspondant has a reply-to
520        * header which is identical to the electronic mail address given
521        * in his From header.
522        * 
523        */
524       rfc822_append (to, env->from);
525     }
526     else if (!(mutt_addrcmp (env->from, env->reply_to) &&
527                !env->reply_to->next) && quadoption (OPT_REPLYTO) != M_YES) {
528       /* There are quite a few mailing lists which set the Reply-To:
529        * header field to the list address, which makes it quite impossible
530        * to send a message to only the sender of the message.  This
531        * provides a way to do that.
532        */
533       snprintf (prompt, sizeof (prompt), _("Reply to %s%s?"),
534                 env->reply_to->mailbox, env->reply_to->next ? ",..." : "");
535       switch (query_quadoption (OPT_REPLYTO, prompt)) {
536       case M_YES:
537         rfc822_append (to, env->reply_to);
538         break;
539
540       case M_NO:
541         rfc822_append (to, env->from);
542         break;
543
544       default:
545         return (-1);            /* abort */
546       }
547     }
548     else
549       rfc822_append (to, env->reply_to);
550   }
551   else
552     rfc822_append (to, env->from);
553
554   return (0);
555 }
556
557 int mutt_fetch_recips (ENVELOPE * out, ENVELOPE * in, int flags)
558 {
559   char prompt[STRING];
560   ADDRESS *tmp;
561   int hmfupto = -1;
562
563   if ((flags & (SENDLISTREPLY | SENDGROUPREPLY)) && in->mail_followup_to) {
564     snprintf (prompt, sizeof (prompt), _("Follow-up to %s%s?"),
565               in->mail_followup_to->mailbox,
566               in->mail_followup_to->next ? ",..." : "");
567
568     if ((hmfupto = query_quadoption (OPT_MFUPTO, prompt)) == -1)
569       return -1;
570   }
571
572   if (flags & SENDLISTREPLY) {
573     tmp = find_mailing_lists (in->to, in->cc);
574     rfc822_append (&out->to, tmp);
575     rfc822_free_address (&tmp);
576
577     if (in->mail_followup_to && hmfupto == M_YES &&
578         default_to (&out->cc, in, flags & SENDLISTREPLY, hmfupto) == -1)
579       return (-1);              /* abort */
580   }
581   else {
582     if (default_to (&out->to, in, flags & SENDGROUPREPLY, hmfupto) == -1)
583       return (-1);              /* abort */
584
585     if ((flags & SENDGROUPREPLY)
586         && (!in->mail_followup_to || hmfupto != M_YES)) {
587       /* if(!mutt_addr_is_user(in->to)) */
588       rfc822_append (&out->cc, in->to);
589       rfc822_append (&out->cc, in->cc);
590     }
591   }
592   return 0;
593 }
594
595 LIST *mutt_make_references (ENVELOPE * e)
596 {
597   LIST *t = NULL, *l = NULL;
598
599   if (e->references)
600     l = mutt_copy_list (e->references);
601   else
602     l = mutt_copy_list (e->in_reply_to);
603
604   if (e->message_id) {
605     t = mutt_new_list ();
606     t->data = str_dup (e->message_id);
607     t->next = l;
608     l = t;
609   }
610
611   return l;
612 }
613
614 void mutt_fix_reply_recipients (ENVELOPE * env)
615 {
616   mutt_expand_aliases_env (env);
617
618   if (!option (OPTMETOO)) {
619     /* the order is important here.  do the CC: first so that if the
620      * the user is the only recipient, it ends up on the TO: field
621      */
622     env->cc = remove_user (env->cc, (env->to == NULL));
623     env->to = remove_user (env->to, (env->cc == NULL));
624   }
625
626   /* the CC field can get cluttered, especially with lists */
627   env->to = mutt_remove_duplicates (env->to);
628   env->cc = mutt_remove_duplicates (env->cc);
629   env->cc = mutt_remove_xrefs (env->to, env->cc);
630 }
631
632 void mutt_make_forward_subject (ENVELOPE * env, CONTEXT * ctx, HEADER * cur)
633 {
634   char buffer[STRING];
635
636   /* set the default subject for the message. */
637   mutt_make_string (buffer, sizeof (buffer), NONULL (ForwFmt), ctx, cur);
638   str_replace (&env->subject, buffer);
639 }
640
641 void mutt_make_misc_reply_headers (ENVELOPE * env, CONTEXT * ctx,
642                                    HEADER * cur, ENVELOPE * curenv)
643 {
644   /* This takes precedence over a subject that might have
645    * been taken from a List-Post header.  Is that correct?
646    */
647   if (curenv->real_subj) {
648     mem_free (&env->subject);
649     env->subject = mem_malloc (str_len (curenv->real_subj) + 5);
650     sprintf (env->subject, "Re: %s", curenv->real_subj);        /* __SPRINTF_CHECKED__ */
651   }
652   else if (!env->subject)
653     env->subject = str_dup ("Re: your mail");
654
655 #ifdef USE_NNTP
656   if (option (OPTNEWSSEND) && option (OPTXCOMMENTTO) && curenv->from)
657     env->x_comment_to = str_dup (mutt_get_name (curenv->from));
658 #endif
659 }
660
661 void mutt_add_to_reference_headers (ENVELOPE * env, ENVELOPE * curenv,
662                                     LIST *** pp, LIST *** qq)
663 {
664   LIST **p = NULL, **q = NULL;
665
666   if (pp)
667     p = *pp;
668   if (qq)
669     q = *qq;
670
671   if (!p)
672     p = &env->references;
673   if (!q)
674     q = &env->in_reply_to;
675
676   while (*p)
677     p = &(*p)->next;
678   while (*q)
679     q = &(*q)->next;
680
681   *p = mutt_make_references (curenv);
682
683   if (curenv->message_id) {
684     *q = mutt_new_list ();
685     (*q)->data = str_dup (curenv->message_id);
686   }
687
688   if (pp)
689     *pp = p;
690   if (qq)
691     *qq = q;
692
693 }
694
695 static void
696 mutt_make_reference_headers (ENVELOPE * curenv, ENVELOPE * env, CONTEXT * ctx)
697 {
698   env->references = NULL;
699   env->in_reply_to = NULL;
700
701   if (!curenv) {
702     HEADER *h;
703     LIST **p = NULL, **q = NULL;
704     int i;
705
706     for (i = 0; i < ctx->vcount; i++) {
707       h = ctx->hdrs[ctx->v2r[i]];
708       if (h->tagged)
709         mutt_add_to_reference_headers (env, h->env, &p, &q);
710     }
711   }
712   else
713     mutt_add_to_reference_headers (env, curenv, NULL, NULL);
714 }
715
716 static int
717 envelope_defaults (ENVELOPE * env, CONTEXT * ctx, HEADER * cur, int flags)
718 {
719   ENVELOPE *curenv = NULL;
720   int i = 0, tag = 0;
721
722   if (!cur) {
723     tag = 1;
724     for (i = 0; i < ctx->vcount; i++)
725       if (ctx->hdrs[ctx->v2r[i]]->tagged) {
726         cur = ctx->hdrs[ctx->v2r[i]];
727         curenv = cur->env;
728         break;
729       }
730
731     if (!cur) {
732       /* This could happen if the user tagged some messages and then did
733        * a limit such that none of the tagged message are visible.
734        */
735       mutt_error _("No tagged messages are visible!");
736
737       return (-1);
738     }
739   }
740   else
741     curenv = cur->env;
742
743   if (flags & SENDREPLY) {
744 #ifdef USE_NNTP
745     if ((flags & SENDNEWS)) {
746       /* in case followup set Newsgroups: with Followup-To: if it present */
747       if (!env->newsgroups && curenv &&
748           str_casecmp (curenv->followup_to, "poster"))
749         env->newsgroups = str_dup (curenv->followup_to);
750     }
751     else
752 #endif
753     if (tag) {
754       HEADER *h;
755
756       for (i = 0; i < ctx->vcount; i++) {
757         h = ctx->hdrs[ctx->v2r[i]];
758         if (h->tagged && mutt_fetch_recips (env, h->env, flags) == -1)
759           return -1;
760       }
761     }
762     else if (mutt_fetch_recips (env, curenv, flags) == -1)
763       return -1;
764
765     if ((flags & SENDLISTREPLY) && !env->to) {
766       mutt_error _("No mailing lists found!");
767
768       return (-1);
769     }
770
771     mutt_make_misc_reply_headers (env, ctx, cur, curenv);
772     mutt_make_reference_headers (tag ? NULL : curenv, env, ctx);
773   }
774   else if (flags & SENDFORWARD)
775     mutt_make_forward_subject (env, ctx, cur);
776
777   return (0);
778 }
779
780 static int generate_body (FILE * tempfp,        /* stream for outgoing message */
781                           HEADER * msg, /* header for outgoing message */
782                           int flags,    /* compose mode */
783                           CONTEXT * ctx,        /* current mailbox */
784                           HEADER * cur)
785 {                               /* current message */
786   int i;
787   HEADER *h;
788   BODY *tmp;
789
790   if (flags & SENDREPLY) {
791     if ((i =
792          query_quadoption (OPT_INCLUDE,
793                            _("Include message in reply?"))) == -1)
794       return (-1);
795
796     if (i == M_YES) {
797       mutt_message _("Including quoted message...");
798
799       if (!cur) {
800         for (i = 0; i < ctx->vcount; i++) {
801           h = ctx->hdrs[ctx->v2r[i]];
802           if (h->tagged) {
803             if (include_reply (ctx, h, tempfp) == -1) {
804               mutt_error _("Could not include all requested messages!");
805
806               return (-1);
807             }
808             fputc ('\n', tempfp);
809           }
810         }
811       }
812       else
813         include_reply (ctx, cur, tempfp);
814
815     }
816   }
817   else if (flags & SENDFORWARD) {
818     if ((i =
819          query_quadoption (OPT_MIMEFWD,
820                            _("Forward as attachment?"))) == M_YES) {
821       BODY *last = msg->content;
822
823       mutt_message _("Preparing forwarded message...");
824
825       while (last && last->next)
826         last = last->next;
827
828       if (cur) {
829         tmp = mutt_make_message_attach (ctx, cur, 0);
830         if (last)
831           last->next = tmp;
832         else
833           msg->content = tmp;
834       }
835       else {
836         for (i = 0; i < ctx->vcount; i++) {
837           if (ctx->hdrs[ctx->v2r[i]]->tagged) {
838             tmp = mutt_make_message_attach (ctx, ctx->hdrs[ctx->v2r[i]], 0);
839             if (last) {
840               last->next = tmp;
841               last = tmp;
842             }
843             else
844               last = msg->content = tmp;
845           }
846         }
847       }
848     }
849     else if (i != -1) {
850       if (cur)
851         include_forward (ctx, cur, tempfp);
852       else
853         for (i = 0; i < ctx->vcount; i++)
854           if (ctx->hdrs[ctx->v2r[i]]->tagged)
855             include_forward (ctx, ctx->hdrs[ctx->v2r[i]], tempfp);
856     }
857     else if (i == -1)
858       return -1;
859   }
860   /* if (WithCrypto && (flags & SENDKEY)) */
861   else if ((WithCrypto & APPLICATION_PGP) && (flags & SENDKEY)) {
862     BODY *tmp;
863
864     if ((WithCrypto & APPLICATION_PGP)
865         && (tmp = crypt_pgp_make_key_attachment (NULL)) == NULL)
866       return -1;
867
868     tmp->next = msg->content;
869     msg->content = tmp;
870   }
871
872   mutt_clear_error ();
873
874   return (0);
875 }
876
877 void mutt_set_followup_to (ENVELOPE * e)
878 {
879   ADDRESS *t = NULL;
880   ADDRESS *from;
881
882   /* 
883    * Only generate the Mail-Followup-To if the user has requested it, and
884    * it hasn't already been set
885    */
886
887   if (!option (OPTFOLLOWUPTO))
888     return;
889 #ifdef USE_NNTP
890   if (option (OPTNEWSSEND)) {
891     if (!e->followup_to && e->newsgroups && (strrchr (e->newsgroups, ',')))
892       e->followup_to = str_dup (e->newsgroups);
893     return;
894   }
895 #endif
896
897   if (!e->mail_followup_to) {
898     if (mutt_is_list_cc (0, e->to, e->cc)) {
899       /* 
900        * this message goes to known mailing lists, so create a proper
901        * mail-followup-to header
902        */
903
904       t = rfc822_append (&e->mail_followup_to, e->to);
905       rfc822_append (&t, e->cc);
906     }
907
908     /* remove ourselves from the mail-followup-to header */
909     e->mail_followup_to = remove_user (e->mail_followup_to, 0);
910
911     /*
912      * If we are not subscribed to any of the lists in question,
913      * re-add ourselves to the mail-followup-to header.  The 
914      * mail-followup-to header generated is a no-op with group-reply,
915      * but makes sure list-reply has the desired effect.
916      */
917
918     if (e->mail_followup_to && !mutt_is_list_recipient (0, e->to, e->cc)) {
919       if (e->reply_to)
920         from = rfc822_cpy_adr (e->reply_to);
921       else if (e->from)
922         from = rfc822_cpy_adr (e->from);
923       else
924         from = mutt_default_from ();
925
926       if (from) {
927         /* Normally, this loop will not even be entered. */
928         for (t = from; t && t->next; t = t->next);
929
930         t->next = e->mail_followup_to;  /* t cannot be NULL at this point. */
931         e->mail_followup_to = from;
932       }
933     }
934
935     e->mail_followup_to = mutt_remove_duplicates (e->mail_followup_to);
936
937   }
938 }
939
940
941 /* look through the recipients of the message we are replying to, and if
942    we find an address that matches $alternates, we use that as the default
943    from field */
944 static ADDRESS *set_reverse_name (ENVELOPE * env)
945 {
946   ADDRESS *tmp;
947
948   for (tmp = env->to; tmp; tmp = tmp->next) {
949     if (mutt_addr_is_user (tmp))
950       break;
951   }
952   if (!tmp) {
953     for (tmp = env->cc; tmp; tmp = tmp->next) {
954       if (mutt_addr_is_user (tmp))
955         break;
956     }
957   }
958   if (!tmp && mutt_addr_is_user (env->from))
959     tmp = env->from;
960   if (tmp) {
961     tmp = rfc822_cpy_adr_real (tmp);
962     if (!option (OPTREVREAL))
963       mem_free (&tmp->personal);
964     if (!tmp->personal)
965       tmp->personal = str_dup (Realname);
966   }
967   return (tmp);
968 }
969
970 ADDRESS *mutt_default_from (void)
971 {
972   ADDRESS *adr;
973   const char *fqdn = mutt_fqdn (1);
974
975   /* 
976    * Note: We let $from override $realname here.  Is this the right
977    * thing to do? 
978    */
979
980   if (From)
981     adr = rfc822_cpy_adr_real (From);
982   else if (option (OPTUSEDOMAIN)) {
983     adr = rfc822_new_address ();
984     adr->mailbox =
985       mem_malloc (str_len (Username) + str_len (fqdn) + 2);
986     sprintf (adr->mailbox, "%s@%s", NONULL (Username), NONULL (fqdn));  /* __SPRINTF_CHECKED__ */
987   }
988   else {
989     adr = rfc822_new_address ();
990     adr->mailbox = str_dup (NONULL (Username));
991   }
992
993   return (adr);
994 }
995
996 static int send_message (HEADER * msg)
997 {
998   char tempfile[_POSIX_PATH_MAX];
999   FILE *tempfp;
1000   int i;
1001
1002   /* Write out the message in MIME form. */
1003   mutt_mktemp (tempfile);
1004   if ((tempfp = safe_fopen (tempfile, "w")) == NULL)
1005     return (-1);
1006
1007 #ifdef MIXMASTER
1008   mutt_write_rfc822_header (tempfp, msg->env, msg->content, 0,
1009                             msg->chain ? 1 : 0);
1010 #endif
1011 #ifndef MIXMASTER
1012   mutt_write_rfc822_header (tempfp, msg->env, msg->content, 0, 0);
1013 #endif
1014
1015   fputc ('\n', tempfp);         /* tie off the header. */
1016
1017   if ((mutt_write_mime_body (msg->content, tempfp) == -1)) {
1018     fclose (tempfp);
1019     unlink (tempfile);
1020     return (-1);
1021   }
1022
1023   if (fclose (tempfp) != 0) {
1024     mutt_perror (tempfile);
1025     unlink (tempfile);
1026     return (-1);
1027   }
1028
1029 #ifdef MIXMASTER
1030   if (msg->chain)
1031     return mix_send_message (msg->chain, tempfile);
1032 #endif
1033
1034   i = mutt_invoke_mta (msg->env->from, msg->env->to, msg->env->cc,
1035                        msg->env->bcc, tempfile,
1036                        (msg->content->encoding == ENC8BIT));
1037   return (i);
1038 }
1039
1040 /* rfc2047 encode the content-descriptions */
1041 static void encode_descriptions (BODY * b, short recurse)
1042 {
1043   BODY *t;
1044
1045   for (t = b; t; t = t->next) {
1046     if (t->description) {
1047       rfc2047_encode_string (&t->description);
1048     }
1049     if (recurse && t->parts)
1050       encode_descriptions (t->parts, recurse);
1051   }
1052 }
1053
1054 /* rfc2047 decode them in case of an error */
1055 static void decode_descriptions (BODY * b)
1056 {
1057   BODY *t;
1058
1059   for (t = b; t; t = t->next) {
1060     if (t->description) {
1061       rfc2047_decode (&t->description);
1062     }
1063     if (t->parts)
1064       decode_descriptions (t->parts);
1065   }
1066 }
1067
1068 int mutt_resend_message (FILE * fp, CONTEXT * ctx, HEADER * cur)
1069 {
1070   HEADER *msg = mutt_new_header ();
1071
1072   if (mutt_prepare_template (fp, ctx, msg, cur, 1) < 0)
1073     return -1;
1074
1075   return ci_send_message (SENDRESEND, msg, NULL, ctx, cur);
1076 }
1077
1078 int ci_send_message (int flags, /* send mode */
1079                      HEADER * msg,      /* template to use for new message */
1080                      char *tempfile,    /* file specified by -i or -H */
1081                      CONTEXT * ctx,     /* current mailbox */
1082                      HEADER * cur)
1083 {                               /* current message */
1084   char buffer[LONG_STRING];
1085   char fcc[_POSIX_PATH_MAX] = "";       /* where to copy this message */
1086   FILE *tempfp = NULL;
1087   BODY *pbody;
1088   int i, killfrom = 0;
1089   int fcc_error = 0;
1090   int free_clear_content = 0;
1091
1092   BODY *save_content = NULL;
1093   BODY *clear_content = NULL;
1094   char *pgpkeylist = NULL;
1095
1096   /* save current value of "pgp_sign_as" */
1097   char *signas = NULL;
1098   char *tag = NULL, *err = NULL;
1099   char *ctype;
1100
1101   int rv = -1;
1102
1103 #ifdef USE_NNTP
1104   if (flags & SENDNEWS)
1105     set_option (OPTNEWSSEND);
1106   else
1107     unset_option (OPTNEWSSEND);
1108 #endif
1109
1110   if (!flags && !msg && quadoption (OPT_RECALL) != M_NO &&
1111       mutt_num_postponed (1)) {
1112     /* If the user is composing a new message, check to see if there
1113      * are any postponed messages first.
1114      */
1115     if ((i =
1116          query_quadoption (OPT_RECALL, _("Recall postponed message?"))) == -1)
1117       return rv;
1118
1119     if (i == M_YES)
1120       flags |= SENDPOSTPONED;
1121   }
1122
1123
1124   if ((WithCrypto & APPLICATION_PGP) && (flags & SENDPOSTPONED))
1125     signas = str_dup (PgpSignAs);
1126
1127   /* Delay expansion of aliases until absolutely necessary--shouldn't
1128    * be necessary unless we are prompting the user or about to execute a
1129    * send-hook.
1130    */
1131
1132   if (!msg) {
1133     msg = mutt_new_header ();
1134
1135     if (flags == SENDPOSTPONED) {
1136       if ((flags =
1137            mutt_get_postponed (ctx, msg, &cur, fcc, sizeof (fcc))) < 0)
1138         goto cleanup;
1139 #ifdef USE_NNTP
1140       /*
1141        * If postponed message is a news article, it have
1142        * a "Newsgroups:" header line, then set appropriate flag.
1143        */
1144       if (msg->env->newsgroups) {
1145         flags |= SENDNEWS;
1146         set_option (OPTNEWSSEND);
1147       }
1148       else {
1149         flags &= ~SENDNEWS;
1150         unset_option (OPTNEWSSEND);
1151       }
1152 #endif
1153     }
1154
1155     if (flags & (SENDPOSTPONED | SENDRESEND)) {
1156       if ((tempfp = safe_fopen (msg->content->filename, "a+")) == NULL) {
1157         mutt_perror (msg->content->filename);
1158         goto cleanup;
1159       }
1160     }
1161
1162     if (!msg->env)
1163       msg->env = mutt_new_envelope ();
1164   }
1165
1166   /* Parse and use an eventual list-post header */
1167   if ((flags & SENDLISTREPLY)
1168       && cur && cur->env && cur->env->list_post) {
1169     /* Use any list-post header as a template */
1170     url_parse_mailto (msg->env, NULL, cur->env->list_post);
1171     /* We don't let them set the sender's address. */
1172     rfc822_free_address (&msg->env->from);
1173   }
1174
1175   if (!(flags & (SENDKEY | SENDPOSTPONED | SENDRESEND))) {
1176     pbody = mutt_new_body ();
1177     pbody->next = msg->content; /* don't kill command-line attachments */
1178     msg->content = pbody;
1179
1180     ctype = str_dup (ContentType);
1181     mutt_parse_content_type (ctype, msg->content);
1182     mem_free (&ctype);
1183
1184     msg->content->unlink = 1;
1185     msg->content->use_disp = 0;
1186     msg->content->disposition = DISPINLINE;
1187     if (option (OPTTEXTFLOWED) && msg->content->type == TYPETEXT
1188         && !ascii_strcasecmp (msg->content->subtype, "plain")) {
1189       mutt_set_parameter ("format", "flowed", &msg->content->parameter);
1190       if (option (OPTDELSP))
1191         mutt_set_parameter ("delsp", "yes", &msg->content->parameter);
1192     }
1193
1194     if (!tempfile) {
1195       mutt_mktemp (buffer);
1196       tempfp = safe_fopen (buffer, "w+");
1197       msg->content->filename = str_dup (buffer);
1198     }
1199     else {
1200       tempfp = safe_fopen (tempfile, "a+");
1201       msg->content->filename = str_dup (tempfile);
1202     }
1203
1204     if (!tempfp) {
1205       debug_print (1, ("can't create tempfile %s (errno=%d)\n", 
1206                   msg->content->filename, errno));
1207       mutt_perror (msg->content->filename);
1208       goto cleanup;
1209     }
1210   }
1211
1212   /* this is handled here so that the user can match ~f in send-hook */
1213   if (cur && option (OPTREVNAME) && !(flags & (SENDPOSTPONED | SENDRESEND))) {
1214     /* we shouldn't have to worry about freeing `msg->env->from' before
1215      * setting it here since this code will only execute when doing some
1216      * sort of reply.  the pointer will only be set when using the -H command
1217      * line option.
1218      *
1219      * We shouldn't have to worry about alias expansion here since we are
1220      * either replying to a real or postponed message, therefore no aliases
1221      * should exist since the user has not had the opportunity to add
1222      * addresses to the list.  We just have to ensure the postponed messages
1223      * have their aliases expanded.
1224      */
1225
1226     msg->env->from = set_reverse_name (cur->env);
1227   }
1228
1229   if (!msg->env->from && option (OPTUSEFROM)
1230       && !(flags & (SENDPOSTPONED | SENDRESEND)))
1231     msg->env->from = mutt_default_from ();
1232
1233   if (flags & SENDBATCH) {
1234     mutt_copy_stream (stdin, tempfp);
1235     if (option (OPTHDRS)) {
1236       process_user_recips (msg->env);
1237       process_user_header (msg->env);
1238     }
1239     mutt_expand_aliases_env (msg->env);
1240   }
1241   else if (!(flags & (SENDPOSTPONED | SENDRESEND))) {
1242     if ((flags & (SENDREPLY | SENDFORWARD)) && ctx &&
1243         envelope_defaults (msg->env, ctx, cur, flags) == -1)
1244       goto cleanup;
1245
1246     if (option (OPTHDRS))
1247       process_user_recips (msg->env);
1248
1249     /* Expand aliases and remove duplicates/crossrefs */
1250     mutt_fix_reply_recipients (msg->env);
1251
1252 #ifdef USE_NNTP
1253     if ((flags & SENDNEWS) && ctx && ctx->magic == M_NNTP
1254         && !msg->env->newsgroups)
1255       msg->env->newsgroups = str_dup (((NNTP_DATA *) ctx->data)->group);
1256 #endif
1257
1258     if (!(flags & SENDMAILX) &&
1259         !(option (OPTAUTOEDIT) && option (OPTEDITHDRS)) &&
1260         !((flags & SENDREPLY) && option (OPTFASTREPLY))) {
1261       if (edit_envelope (msg->env, flags) == -1)
1262         goto cleanup;
1263     }
1264
1265     /* the from address must be set here regardless of whether or not
1266      * $use_from is set so that the `~P' (from you) operator in send-hook
1267      * patterns will work.  if $use_from is unset, the from address is killed
1268      * after send-hooks are evaulated */
1269
1270     if (!msg->env->from) {
1271       msg->env->from = mutt_default_from ();
1272       killfrom = 1;
1273     }
1274
1275     if ((flags & SENDREPLY) && cur) {
1276       /* change setting based upon message we are replying to */
1277       mutt_message_hook (ctx, cur, M_REPLYHOOK);
1278
1279       /*
1280        * set the replied flag for the message we are generating so that the
1281        * user can use ~Q in a send-hook to know when reply-hook's are also
1282        * being used.
1283        */
1284       msg->replied = 1;
1285     }
1286
1287     /* change settings based upon recipients */
1288
1289     mutt_message_hook (NULL, msg, M_SENDHOOK);
1290
1291     /*
1292      * Unset the replied flag from the message we are composing since it is
1293      * no longer required.  This is done here because the FCC'd copy of
1294      * this message was erroneously get the 'R'eplied flag when stored in
1295      * a maildir-style mailbox.
1296      */
1297     msg->replied = 0;
1298
1299     if (killfrom) {
1300       rfc822_free_address (&msg->env->from);
1301       killfrom = 0;
1302     }
1303
1304     if (option (OPTHDRS))
1305       process_user_header (msg->env);
1306
1307
1308     if (option (OPTSIGONTOP)
1309         && (!(flags & (SENDMAILX | SENDKEY)) && Editor
1310             && str_cmp (Editor, "builtin") != 0))
1311       append_signature (tempfp);
1312
1313     /* include replies/forwarded messages, unless we are given a template */
1314     if (!tempfile && (ctx || !(flags & (SENDREPLY | SENDFORWARD)))
1315         && generate_body (tempfp, msg, flags, ctx, cur) == -1)
1316       goto cleanup;
1317
1318     if (!option (OPTSIGONTOP)
1319         && (!(flags & (SENDMAILX | SENDKEY)) && Editor
1320             && str_cmp (Editor, "builtin") != 0))
1321       append_signature (tempfp);
1322
1323     /* 
1324      * this wants to be done _after_ generate_body, so message-hooks
1325      * can take effect.
1326      */
1327
1328     if (WithCrypto && !(flags & SENDMAILX)) {
1329       if (option (OPTCRYPTAUTOSIGN))
1330         msg->security |= SIGN;
1331       if (option (OPTCRYPTAUTOENCRYPT))
1332         msg->security |= ENCRYPT;
1333       if (option (OPTCRYPTREPLYENCRYPT) && cur && (cur->security & ENCRYPT))
1334         msg->security |= ENCRYPT;
1335       if (option (OPTCRYPTREPLYSIGN) && cur && (cur->security & SIGN))
1336         msg->security |= SIGN;
1337       if (option (OPTCRYPTREPLYSIGNENCRYPTED) && cur
1338           && (cur->security & ENCRYPT))
1339         msg->security |= SIGN;
1340       if (WithCrypto & APPLICATION_PGP && (msg->security & (ENCRYPT | SIGN))) {
1341         if (option (OPTPGPAUTOINLINE))
1342           msg->security |= INLINE;
1343         if (option (OPTPGPREPLYINLINE) && cur && (cur->security & INLINE))
1344           msg->security |= INLINE;
1345       }
1346     }
1347
1348     if (WithCrypto && msg->security) {
1349       /* 
1350        * When reypling / forwarding, use the original message's
1351        * crypto system.  According to the documentation,
1352        * smime_is_default should be disregarded here.
1353        * 
1354        * Problem: At least with forwarding, this doesn't really
1355        * make much sense. Should we have an option to completely
1356        * disable individual mechanisms at run-time?
1357        */
1358       if (cur) {
1359         if ((WithCrypto & APPLICATION_PGP) && option (OPTCRYPTAUTOPGP)
1360             && (cur->security & APPLICATION_PGP))
1361           msg->security |= APPLICATION_PGP;
1362         else if ((WithCrypto & APPLICATION_SMIME)
1363                  && option (OPTCRYPTAUTOSMIME)
1364                  && (cur->security & APPLICATION_SMIME))
1365           msg->security |= APPLICATION_SMIME;
1366       }
1367
1368       /*
1369        * No crypto mechanism selected? Use availability + smime_is_default
1370        * for the decision. 
1371        */
1372       if (!(msg->security & (APPLICATION_SMIME | APPLICATION_PGP))) {
1373         if ((WithCrypto & APPLICATION_SMIME) && option (OPTCRYPTAUTOSMIME)
1374             && option (OPTSMIMEISDEFAULT))
1375           msg->security |= APPLICATION_SMIME;
1376         else if ((WithCrypto & APPLICATION_PGP) && option (OPTCRYPTAUTOPGP))
1377           msg->security |= APPLICATION_PGP;
1378         else if ((WithCrypto & APPLICATION_SMIME)
1379                  && option (OPTCRYPTAUTOSMIME))
1380           msg->security |= APPLICATION_SMIME;
1381       }
1382     }
1383
1384     /* No permissible mechanisms found.  Don't sign or encrypt. */
1385     if (!(msg->security & (APPLICATION_SMIME | APPLICATION_PGP)))
1386       msg->security = 0;
1387   }
1388
1389   /* 
1390    * This hook is even called for postponed messages, and can, e.g., be
1391    * used for setting the editor, the sendmail path, or the
1392    * envelope sender.
1393    */
1394   mutt_message_hook (NULL, msg, M_SEND2HOOK);
1395
1396   /* wait until now to set the real name portion of our return address so
1397      that $realname can be set in a send-hook */
1398   if (msg->env->from && !msg->env->from->personal
1399       && !(flags & (SENDRESEND | SENDPOSTPONED)))
1400     msg->env->from->personal = str_dup (Realname);
1401
1402   if (!((WithCrypto & APPLICATION_PGP) && (flags & SENDKEY)))
1403     safe_fclose (&tempfp);
1404
1405   if (flags & SENDMAILX) {
1406     if (mutt_builtin_editor (msg->content->filename, msg, cur) == -1)
1407       goto cleanup;
1408   }
1409   else if (!(flags & SENDBATCH)) {
1410     struct stat st;
1411     time_t mtime = mutt_decrease_mtime (msg->content->filename, NULL);
1412
1413     mutt_update_encoding (msg->content);
1414
1415     /*
1416      * Select whether or not the user's editor should be called now.  We
1417      * don't want to do this when:
1418      * 1) we are sending a key/cert
1419      * 2) we are forwarding a message and the user doesn't want to edit it.
1420      *    This is controled by the quadoption $forward_edit.  However, if
1421      *    both $edit_headers and $autoedit are set, we want to ignore the
1422      *    setting of $forward_edit because the user probably needs to add the
1423      *    recipients.
1424      */
1425     if (!(flags & SENDKEY) &&
1426         ((flags & SENDFORWARD) == 0 ||
1427          (option (OPTEDITHDRS) && option (OPTAUTOEDIT)) ||
1428          query_quadoption (OPT_FORWEDIT,
1429                            _("Edit forwarded message?")) == M_YES)) {
1430       /* If the this isn't a text message, look for a mailcap edit command */
1431       if (mutt_needs_mailcap (msg->content))
1432         mutt_edit_attachment (msg->content);
1433       else if (!Editor || str_cmp ("builtin", Editor) == 0)
1434         mutt_builtin_editor (msg->content->filename, msg, cur);
1435       else if (option (OPTEDITHDRS)) {
1436         mutt_env_to_local (msg->env);
1437         mutt_edit_headers (Editor, msg->content->filename, msg, fcc,
1438                            sizeof (fcc));
1439         mutt_env_to_idna (msg->env, NULL, NULL);
1440       }
1441       else
1442         mutt_edit_file (Editor, msg->content->filename);
1443
1444       if (option (OPTTEXTFLOWED))
1445         rfc3676_space_stuff (msg);
1446
1447       mutt_message_hook (NULL, msg, M_SEND2HOOK);
1448     }
1449
1450     if (!(flags & (SENDPOSTPONED | SENDFORWARD | SENDKEY | SENDRESEND))) {
1451       if (stat (msg->content->filename, &st) == 0) {
1452         /* if the file was not modified, bail out now */
1453         if (mtime == st.st_mtime && !msg->content->next &&
1454             query_quadoption (OPT_ABORT,
1455                               _("Abort unmodified message?")) == M_YES) {
1456           mutt_message _("Aborted unmodified message.");
1457
1458           goto cleanup;
1459         }
1460       }
1461       else
1462         mutt_perror (msg->content->filename);
1463     }
1464   }
1465
1466   /* specify a default fcc.  if we are in batchmode, only save a copy of
1467    * the message if the value of $copy is yes or ask-yes */
1468
1469   if (!fcc[0] && !(flags & (SENDPOSTPONED))
1470       && (!(flags & SENDBATCH) || (quadoption (OPT_COPY) & 0x1))) {
1471     /* set the default FCC */
1472     if (!msg->env->from) {
1473       msg->env->from = mutt_default_from ();
1474       killfrom = 1;             /* no need to check $use_from because if the user specified
1475                                    a from address it would have already been set by now */
1476     }
1477     mutt_select_fcc (fcc, sizeof (fcc), msg);
1478     if (killfrom) {
1479       rfc822_free_address (&msg->env->from);
1480       killfrom = 0;
1481     }
1482   }
1483
1484
1485   mutt_update_encoding (msg->content);
1486
1487   if (!(flags & (SENDMAILX | SENDBATCH))) {
1488   main_loop:
1489
1490     fcc_error = 0;              /* reset value since we may have failed before */
1491     mutt_pretty_mailbox (fcc);
1492     i = mutt_compose_menu (msg, fcc, sizeof (fcc), cur);
1493     if (i == -1) {
1494       /* abort */
1495 #ifdef USE_NNTP
1496       if (flags & SENDNEWS)
1497         mutt_message (_("Article not posted."));
1498
1499       else
1500 #endif
1501         mutt_message _("Mail not sent.");
1502       goto cleanup;
1503     }
1504     else if (i == 1) {
1505       /* postpone the message until later. */
1506       if (msg->content->next)
1507         msg->content = mutt_make_multipart (msg->content);
1508
1509       /*
1510        * make sure the message is written to the right part of a maildir 
1511        * postponed folder.
1512        */
1513       msg->read = 0;
1514       msg->old = 0;
1515
1516       encode_descriptions (msg->content, 1);
1517       mutt_prepare_envelope (msg->env, 0);
1518       mutt_env_to_idna (msg->env, NULL, NULL);  /* Handle bad IDNAs the next time. */
1519
1520       if (!Postponed
1521           || mutt_write_fcc (NONULL (Postponed), msg,
1522                              (cur
1523                               && (flags & SENDREPLY)) ? cur->env->
1524                              message_id : NULL, 1, fcc) < 0) {
1525         msg->content = mutt_remove_multipart (msg->content);
1526         decode_descriptions (msg->content);
1527         mutt_unprepare_envelope (msg->env);
1528         goto main_loop;
1529       }
1530       mutt_update_num_postponed ();
1531       mutt_message _("Message postponed.");
1532
1533       goto cleanup;
1534     }
1535   }
1536
1537 #ifdef USE_NNTP
1538   if (!(flags & SENDNEWS))
1539 #endif
1540     if (!msg->env->to && !msg->env->cc && !msg->env->bcc) {
1541       if (!(flags & SENDBATCH)) {
1542         mutt_error _("No recipients are specified!");
1543
1544         goto main_loop;
1545       }
1546       else {
1547         puts _("No recipients were specified.");
1548
1549         goto cleanup;
1550       }
1551     }
1552
1553   if (mutt_env_to_idna (msg->env, &tag, &err)) {
1554     mutt_error (_("Bad IDN in \"%s\": '%s'"), tag, err);
1555     mem_free (&err);
1556     if (!(flags & SENDBATCH))
1557       goto main_loop;
1558     else
1559       goto cleanup;
1560   }
1561
1562   if (!msg->env->subject && !(flags & SENDBATCH) &&
1563       (i =
1564        query_quadoption (OPT_SUBJECT,
1565                          _("No subject, abort sending?"))) != M_NO) {
1566     /* if the abort is automatic, print an error message */
1567     if (quadoption (OPT_SUBJECT) == M_YES)
1568       mutt_error _("No subject specified.");
1569
1570     goto main_loop;
1571   }
1572 #ifdef USE_NNTP
1573   if ((flags & SENDNEWS) && !msg->env->subject) {
1574     mutt_error _("No subject specified.");
1575
1576     goto main_loop;
1577   }
1578
1579   if ((flags & SENDNEWS) && !msg->env->newsgroups) {
1580     mutt_error _("No newsgroup specified.");
1581
1582     goto main_loop;
1583   }
1584 #endif
1585
1586   if (msg->content->next)
1587     msg->content = mutt_make_multipart (msg->content);
1588
1589   if (mutt_attach_check (msg) &&
1590       !msg->content->next &&
1591       query_quadoption (OPT_ATTACH,
1592                         _("No attachments made but indicator found in text. "
1593                           "Cancel sending?")) == M_YES) {
1594     if (quadoption (OPT_ATTACH) == M_YES) {
1595       mutt_message _("No attachments made but indicator found in text. "
1596                      "Abort sending.");
1597       sleep (2);
1598     }
1599     mutt_message (_("Mail not sent."));
1600     goto main_loop;
1601   }
1602
1603   /* 
1604    * Ok, we need to do it this way instead of handling all fcc stuff in
1605    * one place in order to avoid going to main_loop with encoded "env"
1606    * in case of error.  Ugh.
1607    */
1608
1609   encode_descriptions (msg->content, 1);
1610
1611   /*
1612    * Make sure that clear_content and free_clear_content are
1613    * properly initialized -- we may visit this particular place in
1614    * the code multiple times, including after a failed call to
1615    * mutt_protect().
1616    */
1617
1618   clear_content = NULL;
1619   free_clear_content = 0;
1620
1621   if (WithCrypto) {
1622     if (msg->security) {
1623       /* save the decrypted attachments */
1624       clear_content = msg->content;
1625
1626       if ((crypt_get_keys (msg, &pgpkeylist) == -1) ||
1627           mutt_protect (msg, pgpkeylist) == -1) {
1628         msg->content = mutt_remove_multipart (msg->content);
1629
1630         mem_free (&pgpkeylist);
1631
1632         decode_descriptions (msg->content);
1633         goto main_loop;
1634       }
1635       encode_descriptions (msg->content, 0);
1636     }
1637
1638     /* 
1639      * at this point, msg->content is one of the following three things:
1640      * - multipart/signed.  In this case, clear_content is a child.
1641      * - multipart/encrypted.  In this case, clear_content exists
1642      *   independently
1643      * - application/pgp.  In this case, clear_content exists independently.
1644      * - something else.  In this case, it's the same as clear_content.
1645      */
1646
1647     /* This is ugly -- lack of "reporting back" from mutt_protect(). */
1648
1649     if (clear_content && (msg->content != clear_content)
1650         && (msg->content->parts != clear_content))
1651       free_clear_content = 1;
1652   }
1653
1654   if (!option (OPTNOCURSES) && !(flags & SENDMAILX))
1655     mutt_message _("Sending message...");
1656
1657   mutt_prepare_envelope (msg->env, 1);
1658
1659   /* save a copy of the message, if necessary. */
1660
1661   mutt_expand_path (fcc, sizeof (fcc));
1662
1663
1664   /* Don't save a copy when we are in batch-mode, and the FCC
1665    * folder is on an IMAP server: This would involve possibly lots
1666    * of user interaction, which is not available in batch mode. 
1667    * 
1668    * Note: A patch to fix the problems with the use of IMAP servers
1669    * from non-curses mode is available from Brendan Cully.  However, 
1670    * I'd like to think a bit more about this before including it.
1671    */
1672
1673 #ifdef USE_IMAP
1674   if ((flags & SENDBATCH) && fcc[0] && mx_get_magic (fcc) == M_IMAP)
1675     fcc[0] = '\0';
1676 #endif
1677
1678   if (*fcc && str_cmp ("/dev/null", fcc) != 0) {
1679     BODY *tmpbody = msg->content;
1680     BODY *save_sig = NULL;
1681     BODY *save_parts = NULL;
1682
1683     if (WithCrypto && msg->security && option (OPTFCCCLEAR))
1684       msg->content = clear_content;
1685
1686     /* check to see if the user wants copies of all attachments */
1687     if (!option (OPTFCCATTACH) && msg->content->type == TYPEMULTIPART) {
1688       if (WithCrypto
1689           && (str_cmp (msg->content->subtype, "encrypted") == 0 ||
1690               str_cmp (msg->content->subtype, "signed") == 0)) {
1691         if (clear_content->type == TYPEMULTIPART) {
1692           if (!(msg->security & ENCRYPT) && (msg->security & SIGN)) {
1693             /* save initial signature and attachments */
1694             save_sig = msg->content->parts->next;
1695             save_parts = clear_content->parts->next;
1696           }
1697
1698           /* this means writing only the main part */
1699           msg->content = clear_content->parts;
1700
1701           if (mutt_protect (msg, pgpkeylist) == -1) {
1702             /* we can't do much about it at this point, so
1703              * fallback to saving the whole thing to fcc
1704              */
1705             msg->content = tmpbody;
1706             save_sig = NULL;
1707             goto full_fcc;
1708           }
1709
1710           save_content = msg->content;
1711         }
1712       }
1713       else
1714         msg->content = msg->content->parts;
1715     }
1716
1717   full_fcc:
1718     if (msg->content) {
1719       /* update received time so that when storing to a mbox-style folder
1720        * the From_ line contains the current time instead of when the
1721        * message was first postponed.
1722        */
1723       msg->received = time (NULL);
1724       if (mutt_write_fcc (fcc, msg, NULL, 0, NULL) == -1) {
1725         /*
1726          * Error writing FCC, we should abort sending.
1727          */
1728         fcc_error = 1;
1729       }
1730     }
1731
1732     msg->content = tmpbody;
1733
1734     if (WithCrypto && save_sig) {
1735       /* cleanup the second signature structures */
1736       if (save_content->parts) {
1737         mutt_free_body (&save_content->parts->next);
1738         save_content->parts = NULL;
1739       }
1740       mutt_free_body (&save_content);
1741
1742       /* restore old signature and attachments */
1743       msg->content->parts->next = save_sig;
1744       msg->content->parts->parts->next = save_parts;
1745     }
1746     else if (WithCrypto && save_content) {
1747       /* destroy the new encrypted body. */
1748       mutt_free_body (&save_content);
1749     }
1750
1751   }
1752
1753
1754   /*
1755    * Don't attempt to send the message if the FCC failed.  Just pretend
1756    * the send failed as well so we give the user a chance to fix the
1757    * error.
1758    */
1759   if (fcc_error || (i = send_message (msg)) == -1) {
1760     if (!(flags & SENDBATCH)) {
1761       if (!WithCrypto);
1762       else if ((msg->security & ENCRYPT) || ((msg->security & SIGN)
1763                                              && msg->content->type ==
1764                                              TYPEAPPLICATION)) {
1765         mutt_free_body (&msg->content); /* destroy PGP data */
1766         msg->content = clear_content;   /* restore clear text. */
1767       }
1768       else if ((msg->security & SIGN) && msg->content->type == TYPEMULTIPART) {
1769         mutt_free_body (&msg->content->parts->next);    /* destroy sig */
1770         msg->content = mutt_remove_multipart (msg->content);
1771       }
1772
1773       msg->content = mutt_remove_multipart (msg->content);
1774       decode_descriptions (msg->content);
1775       mutt_unprepare_envelope (msg->env);
1776       goto main_loop;
1777     }
1778     else {
1779       puts _("Could not send the message.");
1780
1781       goto cleanup;
1782     }
1783   }
1784   else if (!option (OPTNOCURSES) && !(flags & SENDMAILX))
1785     mutt_message (i != 0 ? _("Sending in background.") :
1786 #ifdef USE_NNTP
1787                   (flags & SENDNEWS) ? _("Article posted.") :
1788                   _("Mail sent."));
1789 #else
1790                   _("Mail sent."));
1791 #endif
1792
1793   if (WithCrypto && (msg->security & ENCRYPT))
1794     mem_free (&pgpkeylist);
1795
1796   if (WithCrypto && free_clear_content)
1797     mutt_free_body (&clear_content);
1798
1799   if (flags & SENDREPLY) {
1800     if (cur && ctx)
1801       mutt_set_flag (ctx, cur, M_REPLIED, 1);
1802     else if (!(flags & SENDPOSTPONED) && ctx && ctx->tagged) {
1803       for (i = 0; i < ctx->vcount; i++)
1804         if (ctx->hdrs[ctx->v2r[i]]->tagged)
1805           mutt_set_flag (ctx, ctx->hdrs[ctx->v2r[i]], M_REPLIED, 1);
1806     }
1807   }
1808
1809
1810   rv = 0;
1811
1812 cleanup:
1813
1814   if ((WithCrypto & APPLICATION_PGP) && (flags & SENDPOSTPONED)) {
1815     if (signas) {
1816       mem_free (&PgpSignAs);
1817       PgpSignAs = signas;
1818     }
1819   }
1820
1821   safe_fclose (&tempfp);
1822   mutt_free_header (&msg);
1823
1824   return rv;
1825 }
1826
1827 /* vim: set sw=2: */