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