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