2 * Copyright notice from original mutt:
3 * Copyright (C) 1999-2000 Thomas Roessler <roessler@does-not-exist.org>
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.
14 #include <lib-lib/mem.h>
15 #include <lib-lib/str.h>
16 #include <lib-lib/macros.h>
17 #include <lib-lib/file.h>
23 #include "recvattach.h"
24 #include "mutt_curses.h"
25 #include "mutt_menu.h"
32 #include "mutt_idna.h"
35 /* some helper functions to verify that we are exclusively operating
36 * on message/rfc822 attachments
39 static short check_msg (BODY * b, short err)
41 if (!mutt_is_message_type (b->type, b->subtype)) {
43 mutt_error _("You may only bounce message/rfc822 parts.");
50 static short check_all_msg (ATTACHPTR ** idx, short idxlen,
51 BODY * cur, short err)
55 if (cur && check_msg (cur, err) == -1)
58 for (i = 0; i < idxlen; i++) {
59 if (idx[i]->content->tagged) {
60 if (check_msg (idx[i]->content, err) == -1)
69 /* can we decode all tagged attachments? */
71 static short check_can_decode (ATTACHPTR ** idx, short idxlen, BODY * cur)
76 return mutt_can_decode (cur);
78 for (i = 0; i < idxlen; i++)
79 if (idx[i]->content->tagged && !mutt_can_decode (idx[i]->content))
85 static short count_tagged (ATTACHPTR ** idx, short idxlen)
90 for (i = 0; i < idxlen; i++)
91 if (idx[i]->content->tagged)
97 /* count the number of tagged children below a multipart or message
101 static short count_tagged_children (ATTACHPTR ** idx, short idxlen, short i)
103 short level = idx[i]->level;
106 while ((++i < idxlen) && (level < idx[i]->level))
107 if (idx[i]->content->tagged)
117 ** The bounce function, from the attachment menu
121 void mutt_attach_bounce (FILE * fp, HEADER * hdr,
122 ATTACHPTR ** idx, short idxlen, BODY * cur)
126 char buf[HUGE_STRING];
132 if (check_all_msg (idx, idxlen, cur, 1) == -1)
135 /* one or more messages? */
136 p = (cur || count_tagged (idx, idxlen) == 1);
139 strfcpy (prompt, _("Bounce message to: "), sizeof (prompt));
141 strfcpy (prompt, _("Bounce tagged messages to: "), sizeof (prompt));
144 if (mutt_get_field (prompt, buf, sizeof (buf), M_ALIAS)
148 if (!(adr = rfc822_parse_adrlist (adr, buf))) {
149 mutt_error _("Error parsing address!");
154 adr = mutt_expand_aliases (adr);
156 if (mutt_addrlist_to_idna (adr, &err) < 0) {
157 mutt_error (_("Bad IDN: '%s'"), err);
159 rfc822_free_address (&adr);
164 rfc822_write_address (buf, sizeof (buf), adr, 1);
166 #define extra_space (15+7+2)
170 snprintf (prompt, sizeof (prompt) - 4,
171 (p ? _("Bounce message to %s") : _("Bounce messages to %s")),
174 if (mutt_strwidth (prompt) > COLS - extra_space) {
175 mutt_format_string (prompt, sizeof (prompt) - 4,
176 0, COLS - extra_space, 0, 0,
177 prompt, sizeof (prompt), 0);
178 m_strcat(prompt, sizeof(prompt), "...?");
180 m_strcat(prompt, sizeof(prompt), "?");
183 if (query_quadoption (OPT_BOUNCE, prompt) != M_YES) {
184 rfc822_free_address (&adr);
185 CLEARLINE (LINES - 1);
186 mutt_message (p ? _("Message not bounced.") : _("Messages not bounced."));
190 CLEARLINE (LINES - 1);
193 ret = mutt_bounce_message (fp, cur->hdr, adr);
195 for (i = 0; i < idxlen; i++) {
196 if (idx[i]->content->tagged)
197 if (mutt_bounce_message (fp, idx[i]->content->hdr, adr))
203 mutt_message (p ? _("Message bounced.") : _("Messages bounced."));
205 mutt_error (p ? _("Error bouncing message!") :
206 _("Error bouncing messages!"));
213 ** resend-message, from the attachment menu
218 void mutt_attach_resend (FILE * fp, HEADER * hdr, ATTACHPTR ** idx,
219 short idxlen, BODY * cur)
223 if (check_all_msg (idx, idxlen, cur, 1) == -1)
227 mutt_resend_message (fp, Context, cur->hdr);
229 for (i = 0; i < idxlen; i++)
230 if (idx[i]->content->tagged)
231 mutt_resend_message (fp, Context, idx[i]->content->hdr);
238 ** forward-message, from the attachment menu
242 /* try to find a common parent message for the tagged attachments. */
244 static HEADER *find_common_parent (ATTACHPTR ** idx, short idxlen,
250 for (i = 0; i < idxlen; i++)
251 if (idx[i]->content->tagged)
255 if (mutt_is_message_type
256 (idx[i]->content->type, idx[i]->content->subtype)) {
257 nchildren = count_tagged_children (idx, idxlen, i);
258 if (nchildren == nattach)
259 return idx[i]->content->hdr;
267 * check whether attachment #i is a parent of the attachment
270 * Note: This and the calling procedure could be optimized quite a
271 * bit. For now, it's not worth the effort.
274 static int is_parent (short i, ATTACHPTR ** idx, short idxlen, BODY * cur)
276 short level = idx[i]->level;
278 while ((++i < idxlen) && idx[i]->level > level) {
279 if (idx[i]->content == cur)
286 static HEADER *find_parent (ATTACHPTR ** idx, short idxlen, BODY * cur,
290 HEADER *parent = NULL;
293 for (i = 0; i < idxlen; i++) {
294 if (mutt_is_message_type
295 (idx[i]->content->type, idx[i]->content->subtype)
296 && is_parent (i, idx, idxlen, cur))
297 parent = idx[i]->content->hdr;
298 if (idx[i]->content == cur)
303 parent = find_common_parent (idx, idxlen, nattach);
308 static void include_header (int quote, FILE * ifp,
309 HEADER * hdr, FILE * ofp, char *_prefix)
311 int chflags = CH_DECODE;
312 char prefix[SHORT_STRING];
314 if (option (OPTWEED))
315 chflags |= CH_WEED | CH_REORDER;
319 strfcpy (prefix, _prefix, sizeof (prefix));
320 else if (!option (OPTTEXTFLOWED))
321 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix),
324 strfcpy (prefix, ">", sizeof (prefix));
326 chflags |= CH_PREFIX;
329 mutt_copy_header (ifp, hdr, ofp, chflags, quote ? prefix : NULL);
332 /* Attach all the body parts which can't be decoded.
333 * This code is shared by forwarding and replying. */
335 static BODY **copy_problematic_attachments (FILE * fp,
338 short idxlen, short force)
342 for (i = 0; i < idxlen; i++) {
343 if (idx[i]->content->tagged &&
344 (force || !mutt_can_decode (idx[i]->content))) {
345 if (mutt_copy_body (fp, last, idx[i]->content) == -1)
346 return NULL; /* XXXXX - may lead to crashes */
347 last = &((*last)->next);
354 * forward one or several MIME bodies
355 * (non-message types)
358 static void attach_forward_bodies (FILE * fp, HEADER * hdr,
359 ATTACHPTR ** idx, short idxlen,
360 BODY * cur, short nattach, int flags)
363 short mime_fwd_all = 0;
364 short mime_fwd_any = 1;
365 HEADER *parent = NULL;
366 HEADER *tmphdr = NULL;
368 char tmpbody[_POSIX_PATH_MAX];
378 * First, find the parent message.
379 * Note: This could be made an option by just
380 * putting the following lines into an if block.
384 parent = find_parent (idx, idxlen, cur, nattach);
390 tmphdr = mutt_new_header ();
391 tmphdr->env = mutt_new_envelope ();
392 mutt_make_forward_subject (tmphdr->env, Context, parent);
394 mutt_mktemp (tmpbody);
395 if ((tmpfp = safe_fopen (tmpbody, "w")) == NULL) {
396 mutt_error (_("Can't open temporary file %s."), tmpbody);
400 mutt_forward_intro (tmpfp, parent);
402 /* prepare the prefix here since we'll need it later. */
404 if (option (OPTFORWQUOTE)) {
405 if (!option (OPTTEXTFLOWED))
406 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix), Context,
409 strfcpy (prefix, ">", sizeof (prefix));
412 include_header (option (OPTFORWQUOTE), fp, parent, tmpfp, prefix);
416 * Now, we have prepared the first part of the message body: The
417 * original message's header.
419 * The next part is more interesting: either include the message bodies,
423 if ((!cur || mutt_can_decode (cur)) &&
424 (rc = query_quadoption (OPT_MIMEFWD,
425 _("Forward as attachments?"))) == M_YES)
431 * shortcut MIMEFWDREST when there is only one attachment. Is
435 if (!mime_fwd_all && !cur && (nattach > 1)
436 && !check_can_decode (idx, idxlen, cur)) {
437 if ((rc = query_quadoption (OPT_MIMEFWDREST,
439 ("Can't decode all tagged attachments. MIME-forward the others?")))
446 /* initialize a state structure */
450 if (option (OPTFORWQUOTE))
452 st.flags = M_CHARCONV;
453 if (option (OPTWEED))
458 /* where do we append new MIME parts? */
459 last = &tmphdr->content;
462 /* single body case */
464 if (!mime_fwd_all && mutt_can_decode (cur)) {
465 mutt_body_handler (cur, &st);
466 state_putc ('\n', &st);
469 if (mutt_copy_body (fp, last, cur) == -1)
471 last = &((*last)->next);
475 /* multiple body case */
478 for (i = 0; i < idxlen; i++) {
479 if (idx[i]->content->tagged && mutt_can_decode (idx[i]->content)) {
480 mutt_body_handler (idx[i]->content, &st);
481 state_putc ('\n', &st);
488 copy_problematic_attachments (fp, last, idx, idxlen,
489 mime_fwd_all)) == NULL)
493 mutt_forward_trailer (tmpfp);
498 /* now that we have the template, send it. */
499 ci_send_message (flags, tmphdr, tmpbody, NULL, parent);
506 mutt_unlink (tmpbody);
509 mutt_free_header (&tmphdr);
514 * Forward one or several message-type attachments. This
515 * is different from the previous function
516 * since we want to mimic the index menu's behaviour.
518 * Code reuse from ci_send_message is not possible here -
519 * ci_send_message relies on a context structure to find messages,
520 * while, on the attachment menu, messages are referenced through
521 * the attachment index.
524 static void attach_forward_msgs (FILE * fp, HEADER * hdr,
525 ATTACHPTR ** idx, short idxlen, BODY * cur,
528 HEADER *curhdr = NULL;
534 char tmpbody[_POSIX_PATH_MAX];
538 int chflags = CH_XMIT;
543 for (i = 0; i < idxlen; i++)
544 if (idx[i]->content->tagged) {
545 curhdr = idx[i]->content->hdr;
550 tmphdr = mutt_new_header ();
551 tmphdr->env = mutt_new_envelope ();
552 mutt_make_forward_subject (tmphdr->env, Context, curhdr);
557 if ((rc = query_quadoption (OPT_MIMEFWD,
558 _("Forward MIME encapsulated?"))) == M_NO) {
560 /* no MIME encapsulation */
562 mutt_mktemp (tmpbody);
563 if (!(tmpfp = safe_fopen (tmpbody, "w"))) {
564 mutt_error (_("Can't create %s."), tmpbody);
565 mutt_free_header (&tmphdr);
569 if (option (OPTFORWQUOTE)) {
570 chflags |= CH_PREFIX;
571 cmflags |= M_CM_PREFIX;
574 if (option (OPTFORWDECODE)) {
575 cmflags |= M_CM_DECODE | M_CM_CHARCONV;
576 if (option (OPTWEED)) {
577 chflags |= CH_WEED | CH_REORDER;
578 cmflags |= M_CM_WEED;
584 /* mutt_message_hook (cur->hdr, M_MESSAGEHOOK); */
585 mutt_forward_intro (tmpfp, cur->hdr);
586 _mutt_copy_message (tmpfp, fp, cur->hdr, cur->hdr->content, cmflags,
588 mutt_forward_trailer (tmpfp);
591 for (i = 0; i < idxlen; i++) {
592 if (idx[i]->content->tagged) {
593 /* mutt_message_hook (idx[i]->content->hdr, M_MESSAGEHOOK); */
594 mutt_forward_intro (tmpfp, idx[i]->content->hdr);
595 _mutt_copy_message (tmpfp, fp, idx[i]->content->hdr,
596 idx[i]->content->hdr->content, cmflags,
598 mutt_forward_trailer (tmpfp);
604 else if (rc == M_YES) { /* do MIME encapsulation - we don't need to do much here */
605 last = &tmphdr->content;
607 mutt_copy_body (fp, last, cur);
609 for (i = 0; i < idxlen; i++)
610 if (idx[i]->content->tagged) {
611 mutt_copy_body (fp, last, idx[i]->content);
612 last = &((*last)->next);
617 mutt_free_header (&tmphdr);
619 ci_send_message (flags, tmphdr, *tmpbody ? tmpbody : NULL, NULL, curhdr);
623 void mutt_attach_forward (FILE * fp, HEADER * hdr,
624 ATTACHPTR ** idx, short idxlen, BODY * cur,
630 if (check_all_msg (idx, idxlen, cur, 0) == 0)
631 attach_forward_msgs (fp, hdr, idx, idxlen, cur, flags);
633 nattach = count_tagged (idx, idxlen);
634 attach_forward_bodies (fp, hdr, idx, idxlen, cur, nattach, flags);
642 ** the various reply functions, from the attachment menu
647 /* Create the envelope defaults for a reply.
649 * This function can be invoked in two ways.
651 * Either, parent is NULL. In this case, all tagged bodies are of a message type,
652 * and the header information is fetched from them.
654 * Or, parent is non-NULL. In this case, cur is the common parent of all the
655 * tagged attachments.
657 * Note that this code is horribly similar to envelope_defaults () from send.c.
661 attach_reply_envelope_defaults (ENVELOPE * env, ATTACHPTR ** idx,
662 short idxlen, HEADER * parent, int flags)
664 ENVELOPE *curenv = NULL;
665 HEADER *curhdr = NULL;
669 for (i = 0; i < idxlen; i++) {
670 if (idx[i]->content->tagged) {
671 curhdr = idx[i]->content->hdr;
672 curenv = curhdr->env;
678 curenv = parent->env;
682 if (curenv == NULL || curhdr == NULL) {
683 mutt_error _("Can't find any tagged messages.");
689 if ((flags & SENDNEWS)) {
690 /* in case followup set Newsgroups: with Followup-To: if it present */
691 if (!env->newsgroups && curenv &&
692 m_strcasecmp(curenv->followup_to, "poster"))
693 env->newsgroups = m_strdup(curenv->followup_to);
699 if (mutt_fetch_recips (env, curenv, flags) == -1)
703 for (i = 0; i < idxlen; i++) {
704 if (idx[i]->content->tagged
705 && mutt_fetch_recips (env, idx[i]->content->hdr->env,
711 if ((flags & SENDLISTREPLY) && !env->to) {
712 mutt_error _("No mailing lists found!");
717 mutt_fix_reply_recipients (env);
719 mutt_make_misc_reply_headers (env, Context, curhdr, curenv);
722 mutt_add_to_reference_headers (env, curenv, NULL, NULL);
724 LIST **p = NULL, **q = NULL;
726 for (i = 0; i < idxlen; i++) {
727 if (idx[i]->content->tagged)
728 mutt_add_to_reference_headers (env, idx[i]->content->hdr->env, &p,
737 /* This is _very_ similar to send.c's include_reply(). */
739 static void attach_include_reply (FILE * fp, FILE * tmpfp, HEADER * cur,
742 int cmflags = M_CM_PREFIX | M_CM_DECODE | M_CM_CHARCONV;
743 int chflags = CH_DECODE;
745 /* mutt_message_hook (cur, M_MESSAGEHOOK); */
747 mutt_make_attribution (Context, cur, tmpfp);
749 if (!option (OPTHEADER))
750 cmflags |= M_CM_NOHEADER;
751 if (option (OPTWEED)) {
753 cmflags |= M_CM_WEED;
756 _mutt_copy_message (tmpfp, fp, cur, cur->content, cmflags, chflags);
757 mutt_make_post_indent (Context, cur, tmpfp);
760 void mutt_attach_reply (FILE * fp, HEADER * hdr,
761 ATTACHPTR ** idx, short idxlen, BODY * cur, int flags)
763 short mime_reply_any = 0;
766 HEADER *parent = NULL;
767 HEADER *tmphdr = NULL;
771 char tmpbody[_POSIX_PATH_MAX];
774 char prefix[SHORT_STRING];
778 if (flags & SENDNEWS)
779 set_option (OPTNEWSSEND);
781 unset_option (OPTNEWSSEND);
784 if (check_all_msg (idx, idxlen, cur, 0) == -1) {
785 nattach = count_tagged (idx, idxlen);
786 if ((parent = find_parent (idx, idxlen, cur, nattach)) == NULL)
790 if (nattach > 1 && !check_can_decode (idx, idxlen, cur)) {
791 if ((rc = query_quadoption (OPT_MIMEFWDREST,
793 ("Can't decode all tagged attachments. MIME-encapsulate the others?")))
796 else if (rc == M_YES)
799 else if (nattach == 1)
802 tmphdr = mutt_new_header ();
803 tmphdr->env = mutt_new_envelope ();
805 if (attach_reply_envelope_defaults (tmphdr->env, idx, idxlen,
806 parent ? parent : (cur ? cur->
809 mutt_free_header (&tmphdr);
813 mutt_mktemp (tmpbody);
814 if ((tmpfp = safe_fopen (tmpbody, "w")) == NULL) {
815 mutt_error (_("Can't create %s."), tmpbody);
816 mutt_free_header (&tmphdr);
822 attach_include_reply (fp, tmpfp, cur->hdr, flags);
824 for (i = 0; i < idxlen; i++) {
825 if (idx[i]->content->tagged)
826 attach_include_reply (fp, tmpfp, idx[i]->content->hdr, flags);
831 mutt_make_attribution (Context, parent, tmpfp);
837 if (!option (OPTTEXTFLOWED))
838 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix),
841 strfcpy (prefix, ">", sizeof (prefix));
844 st.flags = M_CHARCONV;
846 if (option (OPTWEED))
849 if (option (OPTHEADER))
850 include_header (1, fp, parent, tmpfp, prefix);
853 if (mutt_can_decode (cur)) {
854 mutt_body_handler (cur, &st);
855 state_putc ('\n', &st);
858 mutt_copy_body (fp, &tmphdr->content, cur);
861 for (i = 0; i < idxlen; i++) {
862 if (idx[i]->content->tagged && mutt_can_decode (idx[i]->content)) {
863 mutt_body_handler (idx[i]->content, &st);
864 state_putc ('\n', &st);
869 mutt_make_post_indent (Context, parent, tmpfp);
871 if (mime_reply_any && !cur &&
872 copy_problematic_attachments (fp, &tmphdr->content, idx, idxlen,
874 mutt_free_header (&tmphdr);
882 if (ci_send_message (flags, tmphdr, tmpbody, NULL, parent) == 0)
883 mutt_set_flag (Context, hdr, M_REPLIED, 1);