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