2 * Copyright (C) 1999-2000 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., 59 Temple Place - Suite 330,
19 * Boston, MA 02111, USA.
27 #include "mutt_curses.h"
28 #include "mutt_menu.h"
36 #include "mutt_idna.h"
38 /* some helper functions to verify that we are exclusively operating
39 * on message/rfc822 attachments
42 static short check_msg (BODY * b, short err)
44 if (!mutt_is_message_type (b->type, b->subtype)) {
46 mutt_error _("You may only bounce message/rfc822 parts.");
53 static short check_all_msg (ATTACHPTR ** idx, short idxlen,
54 BODY * cur, short err)
58 if (cur && check_msg (cur, err) == -1)
61 for (i = 0; i < idxlen; i++) {
62 if (idx[i]->content->tagged) {
63 if (check_msg (idx[i]->content, err) == -1)
72 /* can we decode all tagged attachments? */
74 static short check_can_decode (ATTACHPTR ** idx, short idxlen, BODY * cur)
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, short idxlen, short i)
106 short level = idx[i]->level;
109 while ((++i < idxlen) && (level < idx[i]->level))
110 if (idx[i]->content->tagged)
120 ** The bounce function, from the attachment menu
124 void mutt_attach_bounce (FILE * fp, HEADER * hdr,
125 ATTACHPTR ** idx, short idxlen, BODY * cur)
129 char buf[HUGE_STRING];
135 if (check_all_msg (idx, idxlen, cur, 1) == -1)
138 /* one or more messages? */
139 p = (cur || count_tagged (idx, idxlen) == 1);
142 strfcpy (prompt, _("Bounce message to: "), sizeof (prompt));
144 strfcpy (prompt, _("Bounce tagged messages to: "), sizeof (prompt));
147 if (mutt_get_field (prompt, buf, sizeof (buf), M_ALIAS)
151 if (!(adr = rfc822_parse_adrlist (adr, buf))) {
152 mutt_error _("Error parsing address!");
157 adr = mutt_expand_aliases (adr);
159 if (mutt_addrlist_to_idna (adr, &err) < 0) {
160 mutt_error (_("Bad IDN: '%s'"), err);
162 rfc822_free_address (&adr);
167 rfc822_write_address (buf, sizeof (buf), adr, 1);
169 #define extra_space (15+7+2)
173 snprintf (prompt, sizeof (prompt) - 4,
174 (p ? _("Bounce message to %s") : _("Bounce messages to %s")),
177 if (mutt_strwidth (prompt) > COLS - extra_space) {
178 mutt_format_string (prompt, sizeof (prompt) - 4,
179 0, COLS - extra_space, 0, 0,
180 prompt, sizeof (prompt), 0);
181 safe_strcat (prompt, sizeof (prompt), "...?");
184 safe_strcat (prompt, sizeof (prompt), "?");
186 if (query_quadoption (OPT_BOUNCE, prompt) != M_YES) {
187 rfc822_free_address (&adr);
188 CLEARLINE (LINES - 1);
189 mutt_message (p ? _("Message not bounced.") : _("Messages not bounced."));
193 CLEARLINE (LINES - 1);
196 ret = mutt_bounce_message (fp, cur->hdr, adr);
198 for (i = 0; i < idxlen; i++) {
199 if (idx[i]->content->tagged)
200 if (mutt_bounce_message (fp, idx[i]->content->hdr, adr))
206 mutt_message (p ? _("Message bounced.") : _("Messages bounced."));
208 mutt_error (p ? _("Error bouncing message!") :
209 _("Error bouncing messages!"));
216 ** resend-message, from the attachment menu
221 void mutt_attach_resend (FILE * fp, HEADER * hdr, ATTACHPTR ** idx,
222 short idxlen, BODY * cur)
226 if (check_all_msg (idx, idxlen, cur, 1) == -1)
230 mutt_resend_message (fp, Context, cur->hdr);
232 for (i = 0; i < idxlen; i++)
233 if (idx[i]->content->tagged)
234 mutt_resend_message (fp, Context, idx[i]->content->hdr);
241 ** forward-message, from the attachment menu
245 /* try to find a common parent message for the tagged attachments. */
247 static HEADER *find_common_parent (ATTACHPTR ** idx, short idxlen,
253 for (i = 0; i < idxlen; i++)
254 if (idx[i]->content->tagged)
258 if (mutt_is_message_type
259 (idx[i]->content->type, idx[i]->content->subtype)) {
260 nchildren = count_tagged_children (idx, idxlen, i);
261 if (nchildren == nattach)
262 return idx[i]->content->hdr;
270 * check whether attachment #i is a parent of the attachment
273 * Note: This and the calling procedure could be optimized quite a
274 * bit. For now, it's not worth the effort.
277 static int is_parent (short i, ATTACHPTR ** idx, short idxlen, BODY * cur)
279 short level = idx[i]->level;
281 while ((++i < idxlen) && idx[i]->level > level) {
282 if (idx[i]->content == cur)
289 static HEADER *find_parent (ATTACHPTR ** idx, short idxlen, BODY * cur,
293 HEADER *parent = NULL;
296 for (i = 0; i < idxlen; i++) {
297 if (mutt_is_message_type
298 (idx[i]->content->type, idx[i]->content->subtype)
299 && is_parent (i, idx, idxlen, cur))
300 parent = idx[i]->content->hdr;
301 if (idx[i]->content == cur)
306 parent = find_common_parent (idx, idxlen, nattach);
311 static void include_header (int quote, FILE * ifp,
312 HEADER * hdr, FILE * ofp, char *_prefix)
314 int chflags = CH_DECODE;
315 char prefix[SHORT_STRING];
317 if (option (OPTWEED))
318 chflags |= CH_WEED | CH_REORDER;
322 strfcpy (prefix, _prefix, sizeof (prefix));
323 else if (!option (OPTTEXTFLOWED))
324 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix),
327 strfcpy (prefix, ">", sizeof (prefix));
329 chflags |= CH_PREFIX;
332 mutt_copy_header (ifp, hdr, ofp, chflags, quote ? prefix : NULL);
335 /* Attach all the body parts which can't be decoded.
336 * This code is shared by forwarding and replying. */
338 static BODY **copy_problematic_attachments (FILE * fp,
341 short idxlen, short force)
345 for (i = 0; i < idxlen; i++) {
346 if (idx[i]->content->tagged &&
347 (force || !mutt_can_decode (idx[i]->content))) {
348 if (mutt_copy_body (fp, last, idx[i]->content) == -1)
349 return NULL; /* XXXXX - may lead to crashes */
350 last = &((*last)->next);
357 * forward one or several MIME bodies
358 * (non-message types)
361 static void attach_forward_bodies (FILE * fp, HEADER * hdr,
362 ATTACHPTR ** idx, short idxlen,
363 BODY * cur, short nattach, int flags)
366 short mime_fwd_all = 0;
367 short mime_fwd_any = 1;
368 HEADER *parent = NULL;
369 HEADER *tmphdr = NULL;
371 char tmpbody[_POSIX_PATH_MAX];
381 * First, find the parent message.
382 * Note: This could be made an option by just
383 * putting the following lines into an if block.
387 parent = find_parent (idx, idxlen, cur, nattach);
393 tmphdr = mutt_new_header ();
394 tmphdr->env = mutt_new_envelope ();
395 mutt_make_forward_subject (tmphdr->env, Context, parent);
397 mutt_mktemp (tmpbody);
398 if ((tmpfp = safe_fopen (tmpbody, "w")) == NULL) {
399 mutt_error (_("Can't open temporary file %s."), tmpbody);
403 mutt_forward_intro (tmpfp, parent);
405 /* prepare the prefix here since we'll need it later. */
407 if (option (OPTFORWQUOTE)) {
408 if (!option (OPTTEXTFLOWED))
409 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix), Context,
412 strfcpy (prefix, ">", sizeof (prefix));
415 include_header (option (OPTFORWQUOTE), fp, parent, tmpfp, prefix);
419 * Now, we have prepared the first part of the message body: The
420 * original message's header.
422 * The next part is more interesting: either include the message bodies,
426 if ((!cur || mutt_can_decode (cur)) &&
427 (rc = query_quadoption (OPT_MIMEFWD,
428 _("Forward as attachments?"))) == M_YES)
434 * shortcut MIMEFWDREST when there is only one attachment. Is
438 if (!mime_fwd_all && !cur && (nattach > 1)
439 && !check_can_decode (idx, idxlen, cur)) {
440 if ((rc = query_quadoption (OPT_MIMEFWDREST,
442 ("Can't decode all tagged attachments. MIME-forward the others?")))
449 /* initialize a state structure */
451 memset (&st, 0, sizeof (st));
453 if (option (OPTFORWQUOTE))
455 st.flags = M_CHARCONV;
456 if (option (OPTWEED))
461 /* where do we append new MIME parts? */
462 last = &tmphdr->content;
465 /* single body case */
467 if (!mime_fwd_all && mutt_can_decode (cur)) {
468 mutt_body_handler (cur, &st);
469 state_putc ('\n', &st);
472 if (mutt_copy_body (fp, last, cur) == -1)
474 last = &((*last)->next);
478 /* multiple body case */
481 for (i = 0; i < idxlen; i++) {
482 if (idx[i]->content->tagged && mutt_can_decode (idx[i]->content)) {
483 mutt_body_handler (idx[i]->content, &st);
484 state_putc ('\n', &st);
491 copy_problematic_attachments (fp, last, idx, idxlen,
492 mime_fwd_all)) == NULL)
496 mutt_forward_trailer (tmpfp);
501 /* now that we have the template, send it. */
502 ci_send_message (flags, tmphdr, tmpbody, NULL, parent);
509 mutt_unlink (tmpbody);
512 mutt_free_header (&tmphdr);
517 * Forward one or several message-type attachments. This
518 * is different from the previous function
519 * since we want to mimic the index menu's behaviour.
521 * Code reuse from ci_send_message is not possible here -
522 * ci_send_message relies on a context structure to find messages,
523 * while, on the attachment menu, messages are referenced through
524 * the attachment index.
527 static void attach_forward_msgs (FILE * fp, HEADER * hdr,
528 ATTACHPTR ** idx, short idxlen, BODY * cur,
531 HEADER *curhdr = NULL;
537 char tmpbody[_POSIX_PATH_MAX];
541 int chflags = CH_XMIT;
546 for (i = 0; i < idxlen; i++)
547 if (idx[i]->content->tagged) {
548 curhdr = idx[i]->content->hdr;
553 tmphdr = mutt_new_header ();
554 tmphdr->env = mutt_new_envelope ();
555 mutt_make_forward_subject (tmphdr->env, Context, curhdr);
560 if ((rc = query_quadoption (OPT_MIMEFWD,
561 _("Forward MIME encapsulated?"))) == M_NO) {
563 /* no MIME encapsulation */
565 mutt_mktemp (tmpbody);
566 if (!(tmpfp = safe_fopen (tmpbody, "w"))) {
567 mutt_error (_("Can't create %s."), tmpbody);
568 mutt_free_header (&tmphdr);
572 if (option (OPTFORWQUOTE)) {
573 chflags |= CH_PREFIX;
574 cmflags |= M_CM_PREFIX;
577 if (option (OPTFORWDECODE)) {
578 cmflags |= M_CM_DECODE | M_CM_CHARCONV;
579 if (option (OPTWEED)) {
580 chflags |= CH_WEED | CH_REORDER;
581 cmflags |= M_CM_WEED;
587 /* mutt_message_hook (cur->hdr, M_MESSAGEHOOK); */
588 mutt_forward_intro (tmpfp, cur->hdr);
589 _mutt_copy_message (tmpfp, fp, cur->hdr, cur->hdr->content, cmflags,
591 mutt_forward_trailer (tmpfp);
594 for (i = 0; i < idxlen; i++) {
595 if (idx[i]->content->tagged) {
596 /* mutt_message_hook (idx[i]->content->hdr, M_MESSAGEHOOK); */
597 mutt_forward_intro (tmpfp, idx[i]->content->hdr);
598 _mutt_copy_message (tmpfp, fp, idx[i]->content->hdr,
599 idx[i]->content->hdr->content, cmflags,
601 mutt_forward_trailer (tmpfp);
607 else if (rc == M_YES) { /* do MIME encapsulation - we don't need to do much here */
608 last = &tmphdr->content;
610 mutt_copy_body (fp, last, cur);
612 for (i = 0; i < idxlen; i++)
613 if (idx[i]->content->tagged) {
614 mutt_copy_body (fp, last, idx[i]->content);
615 last = &((*last)->next);
620 mutt_free_header (&tmphdr);
622 ci_send_message (flags, tmphdr, *tmpbody ? tmpbody : NULL, NULL, curhdr);
626 void mutt_attach_forward (FILE * fp, HEADER * hdr,
627 ATTACHPTR ** idx, short idxlen, BODY * cur,
633 if (check_all_msg (idx, idxlen, cur, 0) == 0)
634 attach_forward_msgs (fp, hdr, idx, idxlen, cur, flags);
636 nattach = count_tagged (idx, idxlen);
637 attach_forward_bodies (fp, hdr, idx, idxlen, cur, nattach, flags);
645 ** the various reply functions, from the attachment menu
650 /* Create the envelope defaults for a reply.
652 * This function can be invoked in two ways.
654 * Either, parent is NULL. In this case, all tagged bodies are of a message type,
655 * and the header information is fetched from them.
657 * Or, parent is non-NULL. In this case, cur is the common parent of all the
658 * tagged attachments.
660 * Note that this code is horribly similar to envelope_defaults () from send.c.
664 attach_reply_envelope_defaults (ENVELOPE * env, ATTACHPTR ** idx,
665 short idxlen, HEADER * parent, int flags)
667 ENVELOPE *curenv = NULL;
668 HEADER *curhdr = NULL;
672 for (i = 0; i < idxlen; i++) {
673 if (idx[i]->content->tagged) {
674 curhdr = idx[i]->content->hdr;
675 curenv = curhdr->env;
681 curenv = parent->env;
685 if (curenv == NULL || curhdr == NULL) {
686 mutt_error _("Can't find any tagged messages.");
692 if ((flags & SENDNEWS)) {
693 /* in case followup set Newsgroups: with Followup-To: if it present */
694 if (!env->newsgroups && curenv &&
695 mutt_strcasecmp (curenv->followup_to, "poster"))
696 env->newsgroups = safe_strdup (curenv->followup_to);
702 if (mutt_fetch_recips (env, curenv, flags) == -1)
706 for (i = 0; i < idxlen; i++) {
707 if (idx[i]->content->tagged
708 && mutt_fetch_recips (env, idx[i]->content->hdr->env,
714 if ((flags & SENDLISTREPLY) && !env->to) {
715 mutt_error _("No mailing lists found!");
720 mutt_fix_reply_recipients (env);
722 mutt_make_misc_reply_headers (env, Context, curhdr, curenv);
725 mutt_add_to_reference_headers (env, curenv, NULL, NULL);
727 LIST **p = NULL, **q = NULL;
729 for (i = 0; i < idxlen; i++) {
730 if (idx[i]->content->tagged)
731 mutt_add_to_reference_headers (env, idx[i]->content->hdr->env, &p,
740 /* This is _very_ similar to send.c's include_reply(). */
742 static void attach_include_reply (FILE * fp, FILE * tmpfp, HEADER * cur,
745 int cmflags = M_CM_PREFIX | M_CM_DECODE | M_CM_CHARCONV;
746 int chflags = CH_DECODE;
748 /* mutt_message_hook (cur, M_MESSAGEHOOK); */
750 mutt_make_attribution (Context, cur, tmpfp);
752 if (!option (OPTHEADER))
753 cmflags |= M_CM_NOHEADER;
754 if (option (OPTWEED)) {
756 cmflags |= M_CM_WEED;
759 _mutt_copy_message (tmpfp, fp, cur, cur->content, cmflags, chflags);
760 mutt_make_post_indent (Context, cur, tmpfp);
763 void mutt_attach_reply (FILE * fp, HEADER * hdr,
764 ATTACHPTR ** idx, short idxlen, BODY * cur, int flags)
766 short mime_reply_any = 0;
769 HEADER *parent = NULL;
770 HEADER *tmphdr = NULL;
774 char tmpbody[_POSIX_PATH_MAX];
777 char prefix[SHORT_STRING];
781 if (flags & SENDNEWS)
782 set_option (OPTNEWSSEND);
784 unset_option (OPTNEWSSEND);
787 if (check_all_msg (idx, idxlen, cur, 0) == -1) {
788 nattach = count_tagged (idx, idxlen);
789 if ((parent = find_parent (idx, idxlen, cur, nattach)) == NULL)
793 if (nattach > 1 && !check_can_decode (idx, idxlen, cur)) {
794 if ((rc = query_quadoption (OPT_MIMEFWDREST,
796 ("Can't decode all tagged attachments. MIME-encapsulate the others?")))
799 else if (rc == M_YES)
802 else if (nattach == 1)
805 tmphdr = mutt_new_header ();
806 tmphdr->env = mutt_new_envelope ();
808 if (attach_reply_envelope_defaults (tmphdr->env, idx, idxlen,
809 parent ? parent : (cur ? cur->
812 mutt_free_header (&tmphdr);
816 mutt_mktemp (tmpbody);
817 if ((tmpfp = safe_fopen (tmpbody, "w")) == NULL) {
818 mutt_error (_("Can't create %s."), tmpbody);
819 mutt_free_header (&tmphdr);
825 attach_include_reply (fp, tmpfp, cur->hdr, flags);
827 for (i = 0; i < idxlen; i++) {
828 if (idx[i]->content->tagged)
829 attach_include_reply (fp, tmpfp, idx[i]->content->hdr, flags);
834 mutt_make_attribution (Context, parent, tmpfp);
836 memset (&st, 0, sizeof (STATE));
840 if (!option (OPTTEXTFLOWED))
841 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix),
844 strfcpy (prefix, ">", sizeof (prefix));
847 st.flags = M_CHARCONV;
849 if (option (OPTWEED))
852 if (option (OPTHEADER))
853 include_header (1, fp, parent, tmpfp, prefix);
856 if (mutt_can_decode (cur)) {
857 mutt_body_handler (cur, &st);
858 state_putc ('\n', &st);
861 mutt_copy_body (fp, &tmphdr->content, cur);
864 for (i = 0; i < idxlen; i++) {
865 if (idx[i]->content->tagged && mutt_can_decode (idx[i]->content)) {
866 mutt_body_handler (idx[i]->content, &st);
867 state_putc ('\n', &st);
872 mutt_make_post_indent (Context, parent, tmpfp);
874 if (mime_reply_any && !cur &&
875 copy_problematic_attachments (fp, &tmphdr->content, idx, idxlen,
877 mutt_free_header (&tmphdr);
885 if (ci_send_message (flags, tmphdr, tmpbody, NULL, parent) == 0)
886 mutt_set_flag (Context, hdr, M_REPLIED, 1);