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