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.
15 #include "mutt_curses.h"
16 #include "mutt_menu.h"
23 #include "mutt_idna.h"
29 /* some helper functions to verify that we are exclusively operating
30 * on message/rfc822 attachments
33 static short check_msg (BODY * b, short err)
35 if (!mutt_is_message_type (b->type, b->subtype)) {
37 mutt_error _("You may only bounce message/rfc822 parts.");
44 static short check_all_msg (ATTACHPTR ** idx, short idxlen,
45 BODY * cur, short err)
49 if (cur && check_msg (cur, err) == -1)
52 for (i = 0; i < idxlen; i++) {
53 if (idx[i]->content->tagged) {
54 if (check_msg (idx[i]->content, err) == -1)
63 /* can we decode all tagged attachments? */
65 static short check_can_decode (ATTACHPTR ** idx, short idxlen, BODY * cur)
70 return mutt_can_decode (cur);
72 for (i = 0; i < idxlen; i++)
73 if (idx[i]->content->tagged && !mutt_can_decode (idx[i]->content))
79 static short count_tagged (ATTACHPTR ** idx, short idxlen)
84 for (i = 0; i < idxlen; i++)
85 if (idx[i]->content->tagged)
91 /* count the number of tagged children below a multipart or message
95 static short count_tagged_children (ATTACHPTR ** idx, short idxlen, short i)
97 short level = idx[i]->level;
100 while ((++i < idxlen) && (level < idx[i]->level))
101 if (idx[i]->content->tagged)
111 ** The bounce function, from the attachment menu
115 void mutt_attach_bounce (FILE * fp, HEADER * hdr,
116 ATTACHPTR ** idx, short idxlen, BODY * cur)
120 char buf[HUGE_STRING];
126 if (check_all_msg (idx, idxlen, cur, 1) == -1)
129 /* one or more messages? */
130 p = (cur || count_tagged (idx, idxlen) == 1);
133 strfcpy (prompt, _("Bounce message to: "), sizeof (prompt));
135 strfcpy (prompt, _("Bounce tagged messages to: "), sizeof (prompt));
138 if (mutt_get_field (prompt, buf, sizeof (buf), M_ALIAS)
142 if (!(adr = rfc822_parse_adrlist (adr, buf))) {
143 mutt_error _("Error parsing address!");
148 adr = mutt_expand_aliases (adr);
150 if (mutt_addrlist_to_idna (adr, &err) < 0) {
151 mutt_error (_("Bad IDN: '%s'"), err);
153 rfc822_free_address (&adr);
158 rfc822_write_address (buf, sizeof (buf), adr, 1);
160 #define extra_space (15+7+2)
164 snprintf (prompt, sizeof (prompt) - 4,
165 (p ? _("Bounce message to %s") : _("Bounce messages to %s")),
168 if (mutt_strwidth (prompt) > COLS - extra_space) {
169 mutt_format_string (prompt, sizeof (prompt) - 4,
170 0, COLS - extra_space, 0, 0,
171 prompt, sizeof (prompt), 0);
172 safe_strcat (prompt, sizeof (prompt), "...?");
175 safe_strcat (prompt, sizeof (prompt), "?");
177 if (query_quadoption (OPT_BOUNCE, prompt) != M_YES) {
178 rfc822_free_address (&adr);
179 CLEARLINE (LINES - 1);
180 mutt_message (p ? _("Message not bounced.") : _("Messages not bounced."));
184 CLEARLINE (LINES - 1);
187 ret = mutt_bounce_message (fp, cur->hdr, adr);
189 for (i = 0; i < idxlen; i++) {
190 if (idx[i]->content->tagged)
191 if (mutt_bounce_message (fp, idx[i]->content->hdr, adr))
197 mutt_message (p ? _("Message bounced.") : _("Messages bounced."));
199 mutt_error (p ? _("Error bouncing message!") :
200 _("Error bouncing messages!"));
207 ** resend-message, from the attachment menu
212 void mutt_attach_resend (FILE * fp, HEADER * hdr, ATTACHPTR ** idx,
213 short idxlen, BODY * cur)
217 if (check_all_msg (idx, idxlen, cur, 1) == -1)
221 mutt_resend_message (fp, Context, cur->hdr);
223 for (i = 0; i < idxlen; i++)
224 if (idx[i]->content->tagged)
225 mutt_resend_message (fp, Context, idx[i]->content->hdr);
232 ** forward-message, from the attachment menu
236 /* try to find a common parent message for the tagged attachments. */
238 static HEADER *find_common_parent (ATTACHPTR ** idx, short idxlen,
244 for (i = 0; i < idxlen; i++)
245 if (idx[i]->content->tagged)
249 if (mutt_is_message_type
250 (idx[i]->content->type, idx[i]->content->subtype)) {
251 nchildren = count_tagged_children (idx, idxlen, i);
252 if (nchildren == nattach)
253 return idx[i]->content->hdr;
261 * check whether attachment #i is a parent of the attachment
264 * Note: This and the calling procedure could be optimized quite a
265 * bit. For now, it's not worth the effort.
268 static int is_parent (short i, ATTACHPTR ** idx, short idxlen, BODY * cur)
270 short level = idx[i]->level;
272 while ((++i < idxlen) && idx[i]->level > level) {
273 if (idx[i]->content == cur)
280 static HEADER *find_parent (ATTACHPTR ** idx, short idxlen, BODY * cur,
284 HEADER *parent = NULL;
287 for (i = 0; i < idxlen; i++) {
288 if (mutt_is_message_type
289 (idx[i]->content->type, idx[i]->content->subtype)
290 && is_parent (i, idx, idxlen, cur))
291 parent = idx[i]->content->hdr;
292 if (idx[i]->content == cur)
297 parent = find_common_parent (idx, idxlen, nattach);
302 static void include_header (int quote, FILE * ifp,
303 HEADER * hdr, FILE * ofp, char *_prefix)
305 int chflags = CH_DECODE;
306 char prefix[SHORT_STRING];
308 if (option (OPTWEED))
309 chflags |= CH_WEED | CH_REORDER;
313 strfcpy (prefix, _prefix, sizeof (prefix));
314 else if (!option (OPTTEXTFLOWED))
315 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix),
318 strfcpy (prefix, ">", sizeof (prefix));
320 chflags |= CH_PREFIX;
323 mutt_copy_header (ifp, hdr, ofp, chflags, quote ? prefix : NULL);
326 /* Attach all the body parts which can't be decoded.
327 * This code is shared by forwarding and replying. */
329 static BODY **copy_problematic_attachments (FILE * fp,
332 short idxlen, short force)
336 for (i = 0; i < idxlen; i++) {
337 if (idx[i]->content->tagged &&
338 (force || !mutt_can_decode (idx[i]->content))) {
339 if (mutt_copy_body (fp, last, idx[i]->content) == -1)
340 return NULL; /* XXXXX - may lead to crashes */
341 last = &((*last)->next);
348 * forward one or several MIME bodies
349 * (non-message types)
352 static void attach_forward_bodies (FILE * fp, HEADER * hdr,
353 ATTACHPTR ** idx, short idxlen,
354 BODY * cur, short nattach, int flags)
357 short mime_fwd_all = 0;
358 short mime_fwd_any = 1;
359 HEADER *parent = NULL;
360 HEADER *tmphdr = NULL;
362 char tmpbody[_POSIX_PATH_MAX];
372 * First, find the parent message.
373 * Note: This could be made an option by just
374 * putting the following lines into an if block.
378 parent = find_parent (idx, idxlen, cur, nattach);
384 tmphdr = mutt_new_header ();
385 tmphdr->env = mutt_new_envelope ();
386 mutt_make_forward_subject (tmphdr->env, Context, parent);
388 mutt_mktemp (tmpbody);
389 if ((tmpfp = safe_fopen (tmpbody, "w")) == NULL) {
390 mutt_error (_("Can't open temporary file %s."), tmpbody);
394 mutt_forward_intro (tmpfp, parent);
396 /* prepare the prefix here since we'll need it later. */
398 if (option (OPTFORWQUOTE)) {
399 if (!option (OPTTEXTFLOWED))
400 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix), Context,
403 strfcpy (prefix, ">", sizeof (prefix));
406 include_header (option (OPTFORWQUOTE), fp, parent, tmpfp, prefix);
410 * Now, we have prepared the first part of the message body: The
411 * original message's header.
413 * The next part is more interesting: either include the message bodies,
417 if ((!cur || mutt_can_decode (cur)) &&
418 (rc = query_quadoption (OPT_MIMEFWD,
419 _("Forward as attachments?"))) == M_YES)
425 * shortcut MIMEFWDREST when there is only one attachment. Is
429 if (!mime_fwd_all && !cur && (nattach > 1)
430 && !check_can_decode (idx, idxlen, cur)) {
431 if ((rc = query_quadoption (OPT_MIMEFWDREST,
433 ("Can't decode all tagged attachments. MIME-forward the others?")))
440 /* initialize a state structure */
442 memset (&st, 0, sizeof (st));
444 if (option (OPTFORWQUOTE))
446 st.flags = M_CHARCONV;
447 if (option (OPTWEED))
452 /* where do we append new MIME parts? */
453 last = &tmphdr->content;
456 /* single body case */
458 if (!mime_fwd_all && mutt_can_decode (cur)) {
459 mutt_body_handler (cur, &st);
460 state_putc ('\n', &st);
463 if (mutt_copy_body (fp, last, cur) == -1)
465 last = &((*last)->next);
469 /* multiple body case */
472 for (i = 0; i < idxlen; i++) {
473 if (idx[i]->content->tagged && mutt_can_decode (idx[i]->content)) {
474 mutt_body_handler (idx[i]->content, &st);
475 state_putc ('\n', &st);
482 copy_problematic_attachments (fp, last, idx, idxlen,
483 mime_fwd_all)) == NULL)
487 mutt_forward_trailer (tmpfp);
492 /* now that we have the template, send it. */
493 ci_send_message (flags, tmphdr, tmpbody, NULL, parent);
500 mutt_unlink (tmpbody);
503 mutt_free_header (&tmphdr);
508 * Forward one or several message-type attachments. This
509 * is different from the previous function
510 * since we want to mimic the index menu's behaviour.
512 * Code reuse from ci_send_message is not possible here -
513 * ci_send_message relies on a context structure to find messages,
514 * while, on the attachment menu, messages are referenced through
515 * the attachment index.
518 static void attach_forward_msgs (FILE * fp, HEADER * hdr,
519 ATTACHPTR ** idx, short idxlen, BODY * cur,
522 HEADER *curhdr = NULL;
528 char tmpbody[_POSIX_PATH_MAX];
532 int chflags = CH_XMIT;
537 for (i = 0; i < idxlen; i++)
538 if (idx[i]->content->tagged) {
539 curhdr = idx[i]->content->hdr;
544 tmphdr = mutt_new_header ();
545 tmphdr->env = mutt_new_envelope ();
546 mutt_make_forward_subject (tmphdr->env, Context, curhdr);
551 if ((rc = query_quadoption (OPT_MIMEFWD,
552 _("Forward MIME encapsulated?"))) == M_NO) {
554 /* no MIME encapsulation */
556 mutt_mktemp (tmpbody);
557 if (!(tmpfp = safe_fopen (tmpbody, "w"))) {
558 mutt_error (_("Can't create %s."), tmpbody);
559 mutt_free_header (&tmphdr);
563 if (option (OPTFORWQUOTE)) {
564 chflags |= CH_PREFIX;
565 cmflags |= M_CM_PREFIX;
568 if (option (OPTFORWDECODE)) {
569 cmflags |= M_CM_DECODE | M_CM_CHARCONV;
570 if (option (OPTWEED)) {
571 chflags |= CH_WEED | CH_REORDER;
572 cmflags |= M_CM_WEED;
578 /* mutt_message_hook (cur->hdr, M_MESSAGEHOOK); */
579 mutt_forward_intro (tmpfp, cur->hdr);
580 _mutt_copy_message (tmpfp, fp, cur->hdr, cur->hdr->content, cmflags,
582 mutt_forward_trailer (tmpfp);
585 for (i = 0; i < idxlen; i++) {
586 if (idx[i]->content->tagged) {
587 /* mutt_message_hook (idx[i]->content->hdr, M_MESSAGEHOOK); */
588 mutt_forward_intro (tmpfp, idx[i]->content->hdr);
589 _mutt_copy_message (tmpfp, fp, idx[i]->content->hdr,
590 idx[i]->content->hdr->content, cmflags,
592 mutt_forward_trailer (tmpfp);
598 else if (rc == M_YES) { /* do MIME encapsulation - we don't need to do much here */
599 last = &tmphdr->content;
601 mutt_copy_body (fp, last, cur);
603 for (i = 0; i < idxlen; i++)
604 if (idx[i]->content->tagged) {
605 mutt_copy_body (fp, last, idx[i]->content);
606 last = &((*last)->next);
611 mutt_free_header (&tmphdr);
613 ci_send_message (flags, tmphdr, *tmpbody ? tmpbody : NULL, NULL, curhdr);
617 void mutt_attach_forward (FILE * fp, HEADER * hdr,
618 ATTACHPTR ** idx, short idxlen, BODY * cur,
624 if (check_all_msg (idx, idxlen, cur, 0) == 0)
625 attach_forward_msgs (fp, hdr, idx, idxlen, cur, flags);
627 nattach = count_tagged (idx, idxlen);
628 attach_forward_bodies (fp, hdr, idx, idxlen, cur, nattach, flags);
636 ** the various reply functions, from the attachment menu
641 /* Create the envelope defaults for a reply.
643 * This function can be invoked in two ways.
645 * Either, parent is NULL. In this case, all tagged bodies are of a message type,
646 * and the header information is fetched from them.
648 * Or, parent is non-NULL. In this case, cur is the common parent of all the
649 * tagged attachments.
651 * Note that this code is horribly similar to envelope_defaults () from send.c.
655 attach_reply_envelope_defaults (ENVELOPE * env, ATTACHPTR ** idx,
656 short idxlen, HEADER * parent, int flags)
658 ENVELOPE *curenv = NULL;
659 HEADER *curhdr = NULL;
663 for (i = 0; i < idxlen; i++) {
664 if (idx[i]->content->tagged) {
665 curhdr = idx[i]->content->hdr;
666 curenv = curhdr->env;
672 curenv = parent->env;
676 if (curenv == NULL || curhdr == NULL) {
677 mutt_error _("Can't find any tagged messages.");
683 if ((flags & SENDNEWS)) {
684 /* in case followup set Newsgroups: with Followup-To: if it present */
685 if (!env->newsgroups && curenv &&
686 safe_strcasecmp (curenv->followup_to, "poster"))
687 env->newsgroups = safe_strdup (curenv->followup_to);
693 if (mutt_fetch_recips (env, curenv, flags) == -1)
697 for (i = 0; i < idxlen; i++) {
698 if (idx[i]->content->tagged
699 && mutt_fetch_recips (env, idx[i]->content->hdr->env,
705 if ((flags & SENDLISTREPLY) && !env->to) {
706 mutt_error _("No mailing lists found!");
711 mutt_fix_reply_recipients (env);
713 mutt_make_misc_reply_headers (env, Context, curhdr, curenv);
716 mutt_add_to_reference_headers (env, curenv, NULL, NULL);
718 LIST **p = NULL, **q = NULL;
720 for (i = 0; i < idxlen; i++) {
721 if (idx[i]->content->tagged)
722 mutt_add_to_reference_headers (env, idx[i]->content->hdr->env, &p,
731 /* This is _very_ similar to send.c's include_reply(). */
733 static void attach_include_reply (FILE * fp, FILE * tmpfp, HEADER * cur,
736 int cmflags = M_CM_PREFIX | M_CM_DECODE | M_CM_CHARCONV;
737 int chflags = CH_DECODE;
739 /* mutt_message_hook (cur, M_MESSAGEHOOK); */
741 mutt_make_attribution (Context, cur, tmpfp);
743 if (!option (OPTHEADER))
744 cmflags |= M_CM_NOHEADER;
745 if (option (OPTWEED)) {
747 cmflags |= M_CM_WEED;
750 _mutt_copy_message (tmpfp, fp, cur, cur->content, cmflags, chflags);
751 mutt_make_post_indent (Context, cur, tmpfp);
754 void mutt_attach_reply (FILE * fp, HEADER * hdr,
755 ATTACHPTR ** idx, short idxlen, BODY * cur, int flags)
757 short mime_reply_any = 0;
760 HEADER *parent = NULL;
761 HEADER *tmphdr = NULL;
765 char tmpbody[_POSIX_PATH_MAX];
768 char prefix[SHORT_STRING];
772 if (flags & SENDNEWS)
773 set_option (OPTNEWSSEND);
775 unset_option (OPTNEWSSEND);
778 if (check_all_msg (idx, idxlen, cur, 0) == -1) {
779 nattach = count_tagged (idx, idxlen);
780 if ((parent = find_parent (idx, idxlen, cur, nattach)) == NULL)
784 if (nattach > 1 && !check_can_decode (idx, idxlen, cur)) {
785 if ((rc = query_quadoption (OPT_MIMEFWDREST,
787 ("Can't decode all tagged attachments. MIME-encapsulate the others?")))
790 else if (rc == M_YES)
793 else if (nattach == 1)
796 tmphdr = mutt_new_header ();
797 tmphdr->env = mutt_new_envelope ();
799 if (attach_reply_envelope_defaults (tmphdr->env, idx, idxlen,
800 parent ? parent : (cur ? cur->
803 mutt_free_header (&tmphdr);
807 mutt_mktemp (tmpbody);
808 if ((tmpfp = safe_fopen (tmpbody, "w")) == NULL) {
809 mutt_error (_("Can't create %s."), tmpbody);
810 mutt_free_header (&tmphdr);
816 attach_include_reply (fp, tmpfp, cur->hdr, flags);
818 for (i = 0; i < idxlen; i++) {
819 if (idx[i]->content->tagged)
820 attach_include_reply (fp, tmpfp, idx[i]->content->hdr, flags);
825 mutt_make_attribution (Context, parent, tmpfp);
827 memset (&st, 0, sizeof (STATE));
831 if (!option (OPTTEXTFLOWED))
832 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix),
835 strfcpy (prefix, ">", sizeof (prefix));
838 st.flags = M_CHARCONV;
840 if (option (OPTWEED))
843 if (option (OPTHEADER))
844 include_header (1, fp, parent, tmpfp, prefix);
847 if (mutt_can_decode (cur)) {
848 mutt_body_handler (cur, &st);
849 state_putc ('\n', &st);
852 mutt_copy_body (fp, &tmphdr->content, cur);
855 for (i = 0; i < idxlen; i++) {
856 if (idx[i]->content->tagged && mutt_can_decode (idx[i]->content)) {
857 mutt_body_handler (idx[i]->content, &st);
858 state_putc ('\n', &st);
863 mutt_make_post_indent (Context, parent, tmpfp);
865 if (mime_reply_any && !cur &&
866 copy_problematic_attachments (fp, &tmphdr->content, idx, idxlen,
868 mutt_free_header (&tmphdr);
876 if (ci_send_message (flags, tmphdr, tmpbody, NULL, parent) == 0)
877 mutt_set_flag (Context, hdr, M_REPLIED, 1);