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