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