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.
18 #include "recvattach.h"
19 #include "mutt_curses.h"
20 #include "mutt_menu.h"
27 #include "mutt_idna.h"
33 /* some helper functions to verify that we are exclusively operating
34 * on message/rfc822 attachments
37 static short check_msg (BODY * b, short err)
39 if (!mutt_is_message_type (b->type, b->subtype)) {
41 mutt_error _("You may only bounce message/rfc822 parts.");
48 static short check_all_msg (ATTACHPTR ** idx, short idxlen,
49 BODY * cur, short err)
53 if (cur && check_msg (cur, err) == -1)
56 for (i = 0; i < idxlen; i++) {
57 if (idx[i]->content->tagged) {
58 if (check_msg (idx[i]->content, err) == -1)
67 /* can we decode all tagged attachments? */
69 static short check_can_decode (ATTACHPTR ** idx, short idxlen, BODY * cur)
74 return mutt_can_decode (cur);
76 for (i = 0; i < idxlen; i++)
77 if (idx[i]->content->tagged && !mutt_can_decode (idx[i]->content))
83 static short count_tagged (ATTACHPTR ** idx, short idxlen)
88 for (i = 0; i < idxlen; i++)
89 if (idx[i]->content->tagged)
95 /* count the number of tagged children below a multipart or message
99 static short count_tagged_children (ATTACHPTR ** idx, short idxlen, short i)
101 short level = idx[i]->level;
104 while ((++i < idxlen) && (level < idx[i]->level))
105 if (idx[i]->content->tagged)
115 ** The bounce function, from the attachment menu
119 void mutt_attach_bounce (FILE * fp, HEADER * hdr,
120 ATTACHPTR ** idx, short idxlen, BODY * cur)
124 char buf[HUGE_STRING];
130 if (check_all_msg (idx, idxlen, cur, 1) == -1)
133 /* one or more messages? */
134 p = (cur || count_tagged (idx, idxlen) == 1);
137 strfcpy (prompt, _("Bounce message to: "), sizeof (prompt));
139 strfcpy (prompt, _("Bounce tagged messages to: "), sizeof (prompt));
142 if (mutt_get_field (prompt, buf, sizeof (buf), M_ALIAS)
146 if (!(adr = rfc822_parse_adrlist (adr, buf))) {
147 mutt_error _("Error parsing address!");
152 adr = mutt_expand_aliases (adr);
154 if (mutt_addrlist_to_idna (adr, &err) < 0) {
155 mutt_error (_("Bad IDN: '%s'"), err);
157 rfc822_free_address (&adr);
162 rfc822_write_address (buf, sizeof (buf), adr, 1);
164 #define extra_space (15+7+2)
168 snprintf (prompt, sizeof (prompt) - 4,
169 (p ? _("Bounce message to %s") : _("Bounce messages to %s")),
172 if (mutt_strwidth (prompt) > COLS - extra_space) {
173 mutt_format_string (prompt, sizeof (prompt) - 4,
174 0, COLS - extra_space, 0, 0,
175 prompt, sizeof (prompt), 0);
176 str_cat (prompt, sizeof (prompt), "...?");
179 str_cat (prompt, sizeof (prompt), "?");
181 if (query_quadoption (OPT_BOUNCE, prompt) != M_YES) {
182 rfc822_free_address (&adr);
183 CLEARLINE (LINES - 1);
184 mutt_message (p ? _("Message not bounced.") : _("Messages not bounced."));
188 CLEARLINE (LINES - 1);
191 ret = mutt_bounce_message (fp, cur->hdr, adr);
193 for (i = 0; i < idxlen; i++) {
194 if (idx[i]->content->tagged)
195 if (mutt_bounce_message (fp, idx[i]->content->hdr, adr))
201 mutt_message (p ? _("Message bounced.") : _("Messages bounced."));
203 mutt_error (p ? _("Error bouncing message!") :
204 _("Error bouncing messages!"));
211 ** resend-message, from the attachment menu
216 void mutt_attach_resend (FILE * fp, HEADER * hdr, ATTACHPTR ** idx,
217 short idxlen, BODY * cur)
221 if (check_all_msg (idx, idxlen, cur, 1) == -1)
225 mutt_resend_message (fp, Context, cur->hdr);
227 for (i = 0; i < idxlen; i++)
228 if (idx[i]->content->tagged)
229 mutt_resend_message (fp, Context, idx[i]->content->hdr);
236 ** forward-message, from the attachment menu
240 /* try to find a common parent message for the tagged attachments. */
242 static HEADER *find_common_parent (ATTACHPTR ** idx, short idxlen,
248 for (i = 0; i < idxlen; i++)
249 if (idx[i]->content->tagged)
253 if (mutt_is_message_type
254 (idx[i]->content->type, idx[i]->content->subtype)) {
255 nchildren = count_tagged_children (idx, idxlen, i);
256 if (nchildren == nattach)
257 return idx[i]->content->hdr;
265 * check whether attachment #i is a parent of the attachment
268 * Note: This and the calling procedure could be optimized quite a
269 * bit. For now, it's not worth the effort.
272 static int is_parent (short i, ATTACHPTR ** idx, short idxlen, BODY * cur)
274 short level = idx[i]->level;
276 while ((++i < idxlen) && idx[i]->level > level) {
277 if (idx[i]->content == cur)
284 static HEADER *find_parent (ATTACHPTR ** idx, short idxlen, BODY * cur,
288 HEADER *parent = NULL;
291 for (i = 0; i < idxlen; i++) {
292 if (mutt_is_message_type
293 (idx[i]->content->type, idx[i]->content->subtype)
294 && is_parent (i, idx, idxlen, cur))
295 parent = idx[i]->content->hdr;
296 if (idx[i]->content == cur)
301 parent = find_common_parent (idx, idxlen, nattach);
306 static void include_header (int quote, FILE * ifp,
307 HEADER * hdr, FILE * ofp, char *_prefix)
309 int chflags = CH_DECODE;
310 char prefix[SHORT_STRING];
312 if (option (OPTWEED))
313 chflags |= CH_WEED | CH_REORDER;
317 strfcpy (prefix, _prefix, sizeof (prefix));
318 else if (!option (OPTTEXTFLOWED))
319 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix),
322 strfcpy (prefix, ">", sizeof (prefix));
324 chflags |= CH_PREFIX;
327 mutt_copy_header (ifp, hdr, ofp, chflags, quote ? prefix : NULL);
330 /* Attach all the body parts which can't be decoded.
331 * This code is shared by forwarding and replying. */
333 static BODY **copy_problematic_attachments (FILE * fp,
336 short idxlen, short force)
340 for (i = 0; i < idxlen; i++) {
341 if (idx[i]->content->tagged &&
342 (force || !mutt_can_decode (idx[i]->content))) {
343 if (mutt_copy_body (fp, last, idx[i]->content) == -1)
344 return NULL; /* XXXXX - may lead to crashes */
345 last = &((*last)->next);
352 * forward one or several MIME bodies
353 * (non-message types)
356 static void attach_forward_bodies (FILE * fp, HEADER * hdr,
357 ATTACHPTR ** idx, short idxlen,
358 BODY * cur, short nattach, int flags)
361 short mime_fwd_all = 0;
362 short mime_fwd_any = 1;
363 HEADER *parent = NULL;
364 HEADER *tmphdr = NULL;
366 char tmpbody[_POSIX_PATH_MAX];
376 * First, find the parent message.
377 * Note: This could be made an option by just
378 * putting the following lines into an if block.
382 parent = find_parent (idx, idxlen, cur, nattach);
388 tmphdr = mutt_new_header ();
389 tmphdr->env = mutt_new_envelope ();
390 mutt_make_forward_subject (tmphdr->env, Context, parent);
392 mutt_mktemp (tmpbody);
393 if ((tmpfp = safe_fopen (tmpbody, "w")) == NULL) {
394 mutt_error (_("Can't open temporary file %s."), tmpbody);
398 mutt_forward_intro (tmpfp, parent);
400 /* prepare the prefix here since we'll need it later. */
402 if (option (OPTFORWQUOTE)) {
403 if (!option (OPTTEXTFLOWED))
404 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix), Context,
407 strfcpy (prefix, ">", sizeof (prefix));
410 include_header (option (OPTFORWQUOTE), fp, parent, tmpfp, prefix);
414 * Now, we have prepared the first part of the message body: The
415 * original message's header.
417 * The next part is more interesting: either include the message bodies,
421 if ((!cur || mutt_can_decode (cur)) &&
422 (rc = query_quadoption (OPT_MIMEFWD,
423 _("Forward as attachments?"))) == M_YES)
429 * shortcut MIMEFWDREST when there is only one attachment. Is
433 if (!mime_fwd_all && !cur && (nattach > 1)
434 && !check_can_decode (idx, idxlen, cur)) {
435 if ((rc = query_quadoption (OPT_MIMEFWDREST,
437 ("Can't decode all tagged attachments. MIME-forward the others?")))
444 /* initialize a state structure */
446 memset (&st, 0, sizeof (st));
448 if (option (OPTFORWQUOTE))
450 st.flags = M_CHARCONV;
451 if (option (OPTWEED))
456 /* where do we append new MIME parts? */
457 last = &tmphdr->content;
460 /* single body case */
462 if (!mime_fwd_all && mutt_can_decode (cur)) {
463 mutt_body_handler (cur, &st);
464 state_putc ('\n', &st);
467 if (mutt_copy_body (fp, last, cur) == -1)
469 last = &((*last)->next);
473 /* multiple body case */
476 for (i = 0; i < idxlen; i++) {
477 if (idx[i]->content->tagged && mutt_can_decode (idx[i]->content)) {
478 mutt_body_handler (idx[i]->content, &st);
479 state_putc ('\n', &st);
486 copy_problematic_attachments (fp, last, idx, idxlen,
487 mime_fwd_all)) == NULL)
491 mutt_forward_trailer (tmpfp);
496 /* now that we have the template, send it. */
497 ci_send_message (flags, tmphdr, tmpbody, NULL, parent);
504 mutt_unlink (tmpbody);
507 mutt_free_header (&tmphdr);
512 * Forward one or several message-type attachments. This
513 * is different from the previous function
514 * since we want to mimic the index menu's behaviour.
516 * Code reuse from ci_send_message is not possible here -
517 * ci_send_message relies on a context structure to find messages,
518 * while, on the attachment menu, messages are referenced through
519 * the attachment index.
522 static void attach_forward_msgs (FILE * fp, HEADER * hdr,
523 ATTACHPTR ** idx, short idxlen, BODY * cur,
526 HEADER *curhdr = NULL;
532 char tmpbody[_POSIX_PATH_MAX];
536 int chflags = CH_XMIT;
541 for (i = 0; i < idxlen; i++)
542 if (idx[i]->content->tagged) {
543 curhdr = idx[i]->content->hdr;
548 tmphdr = mutt_new_header ();
549 tmphdr->env = mutt_new_envelope ();
550 mutt_make_forward_subject (tmphdr->env, Context, curhdr);
555 if ((rc = query_quadoption (OPT_MIMEFWD,
556 _("Forward MIME encapsulated?"))) == M_NO) {
558 /* no MIME encapsulation */
560 mutt_mktemp (tmpbody);
561 if (!(tmpfp = safe_fopen (tmpbody, "w"))) {
562 mutt_error (_("Can't create %s."), tmpbody);
563 mutt_free_header (&tmphdr);
567 if (option (OPTFORWQUOTE)) {
568 chflags |= CH_PREFIX;
569 cmflags |= M_CM_PREFIX;
572 if (option (OPTFORWDECODE)) {
573 cmflags |= M_CM_DECODE | M_CM_CHARCONV;
574 if (option (OPTWEED)) {
575 chflags |= CH_WEED | CH_REORDER;
576 cmflags |= M_CM_WEED;
582 /* mutt_message_hook (cur->hdr, M_MESSAGEHOOK); */
583 mutt_forward_intro (tmpfp, cur->hdr);
584 _mutt_copy_message (tmpfp, fp, cur->hdr, cur->hdr->content, cmflags,
586 mutt_forward_trailer (tmpfp);
589 for (i = 0; i < idxlen; i++) {
590 if (idx[i]->content->tagged) {
591 /* mutt_message_hook (idx[i]->content->hdr, M_MESSAGEHOOK); */
592 mutt_forward_intro (tmpfp, idx[i]->content->hdr);
593 _mutt_copy_message (tmpfp, fp, idx[i]->content->hdr,
594 idx[i]->content->hdr->content, cmflags,
596 mutt_forward_trailer (tmpfp);
602 else if (rc == M_YES) { /* do MIME encapsulation - we don't need to do much here */
603 last = &tmphdr->content;
605 mutt_copy_body (fp, last, cur);
607 for (i = 0; i < idxlen; i++)
608 if (idx[i]->content->tagged) {
609 mutt_copy_body (fp, last, idx[i]->content);
610 last = &((*last)->next);
615 mutt_free_header (&tmphdr);
617 ci_send_message (flags, tmphdr, *tmpbody ? tmpbody : NULL, NULL, curhdr);
621 void mutt_attach_forward (FILE * fp, HEADER * hdr,
622 ATTACHPTR ** idx, short idxlen, BODY * cur,
628 if (check_all_msg (idx, idxlen, cur, 0) == 0)
629 attach_forward_msgs (fp, hdr, idx, idxlen, cur, flags);
631 nattach = count_tagged (idx, idxlen);
632 attach_forward_bodies (fp, hdr, idx, idxlen, cur, nattach, flags);
640 ** the various reply functions, from the attachment menu
645 /* Create the envelope defaults for a reply.
647 * This function can be invoked in two ways.
649 * Either, parent is NULL. In this case, all tagged bodies are of a message type,
650 * and the header information is fetched from them.
652 * Or, parent is non-NULL. In this case, cur is the common parent of all the
653 * tagged attachments.
655 * Note that this code is horribly similar to envelope_defaults () from send.c.
659 attach_reply_envelope_defaults (ENVELOPE * env, ATTACHPTR ** idx,
660 short idxlen, HEADER * parent, int flags)
662 ENVELOPE *curenv = NULL;
663 HEADER *curhdr = NULL;
667 for (i = 0; i < idxlen; i++) {
668 if (idx[i]->content->tagged) {
669 curhdr = idx[i]->content->hdr;
670 curenv = curhdr->env;
676 curenv = parent->env;
680 if (curenv == NULL || curhdr == NULL) {
681 mutt_error _("Can't find any tagged messages.");
687 if ((flags & SENDNEWS)) {
688 /* in case followup set Newsgroups: with Followup-To: if it present */
689 if (!env->newsgroups && curenv &&
690 str_casecmp (curenv->followup_to, "poster"))
691 env->newsgroups = str_dup (curenv->followup_to);
697 if (mutt_fetch_recips (env, curenv, flags) == -1)
701 for (i = 0; i < idxlen; i++) {
702 if (idx[i]->content->tagged
703 && mutt_fetch_recips (env, idx[i]->content->hdr->env,
709 if ((flags & SENDLISTREPLY) && !env->to) {
710 mutt_error _("No mailing lists found!");
715 mutt_fix_reply_recipients (env);
717 mutt_make_misc_reply_headers (env, Context, curhdr, curenv);
720 mutt_add_to_reference_headers (env, curenv, NULL, NULL);
722 LIST **p = NULL, **q = NULL;
724 for (i = 0; i < idxlen; i++) {
725 if (idx[i]->content->tagged)
726 mutt_add_to_reference_headers (env, idx[i]->content->hdr->env, &p,
735 /* This is _very_ similar to send.c's include_reply(). */
737 static void attach_include_reply (FILE * fp, FILE * tmpfp, HEADER * cur,
740 int cmflags = M_CM_PREFIX | M_CM_DECODE | M_CM_CHARCONV;
741 int chflags = CH_DECODE;
743 /* mutt_message_hook (cur, M_MESSAGEHOOK); */
745 mutt_make_attribution (Context, cur, tmpfp);
747 if (!option (OPTHEADER))
748 cmflags |= M_CM_NOHEADER;
749 if (option (OPTWEED)) {
751 cmflags |= M_CM_WEED;
754 _mutt_copy_message (tmpfp, fp, cur, cur->content, cmflags, chflags);
755 mutt_make_post_indent (Context, cur, tmpfp);
758 void mutt_attach_reply (FILE * fp, HEADER * hdr,
759 ATTACHPTR ** idx, short idxlen, BODY * cur, int flags)
761 short mime_reply_any = 0;
764 HEADER *parent = NULL;
765 HEADER *tmphdr = NULL;
769 char tmpbody[_POSIX_PATH_MAX];
772 char prefix[SHORT_STRING];
776 if (flags & SENDNEWS)
777 set_option (OPTNEWSSEND);
779 unset_option (OPTNEWSSEND);
782 if (check_all_msg (idx, idxlen, cur, 0) == -1) {
783 nattach = count_tagged (idx, idxlen);
784 if ((parent = find_parent (idx, idxlen, cur, nattach)) == NULL)
788 if (nattach > 1 && !check_can_decode (idx, idxlen, cur)) {
789 if ((rc = query_quadoption (OPT_MIMEFWDREST,
791 ("Can't decode all tagged attachments. MIME-encapsulate the others?")))
794 else if (rc == M_YES)
797 else if (nattach == 1)
800 tmphdr = mutt_new_header ();
801 tmphdr->env = mutt_new_envelope ();
803 if (attach_reply_envelope_defaults (tmphdr->env, idx, idxlen,
804 parent ? parent : (cur ? cur->
807 mutt_free_header (&tmphdr);
811 mutt_mktemp (tmpbody);
812 if ((tmpfp = safe_fopen (tmpbody, "w")) == NULL) {
813 mutt_error (_("Can't create %s."), tmpbody);
814 mutt_free_header (&tmphdr);
820 attach_include_reply (fp, tmpfp, cur->hdr, flags);
822 for (i = 0; i < idxlen; i++) {
823 if (idx[i]->content->tagged)
824 attach_include_reply (fp, tmpfp, idx[i]->content->hdr, flags);
829 mutt_make_attribution (Context, parent, tmpfp);
831 memset (&st, 0, sizeof (STATE));
835 if (!option (OPTTEXTFLOWED))
836 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix),
839 strfcpy (prefix, ">", sizeof (prefix));
842 st.flags = M_CHARCONV;
844 if (option (OPTWEED))
847 if (option (OPTHEADER))
848 include_header (1, fp, parent, tmpfp, prefix);
851 if (mutt_can_decode (cur)) {
852 mutt_body_handler (cur, &st);
853 state_putc ('\n', &st);
856 mutt_copy_body (fp, &tmphdr->content, cur);
859 for (i = 0; i < idxlen; i++) {
860 if (idx[i]->content->tagged && mutt_can_decode (idx[i]->content)) {
861 mutt_body_handler (idx[i]->content, &st);
862 state_putc ('\n', &st);
867 mutt_make_post_indent (Context, parent, tmpfp);
869 if (mime_reply_any && !cur &&
870 copy_problematic_attachments (fp, &tmphdr->content, idx, idxlen,
872 mutt_free_header (&tmphdr);
880 if (ci_send_message (flags, tmphdr, tmpbody, NULL, parent) == 0)
881 mutt_set_flag (Context, hdr, M_REPLIED, 1);