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"
24 #include "mutt_idna.h"
30 /* some helper functions to verify that we are exclusively operating
31 * on message/rfc822 attachments
34 static short check_msg (BODY * b, short err)
36 if (!mutt_is_message_type (b->type, b->subtype)) {
38 mutt_error _("You may only bounce message/rfc822 parts.");
45 static short check_all_msg (ATTACHPTR ** idx, short idxlen,
46 BODY * cur, short err)
50 if (cur && check_msg (cur, err) == -1)
53 for (i = 0; i < idxlen; i++) {
54 if (idx[i]->content->tagged) {
55 if (check_msg (idx[i]->content, err) == -1)
64 /* can we decode all tagged attachments? */
66 static short check_can_decode (ATTACHPTR ** idx, short idxlen, BODY * cur)
71 return mutt_can_decode (cur);
73 for (i = 0; i < idxlen; i++)
74 if (idx[i]->content->tagged && !mutt_can_decode (idx[i]->content))
80 static short count_tagged (ATTACHPTR ** idx, short idxlen)
85 for (i = 0; i < idxlen; i++)
86 if (idx[i]->content->tagged)
92 /* count the number of tagged children below a multipart or message
96 static short count_tagged_children (ATTACHPTR ** idx, short idxlen, short i)
98 short level = idx[i]->level;
101 while ((++i < idxlen) && (level < idx[i]->level))
102 if (idx[i]->content->tagged)
112 ** The bounce function, from the attachment menu
116 void mutt_attach_bounce (FILE * fp, HEADER * hdr,
117 ATTACHPTR ** idx, short idxlen, BODY * cur)
121 char buf[HUGE_STRING];
127 if (check_all_msg (idx, idxlen, cur, 1) == -1)
130 /* one or more messages? */
131 p = (cur || count_tagged (idx, idxlen) == 1);
134 strfcpy (prompt, _("Bounce message to: "), sizeof (prompt));
136 strfcpy (prompt, _("Bounce tagged messages to: "), sizeof (prompt));
139 if (mutt_get_field (prompt, buf, sizeof (buf), M_ALIAS)
143 if (!(adr = rfc822_parse_adrlist (adr, buf))) {
144 mutt_error _("Error parsing address!");
149 adr = mutt_expand_aliases (adr);
151 if (mutt_addrlist_to_idna (adr, &err) < 0) {
152 mutt_error (_("Bad IDN: '%s'"), err);
154 rfc822_free_address (&adr);
159 rfc822_write_address (buf, sizeof (buf), adr, 1);
161 #define extra_space (15+7+2)
165 snprintf (prompt, sizeof (prompt) - 4,
166 (p ? _("Bounce message to %s") : _("Bounce messages to %s")),
169 if (mutt_strwidth (prompt) > COLS - extra_space) {
170 mutt_format_string (prompt, sizeof (prompt) - 4,
171 0, COLS - extra_space, 0, 0,
172 prompt, sizeof (prompt), 0);
173 safe_strcat (prompt, sizeof (prompt), "...?");
176 safe_strcat (prompt, sizeof (prompt), "?");
178 if (query_quadoption (OPT_BOUNCE, prompt) != M_YES) {
179 rfc822_free_address (&adr);
180 CLEARLINE (LINES - 1);
181 mutt_message (p ? _("Message not bounced.") : _("Messages not bounced."));
185 CLEARLINE (LINES - 1);
188 ret = mutt_bounce_message (fp, cur->hdr, adr);
190 for (i = 0; i < idxlen; i++) {
191 if (idx[i]->content->tagged)
192 if (mutt_bounce_message (fp, idx[i]->content->hdr, adr))
198 mutt_message (p ? _("Message bounced.") : _("Messages bounced."));
200 mutt_error (p ? _("Error bouncing message!") :
201 _("Error bouncing messages!"));
208 ** resend-message, from the attachment menu
213 void mutt_attach_resend (FILE * fp, HEADER * hdr, ATTACHPTR ** idx,
214 short idxlen, BODY * cur)
218 if (check_all_msg (idx, idxlen, cur, 1) == -1)
222 mutt_resend_message (fp, Context, cur->hdr);
224 for (i = 0; i < idxlen; i++)
225 if (idx[i]->content->tagged)
226 mutt_resend_message (fp, Context, idx[i]->content->hdr);
233 ** forward-message, from the attachment menu
237 /* try to find a common parent message for the tagged attachments. */
239 static HEADER *find_common_parent (ATTACHPTR ** idx, short idxlen,
245 for (i = 0; i < idxlen; i++)
246 if (idx[i]->content->tagged)
250 if (mutt_is_message_type
251 (idx[i]->content->type, idx[i]->content->subtype)) {
252 nchildren = count_tagged_children (idx, idxlen, i);
253 if (nchildren == nattach)
254 return idx[i]->content->hdr;
262 * check whether attachment #i is a parent of the attachment
265 * Note: This and the calling procedure could be optimized quite a
266 * bit. For now, it's not worth the effort.
269 static int is_parent (short i, ATTACHPTR ** idx, short idxlen, BODY * cur)
271 short level = idx[i]->level;
273 while ((++i < idxlen) && idx[i]->level > level) {
274 if (idx[i]->content == cur)
281 static HEADER *find_parent (ATTACHPTR ** idx, short idxlen, BODY * cur,
285 HEADER *parent = NULL;
288 for (i = 0; i < idxlen; i++) {
289 if (mutt_is_message_type
290 (idx[i]->content->type, idx[i]->content->subtype)
291 && is_parent (i, idx, idxlen, cur))
292 parent = idx[i]->content->hdr;
293 if (idx[i]->content == cur)
298 parent = find_common_parent (idx, idxlen, nattach);
303 static void include_header (int quote, FILE * ifp,
304 HEADER * hdr, FILE * ofp, char *_prefix)
306 int chflags = CH_DECODE;
307 char prefix[SHORT_STRING];
309 if (option (OPTWEED))
310 chflags |= CH_WEED | CH_REORDER;
314 strfcpy (prefix, _prefix, sizeof (prefix));
315 else if (!option (OPTTEXTFLOWED))
316 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix),
319 strfcpy (prefix, ">", sizeof (prefix));
321 chflags |= CH_PREFIX;
324 mutt_copy_header (ifp, hdr, ofp, chflags, quote ? prefix : NULL);
327 /* Attach all the body parts which can't be decoded.
328 * This code is shared by forwarding and replying. */
330 static BODY **copy_problematic_attachments (FILE * fp,
333 short idxlen, short force)
337 for (i = 0; i < idxlen; i++) {
338 if (idx[i]->content->tagged &&
339 (force || !mutt_can_decode (idx[i]->content))) {
340 if (mutt_copy_body (fp, last, idx[i]->content) == -1)
341 return NULL; /* XXXXX - may lead to crashes */
342 last = &((*last)->next);
349 * forward one or several MIME bodies
350 * (non-message types)
353 static void attach_forward_bodies (FILE * fp, HEADER * hdr,
354 ATTACHPTR ** idx, short idxlen,
355 BODY * cur, short nattach, int flags)
358 short mime_fwd_all = 0;
359 short mime_fwd_any = 1;
360 HEADER *parent = NULL;
361 HEADER *tmphdr = NULL;
363 char tmpbody[_POSIX_PATH_MAX];
373 * First, find the parent message.
374 * Note: This could be made an option by just
375 * putting the following lines into an if block.
379 parent = find_parent (idx, idxlen, cur, nattach);
385 tmphdr = mutt_new_header ();
386 tmphdr->env = mutt_new_envelope ();
387 mutt_make_forward_subject (tmphdr->env, Context, parent);
389 mutt_mktemp (tmpbody);
390 if ((tmpfp = safe_fopen (tmpbody, "w")) == NULL) {
391 mutt_error (_("Can't open temporary file %s."), tmpbody);
395 mutt_forward_intro (tmpfp, parent);
397 /* prepare the prefix here since we'll need it later. */
399 if (option (OPTFORWQUOTE)) {
400 if (!option (OPTTEXTFLOWED))
401 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix), Context,
404 strfcpy (prefix, ">", sizeof (prefix));
407 include_header (option (OPTFORWQUOTE), fp, parent, tmpfp, prefix);
411 * Now, we have prepared the first part of the message body: The
412 * original message's header.
414 * The next part is more interesting: either include the message bodies,
418 if ((!cur || mutt_can_decode (cur)) &&
419 (rc = query_quadoption (OPT_MIMEFWD,
420 _("Forward as attachments?"))) == M_YES)
426 * shortcut MIMEFWDREST when there is only one attachment. Is
430 if (!mime_fwd_all && !cur && (nattach > 1)
431 && !check_can_decode (idx, idxlen, cur)) {
432 if ((rc = query_quadoption (OPT_MIMEFWDREST,
434 ("Can't decode all tagged attachments. MIME-forward the others?")))
441 /* initialize a state structure */
443 memset (&st, 0, sizeof (st));
445 if (option (OPTFORWQUOTE))
447 st.flags = M_CHARCONV;
448 if (option (OPTWEED))
453 /* where do we append new MIME parts? */
454 last = &tmphdr->content;
457 /* single body case */
459 if (!mime_fwd_all && mutt_can_decode (cur)) {
460 mutt_body_handler (cur, &st);
461 state_putc ('\n', &st);
464 if (mutt_copy_body (fp, last, cur) == -1)
466 last = &((*last)->next);
470 /* multiple body case */
473 for (i = 0; i < idxlen; i++) {
474 if (idx[i]->content->tagged && mutt_can_decode (idx[i]->content)) {
475 mutt_body_handler (idx[i]->content, &st);
476 state_putc ('\n', &st);
483 copy_problematic_attachments (fp, last, idx, idxlen,
484 mime_fwd_all)) == NULL)
488 mutt_forward_trailer (tmpfp);
493 /* now that we have the template, send it. */
494 ci_send_message (flags, tmphdr, tmpbody, NULL, parent);
501 mutt_unlink (tmpbody);
504 mutt_free_header (&tmphdr);
509 * Forward one or several message-type attachments. This
510 * is different from the previous function
511 * since we want to mimic the index menu's behaviour.
513 * Code reuse from ci_send_message is not possible here -
514 * ci_send_message relies on a context structure to find messages,
515 * while, on the attachment menu, messages are referenced through
516 * the attachment index.
519 static void attach_forward_msgs (FILE * fp, HEADER * hdr,
520 ATTACHPTR ** idx, short idxlen, BODY * cur,
523 HEADER *curhdr = NULL;
529 char tmpbody[_POSIX_PATH_MAX];
533 int chflags = CH_XMIT;
538 for (i = 0; i < idxlen; i++)
539 if (idx[i]->content->tagged) {
540 curhdr = idx[i]->content->hdr;
545 tmphdr = mutt_new_header ();
546 tmphdr->env = mutt_new_envelope ();
547 mutt_make_forward_subject (tmphdr->env, Context, curhdr);
552 if ((rc = query_quadoption (OPT_MIMEFWD,
553 _("Forward MIME encapsulated?"))) == M_NO) {
555 /* no MIME encapsulation */
557 mutt_mktemp (tmpbody);
558 if (!(tmpfp = safe_fopen (tmpbody, "w"))) {
559 mutt_error (_("Can't create %s."), tmpbody);
560 mutt_free_header (&tmphdr);
564 if (option (OPTFORWQUOTE)) {
565 chflags |= CH_PREFIX;
566 cmflags |= M_CM_PREFIX;
569 if (option (OPTFORWDECODE)) {
570 cmflags |= M_CM_DECODE | M_CM_CHARCONV;
571 if (option (OPTWEED)) {
572 chflags |= CH_WEED | CH_REORDER;
573 cmflags |= M_CM_WEED;
579 /* mutt_message_hook (cur->hdr, M_MESSAGEHOOK); */
580 mutt_forward_intro (tmpfp, cur->hdr);
581 _mutt_copy_message (tmpfp, fp, cur->hdr, cur->hdr->content, cmflags,
583 mutt_forward_trailer (tmpfp);
586 for (i = 0; i < idxlen; i++) {
587 if (idx[i]->content->tagged) {
588 /* mutt_message_hook (idx[i]->content->hdr, M_MESSAGEHOOK); */
589 mutt_forward_intro (tmpfp, idx[i]->content->hdr);
590 _mutt_copy_message (tmpfp, fp, idx[i]->content->hdr,
591 idx[i]->content->hdr->content, cmflags,
593 mutt_forward_trailer (tmpfp);
599 else if (rc == M_YES) { /* do MIME encapsulation - we don't need to do much here */
600 last = &tmphdr->content;
602 mutt_copy_body (fp, last, cur);
604 for (i = 0; i < idxlen; i++)
605 if (idx[i]->content->tagged) {
606 mutt_copy_body (fp, last, idx[i]->content);
607 last = &((*last)->next);
612 mutt_free_header (&tmphdr);
614 ci_send_message (flags, tmphdr, *tmpbody ? tmpbody : NULL, NULL, curhdr);
618 void mutt_attach_forward (FILE * fp, HEADER * hdr,
619 ATTACHPTR ** idx, short idxlen, BODY * cur,
625 if (check_all_msg (idx, idxlen, cur, 0) == 0)
626 attach_forward_msgs (fp, hdr, idx, idxlen, cur, flags);
628 nattach = count_tagged (idx, idxlen);
629 attach_forward_bodies (fp, hdr, idx, idxlen, cur, nattach, flags);
637 ** the various reply functions, from the attachment menu
642 /* Create the envelope defaults for a reply.
644 * This function can be invoked in two ways.
646 * Either, parent is NULL. In this case, all tagged bodies are of a message type,
647 * and the header information is fetched from them.
649 * Or, parent is non-NULL. In this case, cur is the common parent of all the
650 * tagged attachments.
652 * Note that this code is horribly similar to envelope_defaults () from send.c.
656 attach_reply_envelope_defaults (ENVELOPE * env, ATTACHPTR ** idx,
657 short idxlen, HEADER * parent, int flags)
659 ENVELOPE *curenv = NULL;
660 HEADER *curhdr = NULL;
664 for (i = 0; i < idxlen; i++) {
665 if (idx[i]->content->tagged) {
666 curhdr = idx[i]->content->hdr;
667 curenv = curhdr->env;
673 curenv = parent->env;
677 if (curenv == NULL || curhdr == NULL) {
678 mutt_error _("Can't find any tagged messages.");
684 if ((flags & SENDNEWS)) {
685 /* in case followup set Newsgroups: with Followup-To: if it present */
686 if (!env->newsgroups && curenv &&
687 safe_strcasecmp (curenv->followup_to, "poster"))
688 env->newsgroups = safe_strdup (curenv->followup_to);
694 if (mutt_fetch_recips (env, curenv, flags) == -1)
698 for (i = 0; i < idxlen; i++) {
699 if (idx[i]->content->tagged
700 && mutt_fetch_recips (env, idx[i]->content->hdr->env,
706 if ((flags & SENDLISTREPLY) && !env->to) {
707 mutt_error _("No mailing lists found!");
712 mutt_fix_reply_recipients (env);
714 mutt_make_misc_reply_headers (env, Context, curhdr, curenv);
717 mutt_add_to_reference_headers (env, curenv, NULL, NULL);
719 LIST **p = NULL, **q = NULL;
721 for (i = 0; i < idxlen; i++) {
722 if (idx[i]->content->tagged)
723 mutt_add_to_reference_headers (env, idx[i]->content->hdr->env, &p,
732 /* This is _very_ similar to send.c's include_reply(). */
734 static void attach_include_reply (FILE * fp, FILE * tmpfp, HEADER * cur,
737 int cmflags = M_CM_PREFIX | M_CM_DECODE | M_CM_CHARCONV;
738 int chflags = CH_DECODE;
740 /* mutt_message_hook (cur, M_MESSAGEHOOK); */
742 mutt_make_attribution (Context, cur, tmpfp);
744 if (!option (OPTHEADER))
745 cmflags |= M_CM_NOHEADER;
746 if (option (OPTWEED)) {
748 cmflags |= M_CM_WEED;
751 _mutt_copy_message (tmpfp, fp, cur, cur->content, cmflags, chflags);
752 mutt_make_post_indent (Context, cur, tmpfp);
755 void mutt_attach_reply (FILE * fp, HEADER * hdr,
756 ATTACHPTR ** idx, short idxlen, BODY * cur, int flags)
758 short mime_reply_any = 0;
761 HEADER *parent = NULL;
762 HEADER *tmphdr = NULL;
766 char tmpbody[_POSIX_PATH_MAX];
769 char prefix[SHORT_STRING];
773 if (flags & SENDNEWS)
774 set_option (OPTNEWSSEND);
776 unset_option (OPTNEWSSEND);
779 if (check_all_msg (idx, idxlen, cur, 0) == -1) {
780 nattach = count_tagged (idx, idxlen);
781 if ((parent = find_parent (idx, idxlen, cur, nattach)) == NULL)
785 if (nattach > 1 && !check_can_decode (idx, idxlen, cur)) {
786 if ((rc = query_quadoption (OPT_MIMEFWDREST,
788 ("Can't decode all tagged attachments. MIME-encapsulate the others?")))
791 else if (rc == M_YES)
794 else if (nattach == 1)
797 tmphdr = mutt_new_header ();
798 tmphdr->env = mutt_new_envelope ();
800 if (attach_reply_envelope_defaults (tmphdr->env, idx, idxlen,
801 parent ? parent : (cur ? cur->
804 mutt_free_header (&tmphdr);
808 mutt_mktemp (tmpbody);
809 if ((tmpfp = safe_fopen (tmpbody, "w")) == NULL) {
810 mutt_error (_("Can't create %s."), tmpbody);
811 mutt_free_header (&tmphdr);
817 attach_include_reply (fp, tmpfp, cur->hdr, flags);
819 for (i = 0; i < idxlen; i++) {
820 if (idx[i]->content->tagged)
821 attach_include_reply (fp, tmpfp, idx[i]->content->hdr, flags);
826 mutt_make_attribution (Context, parent, tmpfp);
828 memset (&st, 0, sizeof (STATE));
832 if (!option (OPTTEXTFLOWED))
833 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix),
836 strfcpy (prefix, ">", sizeof (prefix));
839 st.flags = M_CHARCONV;
841 if (option (OPTWEED))
844 if (option (OPTHEADER))
845 include_header (1, fp, parent, tmpfp, prefix);
848 if (mutt_can_decode (cur)) {
849 mutt_body_handler (cur, &st);
850 state_putc ('\n', &st);
853 mutt_copy_body (fp, &tmphdr->content, cur);
856 for (i = 0; i < idxlen; i++) {
857 if (idx[i]->content->tagged && mutt_can_decode (idx[i]->content)) {
858 mutt_body_handler (idx[i]->content, &st);
859 state_putc ('\n', &st);
864 mutt_make_post_indent (Context, parent, tmpfp);
866 if (mime_reply_any && !cur &&
867 copy_problematic_attachments (fp, &tmphdr->content, idx, idxlen,
869 mutt_free_header (&tmphdr);
877 if (ci_send_message (flags, tmphdr, tmpbody, NULL, parent) == 0)
878 mutt_set_flag (Context, hdr, M_REPLIED, 1);