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