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