2 * Copyright (C) 1999-2004 Thomas Roessler <roessler@does-not-exist.org>
4 * This program is free software; you can redistribute it
5 * and/or modify it under the terms of the GNU General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later
10 * This program is distributed in the hope that it will be
11 * useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
13 * PURPOSE. See the GNU General Public License for more
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, write to the Free
18 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
27 #include "mutt_curses.h"
28 #include "mutt_menu.h"
32 #include "mutt_idna.h"
34 /* some helper functions to verify that we are exclusively operating
35 * on message/rfc822 attachments
38 static short check_msg (BODY * b, short err)
40 if (!mutt_is_message_type (b->type, b->subtype))
43 mutt_error _("You may only bounce message/rfc822 parts.");
49 static short check_all_msg (ATTACHPTR ** idx, short idxlen,
50 BODY * cur, short err)
54 if (cur && check_msg (cur, err) == -1)
58 for (i = 0; i < idxlen; i++)
60 if (idx[i]->content->tagged)
62 if (check_msg (idx[i]->content, err) == -1)
71 /* can we decode all tagged attachments? */
73 static short check_can_decode (ATTACHPTR ** idx, short idxlen,
79 return mutt_can_decode (cur);
81 for (i = 0; i < idxlen; i++)
82 if (idx[i]->content->tagged && !mutt_can_decode (idx[i]->content))
88 static short count_tagged (ATTACHPTR **idx, short idxlen)
93 for (i = 0; i < idxlen; i++)
94 if (idx[i]->content->tagged)
100 /* count the number of tagged children below a multipart or message
104 static short count_tagged_children (ATTACHPTR ** idx,
105 short idxlen, short i)
107 short level = idx[i]->level;
110 while ((++i < idxlen) && (level < idx[i]->level))
111 if (idx[i]->content->tagged)
121 ** The bounce function, from the attachment menu
125 void mutt_attach_bounce (FILE * fp, HEADER * hdr,
126 ATTACHPTR ** idx, short idxlen, BODY * cur)
130 char buf[HUGE_STRING];
136 if (check_all_msg (idx, idxlen, cur, 1) == -1)
139 /* one or more messages? */
140 p = (cur || count_tagged (idx, idxlen) == 1);
142 /* RfC 5322 mandates a From: header, so warn before bouncing
143 * messages without one */
146 if (!cur->hdr->env->from)
148 mutt_error _("Warning: message contains no From: header");
155 for (i = 0; i < idxlen; i++)
157 if (idx[i]->content->tagged)
159 if (!idx[i]->content->hdr->env->from)
161 mutt_error _("Warning: message contains no From: header");
171 strfcpy (prompt, _("Bounce message to: "), sizeof (prompt));
173 strfcpy (prompt, _("Bounce tagged messages to: "), sizeof (prompt));
176 if (mutt_get_field (prompt, buf, sizeof (buf), M_ALIAS)
180 if (!(adr = rfc822_parse_adrlist (adr, buf)))
182 mutt_error _("Error parsing address!");
186 adr = mutt_expand_aliases (adr);
188 if (mutt_addrlist_to_idna (adr, &err) < 0)
190 mutt_error (_("Bad IDN: '%s'"), err);
192 rfc822_free_address (&adr);
197 rfc822_write_address (buf, sizeof (buf), adr, 1);
199 #define extra_space (15+7+2)
203 snprintf (prompt, sizeof (prompt) - 4,
204 (p ? _("Bounce message to %s") : _("Bounce messages to %s")), buf);
206 if (mutt_strwidth (prompt) > COLS - extra_space)
208 mutt_format_string (prompt, sizeof (prompt) - 4,
209 0, COLS-extra_space, FMT_LEFT, 0,
210 prompt, sizeof (prompt), 0);
211 safe_strcat (prompt, sizeof (prompt), "...?");
214 safe_strcat (prompt, sizeof (prompt), "?");
216 if (query_quadoption (OPT_BOUNCE, prompt) != M_YES)
218 rfc822_free_address (&adr);
219 CLEARLINE (LINES - 1);
220 mutt_message (p ? _("Message not bounced.") : _("Messages not bounced."));
224 CLEARLINE (LINES - 1);
227 ret = mutt_bounce_message (fp, cur->hdr, adr);
230 for (i = 0; i < idxlen; i++)
232 if (idx[i]->content->tagged)
233 if (mutt_bounce_message (fp, idx[i]->content->hdr, adr))
239 mutt_message (p ? _("Message bounced.") : _("Messages bounced."));
241 mutt_error (p ? _("Error bouncing message!") : _("Error bouncing messages!"));
248 ** resend-message, from the attachment menu
253 void mutt_attach_resend (FILE * fp, HEADER * hdr, ATTACHPTR ** idx,
254 short idxlen, BODY * cur)
258 if (check_all_msg (idx, idxlen, cur, 1) == -1)
262 mutt_resend_message (fp, Context, cur->hdr);
265 for (i = 0; i < idxlen; i++)
266 if (idx[i]->content->tagged)
267 mutt_resend_message (fp, Context, idx[i]->content->hdr);
274 ** forward-message, from the attachment menu
278 /* try to find a common parent message for the tagged attachments. */
280 static HEADER *find_common_parent (ATTACHPTR ** idx, short idxlen,
286 for (i = 0; i < idxlen; i++)
287 if (idx[i]->content->tagged)
292 if (mutt_is_message_type (idx[i]->content->type, idx[i]->content->subtype))
294 nchildren = count_tagged_children (idx, idxlen, i);
295 if (nchildren == nattach)
296 return idx[i]->content->hdr;
304 * check whether attachment #i is a parent of the attachment
307 * Note: This and the calling procedure could be optimized quite a
308 * bit. For now, it's not worth the effort.
311 static int is_parent (short i, ATTACHPTR **idx, short idxlen, BODY *cur)
313 short level = idx[i]->level;
315 while ((++i < idxlen) && idx[i]->level > level)
317 if (idx[i]->content == cur)
324 static HEADER *find_parent (ATTACHPTR **idx, short idxlen, BODY *cur, short nattach)
327 HEADER *parent = NULL;
331 for (i = 0; i < idxlen; i++)
333 if (mutt_is_message_type (idx[i]->content->type, idx[i]->content->subtype)
334 && is_parent (i, idx, idxlen, cur))
335 parent = idx[i]->content->hdr;
336 if (idx[i]->content == cur)
341 parent = find_common_parent (idx, idxlen, nattach);
346 static void include_header (int quote, FILE * ifp,
347 HEADER * hdr, FILE * ofp,
350 int chflags = CH_DECODE;
351 char prefix[SHORT_STRING];
353 if (option (OPTWEED))
354 chflags |= CH_WEED | CH_REORDER;
359 strfcpy (prefix, _prefix, sizeof (prefix));
360 else if (!option (OPTTEXTFLOWED))
361 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix),
364 strfcpy (prefix, ">", sizeof (prefix));
366 chflags |= CH_PREFIX;
369 mutt_copy_header (ifp, hdr, ofp, chflags, quote ? prefix : NULL);
372 /* Attach all the body parts which can't be decoded.
373 * This code is shared by forwarding and replying. */
375 static BODY ** copy_problematic_attachments (FILE *fp,
383 for (i = 0; i < idxlen; i++)
385 if (idx[i]->content->tagged &&
386 (force || !mutt_can_decode (idx[i]->content)))
388 if (mutt_copy_body (fp, last, idx[i]->content) == -1)
389 return NULL; /* XXXXX - may lead to crashes */
390 last = &((*last)->next);
397 * forward one or several MIME bodies
398 * (non-message types)
401 static void attach_forward_bodies (FILE * fp, HEADER * hdr,
402 ATTACHPTR ** idx, short idxlen,
407 short mime_fwd_all = 0;
408 short mime_fwd_any = 1;
409 HEADER *parent = NULL;
410 HEADER *tmphdr = NULL;
412 char tmpbody[_POSIX_PATH_MAX];
422 * First, find the parent message.
423 * Note: This could be made an option by just
424 * putting the following lines into an if block.
428 parent = find_parent (idx, idxlen, cur, nattach);
434 tmphdr = mutt_new_header ();
435 tmphdr->env = mutt_new_envelope ();
436 mutt_make_forward_subject (tmphdr->env, Context, parent);
438 mutt_mktemp (tmpbody);
439 if ((tmpfp = safe_fopen (tmpbody, "w")) == NULL)
441 mutt_error (_("Can't open temporary file %s."), tmpbody);
445 mutt_forward_intro (tmpfp, parent);
447 /* prepare the prefix here since we'll need it later. */
449 if (option (OPTFORWQUOTE))
451 if (!option (OPTTEXTFLOWED))
452 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix), Context,
455 strfcpy (prefix, ">", sizeof (prefix));
458 include_header (option (OPTFORWQUOTE), fp, parent,
463 * Now, we have prepared the first part of the message body: The
464 * original message's header.
466 * The next part is more interesting: either include the message bodies,
470 if ((!cur || mutt_can_decode (cur)) &&
471 (rc = query_quadoption (OPT_MIMEFWD,
472 _("Forward as attachments?"))) == M_YES)
478 * shortcut MIMEFWDREST when there is only one attachment. Is
482 if (!mime_fwd_all && !cur && (nattach > 1)
483 && !check_can_decode (idx, idxlen, cur))
485 if ((rc = query_quadoption (OPT_MIMEFWDREST,
486 _("Can't decode all tagged attachments. MIME-forward the others?"))) == -1)
492 /* initialize a state structure */
494 memset (&st, 0, sizeof (st));
496 if (option (OPTFORWQUOTE))
498 st.flags = M_CHARCONV;
499 if (option (OPTWEED))
504 /* where do we append new MIME parts? */
505 last = &tmphdr->content;
509 /* single body case */
511 if (!mime_fwd_all && mutt_can_decode (cur))
513 mutt_body_handler (cur, &st);
514 state_putc ('\n', &st);
518 if (mutt_copy_body (fp, last, cur) == -1)
520 last = &((*last)->next);
525 /* multiple body case */
529 for (i = 0; i < idxlen; i++)
531 if (idx[i]->content->tagged && mutt_can_decode (idx[i]->content))
533 mutt_body_handler (idx[i]->content, &st);
534 state_putc ('\n', &st);
540 copy_problematic_attachments (fp, last, idx, idxlen, mime_fwd_all) == NULL)
544 mutt_forward_trailer (tmpfp);
546 safe_fclose (&tmpfp);
549 /* now that we have the template, send it. */
550 ci_send_message (0, tmphdr, tmpbody, NULL, parent);
557 safe_fclose (&tmpfp);
558 mutt_unlink (tmpbody);
561 mutt_free_header (&tmphdr);
566 * Forward one or several message-type attachments. This
567 * is different from the previous function
568 * since we want to mimic the index menu's behaviour.
570 * Code reuse from ci_send_message is not possible here -
571 * ci_send_message relies on a context structure to find messages,
572 * while, on the attachment menu, messages are referenced through
573 * the attachment index.
576 static void attach_forward_msgs (FILE * fp, HEADER * hdr,
577 ATTACHPTR ** idx, short idxlen, BODY * cur)
579 HEADER *curhdr = NULL;
585 char tmpbody[_POSIX_PATH_MAX];
589 int chflags = CH_XMIT;
595 for (i = 0; i < idxlen; i++)
596 if (idx[i]->content->tagged)
598 curhdr = idx[i]->content->hdr;
603 tmphdr = mutt_new_header ();
604 tmphdr->env = mutt_new_envelope ();
605 mutt_make_forward_subject (tmphdr->env, Context, curhdr);
610 if ((rc = query_quadoption (OPT_MIMEFWD,
611 _("Forward MIME encapsulated?"))) == M_NO)
614 /* no MIME encapsulation */
616 mutt_mktemp (tmpbody);
617 if (!(tmpfp = safe_fopen (tmpbody, "w")))
619 mutt_error (_("Can't create %s."), tmpbody);
620 mutt_free_header (&tmphdr);
624 if (option (OPTFORWQUOTE))
626 chflags |= CH_PREFIX;
627 cmflags |= M_CM_PREFIX;
630 if (option (OPTFORWDECODE))
632 cmflags |= M_CM_DECODE | M_CM_CHARCONV;
633 if (option (OPTWEED))
635 chflags |= CH_WEED | CH_REORDER;
636 cmflags |= M_CM_WEED;
643 /* mutt_message_hook (cur->hdr, M_MESSAGEHOOK); */
644 mutt_forward_intro (tmpfp, cur->hdr);
645 _mutt_copy_message (tmpfp, fp, cur->hdr, cur->hdr->content, cmflags, chflags);
646 mutt_forward_trailer (tmpfp);
650 for (i = 0; i < idxlen; i++)
652 if (idx[i]->content->tagged)
654 /* mutt_message_hook (idx[i]->content->hdr, M_MESSAGEHOOK); */
655 mutt_forward_intro (tmpfp, idx[i]->content->hdr);
656 _mutt_copy_message (tmpfp, fp, idx[i]->content->hdr,
657 idx[i]->content->hdr->content, cmflags, chflags);
658 mutt_forward_trailer (tmpfp);
662 safe_fclose (&tmpfp);
664 else if (rc == M_YES) /* do MIME encapsulation - we don't need to do much here */
666 last = &tmphdr->content;
668 mutt_copy_body (fp, last, cur);
671 for (i = 0; i < idxlen; i++)
672 if (idx[i]->content->tagged)
674 mutt_copy_body (fp, last, idx[i]->content);
675 last = &((*last)->next);
680 mutt_free_header (&tmphdr);
682 ci_send_message (0, tmphdr, *tmpbody ? tmpbody : NULL,
687 void mutt_attach_forward (FILE * fp, HEADER * hdr,
688 ATTACHPTR ** idx, short idxlen, BODY * cur)
693 if (check_all_msg (idx, idxlen, cur, 0) == 0)
694 attach_forward_msgs (fp, hdr, idx, idxlen, cur);
697 nattach = count_tagged (idx, idxlen);
698 attach_forward_bodies (fp, hdr, idx, idxlen, cur, nattach);
706 ** the various reply functions, from the attachment menu
711 /* Create the envelope defaults for a reply.
713 * This function can be invoked in two ways.
715 * Either, parent is NULL. In this case, all tagged bodies are of a message type,
716 * and the header information is fetched from them.
718 * Or, parent is non-NULL. In this case, cur is the common parent of all the
719 * tagged attachments.
721 * Note that this code is horribly similar to envelope_defaults () from send.c.
725 attach_reply_envelope_defaults (ENVELOPE *env, ATTACHPTR **idx, short idxlen,
726 HEADER *parent, int flags)
728 ENVELOPE *curenv = NULL;
729 HEADER *curhdr = NULL;
734 for (i = 0; i < idxlen; i++)
736 if (idx[i]->content->tagged)
738 curhdr = idx[i]->content->hdr;
739 curenv = curhdr->env;
746 curenv = parent->env;
750 if (curenv == NULL || curhdr == NULL)
752 mutt_error _("Can't find any tagged messages.");
758 if (mutt_fetch_recips (env, curenv, flags) == -1)
763 for (i = 0; i < idxlen; i++)
765 if (idx[i]->content->tagged
766 && mutt_fetch_recips (env, idx[i]->content->hdr->env, flags) == -1)
771 if ((flags & SENDLISTREPLY) && !env->to)
773 mutt_error _("No mailing lists found!");
777 mutt_fix_reply_recipients (env);
778 mutt_make_misc_reply_headers (env, Context, curhdr, curenv);
781 mutt_add_to_reference_headers (env, curenv, NULL, NULL);
784 LIST **p = NULL, **q = NULL;
786 for (i = 0; i < idxlen; i++)
788 if (idx[i]->content->tagged)
789 mutt_add_to_reference_headers (env, idx[i]->content->hdr->env, &p, &q);
797 /* This is _very_ similar to send.c's include_reply(). */
799 static void attach_include_reply (FILE *fp, FILE *tmpfp, HEADER *cur, int flags)
801 int cmflags = M_CM_PREFIX | M_CM_DECODE | M_CM_CHARCONV;
802 int chflags = CH_DECODE;
804 /* mutt_message_hook (cur, M_MESSAGEHOOK); */
806 mutt_make_attribution (Context, cur, tmpfp);
808 if (!option (OPTHEADER))
809 cmflags |= M_CM_NOHEADER;
810 if (option (OPTWEED))
813 cmflags |= M_CM_WEED;
816 _mutt_copy_message (tmpfp, fp, cur, cur->content, cmflags, chflags);
817 mutt_make_post_indent (Context, cur, tmpfp);
820 void mutt_attach_reply (FILE * fp, HEADER * hdr,
821 ATTACHPTR ** idx, short idxlen, BODY * cur,
824 short mime_reply_any = 0;
827 HEADER *parent = NULL;
828 HEADER *tmphdr = NULL;
832 char tmpbody[_POSIX_PATH_MAX];
835 char prefix[SHORT_STRING];
838 if (check_all_msg (idx, idxlen, cur, 0) == -1)
840 nattach = count_tagged (idx, idxlen);
841 if ((parent = find_parent (idx, idxlen, cur, nattach)) == NULL)
845 if (nattach > 1 && !check_can_decode (idx, idxlen, cur))
847 if ((rc = query_quadoption (OPT_MIMEFWDREST,
848 _("Can't decode all tagged attachments. MIME-encapsulate the others?"))) == -1)
850 else if (rc == M_YES)
853 else if (nattach == 1)
856 tmphdr = mutt_new_header ();
857 tmphdr->env = mutt_new_envelope ();
859 if (attach_reply_envelope_defaults (tmphdr->env, idx, idxlen,
860 parent ? parent : (cur ? cur->hdr : NULL), flags) == -1)
862 mutt_free_header (&tmphdr);
866 mutt_mktemp (tmpbody);
867 if ((tmpfp = safe_fopen (tmpbody, "w")) == NULL)
869 mutt_error (_("Can't create %s."), tmpbody);
870 mutt_free_header (&tmphdr);
877 attach_include_reply (fp, tmpfp, cur->hdr, flags);
880 for (i = 0; i < idxlen; i++)
882 if (idx[i]->content->tagged)
883 attach_include_reply (fp, tmpfp, idx[i]->content->hdr, flags);
889 mutt_make_attribution (Context, parent, tmpfp);
891 memset (&st, 0, sizeof (STATE));
895 if (!option (OPTTEXTFLOWED))
896 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix),
899 strfcpy (prefix, ">", sizeof (prefix));
902 st.flags = M_CHARCONV;
904 if (option (OPTWEED))
907 if (option (OPTHEADER))
908 include_header (1, fp, parent, tmpfp, prefix);
912 if (mutt_can_decode (cur))
914 mutt_body_handler (cur, &st);
915 state_putc ('\n', &st);
918 mutt_copy_body (fp, &tmphdr->content, cur);
922 for (i = 0; i < idxlen; i++)
924 if (idx[i]->content->tagged && mutt_can_decode (idx[i]->content))
926 mutt_body_handler (idx[i]->content, &st);
927 state_putc ('\n', &st);
932 mutt_make_post_indent (Context, parent, tmpfp);
934 if (mime_reply_any && !cur &&
935 copy_problematic_attachments (fp, &tmphdr->content, idx, idxlen, 0) == NULL)
937 mutt_free_header (&tmphdr);
938 safe_fclose (&tmpfp);
943 safe_fclose (&tmpfp);
945 if (ci_send_message (flags, tmphdr, tmpbody, NULL, parent) == 0)
946 mutt_set_flag (Context, hdr, M_REPLIED, 1);