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