Rewrite `Lreply'++ series, support *!* for them and `mail'..
[s-mailx.git] / cmd-resend.c
blob46cf9506933d9205460c2af3d53bf2f3ae6d2ad9
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ All sorts of `reply', `resend', `forward', and similar user commands.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
35 #undef n_FILE
36 #define n_FILE cmd_resend
38 #ifndef HAVE_AMALGAMATION
39 # include "nail.h"
40 #endif
42 /* Modify subject we reply to to begin with Re: if it does not already */
43 static char *a_crese_reedit(char const *subj);
45 /* Fetch these headers, as appropriate */
46 static struct name *a_crese_reply_to(struct message *mp);
47 static struct name *a_crese_mail_followup_to(struct message *mp);
49 /* We honoured Reply-To: and/or Mail-Followup-To:, but *recipients-in-cc* is
50 * set so try to keep "secondary" addressees in Cc:, if possible, */
51 static void a_crese_polite_rt_mft_move(struct message *mp, struct header *hp,
52 struct name *np);
54 /* References and charset, as appropriate */
55 static void a_crese_make_ref_and_cs(struct message *mp, struct header *head);
57 /* `reply' and `Lreply' workhorse */
58 static int a_crese_list_reply(int *msgvec, enum header_flags hf);
60 /* Get PTF to implementation of command c (i.e., take care for *flipr*) */
61 static int (*a_crese_reply_or_Reply(char c))(int *, bool_t);
63 /* Reply to a single message. Extract each name from the message header and
64 * send them off to mail1() */
65 static int a_crese_reply(int *msgvec, bool_t recipient_record);
67 /* Reply to a series of messages by simply mailing to the senders and not
68 * messing around with the To: and Cc: lists as in normal reply */
69 static int a_crese_Reply(int *msgvec, bool_t recipient_record);
71 /* Forward a message to a new recipient, in the sense of RFC 2822 */
72 static int a_crese_fwd(char *str, int recipient_record);
74 /* Modify the subject we are replying to to begin with Fwd: */
75 static char *a_crese__fwdedit(char *subj);
77 /* Do the real work of resending */
78 static int a_crese_resend1(void *v, bool_t add_resent);
80 static char *
81 a_crese_reedit(char const *subj){
82 char *newsubj;
83 NYD2_ENTER;
85 newsubj = NULL;
87 if(subj != NULL && *subj != '\0'){
88 struct str in, out;
89 size_t i;
90 char const *cp;
92 in.l = strlen(in.s = n_UNCONST(subj));
93 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
95 i = strlen(cp = subject_re_trim(out.s)) +1;
96 /* RFC mandates english "Re: " */
97 newsubj = n_autorec_alloc(sizeof("Re: ") -1 + i);
98 memcpy(newsubj, "Re: ", sizeof("Re: ") -1);
99 memcpy(&newsubj[sizeof("Re: ") -1], cp, i);
101 free(out.s);
103 NYD2_LEAVE;
104 return newsubj;
107 static struct name *
108 a_crese_reply_to(struct message *mp){
109 char const *cp, *cp2;
110 struct name *rt, *np;
111 enum gfield gf;
112 NYD2_ENTER;
114 gf = ok_blook(fullnames) ? GFULL | GSKIN : GSKIN;
115 rt = NULL;
117 if((cp = ok_vlook(reply_to_honour)) != NULL &&
118 (cp2 = hfield1("reply-to", mp)) != NULL &&
119 (rt = checkaddrs(lextract(cp2, GTO | gf), EACM_STRICT, NULL)) != NULL){
120 char *sp;
121 size_t l;
122 char const *tr;
124 if(n_psonce & n_PSO_INTERACTIVE){
125 fprintf(n_stdout, _("Reply-To: header contains:"));
126 for(np = rt; np != NULL; np = np->n_flink)
127 fprintf(n_stdout, " %s", np->n_name);
128 putc('\n', n_stdout);
131 tr = _("Reply-To %s%s");
132 l = strlen(tr) + strlen(rt->n_name) + 3 +1;
133 sp = n_lofi_alloc(l);
135 snprintf(sp, l, tr, rt->n_name, (rt->n_flink != NULL ? "..." : n_empty));
136 if(quadify(cp, UIZ_MAX, sp, TRU1) <= FAL0)
137 rt = NULL;
139 n_lofi_free(sp);
141 NYD2_LEAVE;
142 return rt;
145 static struct name *
146 a_crese_mail_followup_to(struct message *mp){
147 char const *cp, *cp2;
148 struct name *mft, *np;
149 enum gfield gf;
150 NYD2_ENTER;
152 gf = ok_blook(fullnames) ? GFULL | GSKIN : GSKIN;
153 mft = NULL;
155 if((cp = ok_vlook(followup_to_honour)) != NULL &&
156 (cp2 = hfield1("mail-followup-to", mp)) != NULL &&
157 (mft = checkaddrs(lextract(cp2, GTO | gf), EACM_STRICT,NULL)) != NULL){
158 char *sp;
159 size_t l;
160 char const *tr;
162 if(n_psonce & n_PSO_INTERACTIVE){
163 fprintf(n_stdout, _("Mail-Followup-To: header contains:"));
164 for(np = mft; np != NULL; np = np->n_flink)
165 fprintf(n_stdout, " %s", np->n_name);
166 putc('\n', n_stdout);
169 tr = _("Followup-To %s%s");
170 l = strlen(tr) + strlen(mft->n_name) + 3 +1;
171 sp = n_lofi_alloc(l);
173 snprintf(sp, l, tr, mft->n_name, (mft->n_flink != NULL ? "..." : n_empty));
174 if(quadify(cp, UIZ_MAX, sp, TRU1) <= FAL0)
175 mft = NULL;
177 n_lofi_free(sp);
179 NYD2_LEAVE;
180 return mft;
183 static void
184 a_crese_polite_rt_mft_move(struct message *mp, struct header *hp,
185 struct name *np){
186 struct name *np_orig;
187 NYD2_ENTER;
188 n_UNUSED(mp);
190 np_orig = np;
192 if(np == hp->h_to)
193 hp->h_to = NULL;
194 if(np == hp->h_cc)
195 hp->h_cc = NULL;
197 while(np != NULL){
198 enum gfield gf;
199 struct name *nnp, **xpp, *xp;
201 nnp = np;
202 np = np->n_flink;
204 /* Try primary, then secondary. If this receiver came in only via R-T:
205 * or M-F-T:, place her/him/it in To: */
206 gf = GTO;
207 xpp = &hp->h_to;
208 for(xp = hp->h_mailx_orig_to; xp != NULL; xp = xp->n_flink)
209 if(!asccasecmp(xp->n_name, nnp->n_name))
210 goto jlink;
212 gf = GCC;
213 xpp = &hp->h_cc;
214 for(xp = hp->h_mailx_orig_cc; xp != NULL; xp = xp->n_flink)
215 if(!asccasecmp(xp->n_name, nnp->n_name))
216 goto jlink;
218 gf = GTO;
219 xpp = &hp->h_to;
220 jlink:
221 /* Link it at the end to not loose original sort order */
222 if((xp = *xpp) != NULL)
223 while(xp->n_flink != NULL)
224 xp = xp->n_flink;
226 if((nnp->n_blink = xp) != NULL)
227 xp->n_flink = nnp;
228 else
229 *xpp = nnp;
230 nnp->n_flink = NULL;
231 nnp->n_type = (nnp->n_type & ~GMASK) | gf;
234 /* If afterwards only Cc: data remains, upgrade all of it to To: */
235 if(hp->h_to == NULL){
236 hp->h_to = np = hp->h_cc;
237 hp->h_cc = NULL;
239 for(; np != NULL; np = np->n_flink)
240 np->n_type = (np->n_type & ~GMASK) | GTO;
242 NYD2_LEAVE;
245 static void
246 a_crese_make_ref_and_cs(struct message *mp, struct header *head) /* TODO ASAP */
248 char *oldref, *oldmsgid, *newref, *cp;
249 size_t oldreflen = 0, oldmsgidlen = 0, reflen;
250 unsigned i;
251 struct name *n;
252 NYD2_ENTER;
254 oldref = hfield1("references", mp);
255 oldmsgid = hfield1("message-id", mp);
256 if (oldmsgid == NULL || *oldmsgid == '\0') {
257 head->h_ref = NULL;
258 goto jleave;
261 reflen = 1;
262 if (oldref) {
263 oldreflen = strlen(oldref);
264 reflen += oldreflen + 2;
266 if (oldmsgid) {
267 oldmsgidlen = strlen(oldmsgid);
268 reflen += oldmsgidlen;
271 newref = smalloc(reflen);
272 if (oldref != NULL) {
273 memcpy(newref, oldref, oldreflen +1);
274 if (oldmsgid != NULL) {
275 newref[oldreflen++] = ',';
276 newref[oldreflen++] = ' ';
277 memcpy(newref + oldreflen, oldmsgid, oldmsgidlen +1);
279 } else if (oldmsgid)
280 memcpy(newref, oldmsgid, oldmsgidlen +1);
281 n = extract(newref, GREF);
282 free(newref);
284 /* Limit number of references TODO better on parser side */
285 while (n->n_flink != NULL)
286 n = n->n_flink;
287 for (i = 1; i <= REFERENCES_MAX; ++i) {
288 if (n->n_blink != NULL)
289 n = n->n_blink;
290 else
291 break;
293 n->n_blink = NULL;
294 head->h_ref = n;
295 if (ok_blook(reply_in_same_charset) &&
296 (cp = hfield1("content-type", mp)) != NULL){
297 if((head->h_charset = cp = mime_param_get("charset", cp)) != NULL){
298 char *cpo, c;
300 for(cpo = cp; (c = *cpo) != '\0'; ++cpo)
301 *cpo = lowerconv(c);
303 head->h_charset = n_charsetalias_expand(cp);
306 jleave:
307 NYD2_LEAVE;
310 static int
311 a_crese_list_reply(int *msgvec, enum header_flags hf){
312 struct header head;
313 struct message *mp;
314 char const *cp, *cp2;
315 enum gfield gf;
316 struct name *rt, *mft, *np, *rcv;
317 int *save_msgvec;
318 NYD2_ENTER;
320 n_pstate_err_no = n_ERR_NONE;
322 /* TODO Since we may recur and do stuff with message lists we need to save
323 * TODO away the argument vector as long as that isn't done by machinery */
324 /* C99 */{
325 size_t i;
326 for(i = 0; msgvec[i] != 0; ++i)
328 ++i;
329 save_msgvec = n_lofi_alloc(sizeof(*save_msgvec) * i);
330 while(i-- > 0)
331 save_msgvec[i] = msgvec[i];
332 msgvec = save_msgvec;
335 gf = ok_blook(fullnames) ? GFULL | GSKIN : GSKIN;
337 jwork_msg:
338 n_autorec_relax_create();
339 mp = &message[*msgvec - 1];
340 touch(mp);
341 setdot(mp);
343 memset(&head, 0, sizeof head);
344 head.h_flags = hf;
345 head.h_subject = a_crese_reedit(hfield1("subject", mp));
346 head.h_mailx_command = (hf & HF_LIST_REPLY) ? "Lreply" : "reply";
347 head.h_mailx_orig_from = lextract(hfield1("from", mp), GIDENT | gf);
348 head.h_mailx_orig_to = lextract(hfield1("to", mp), GTO | gf);
349 head.h_mailx_orig_cc = lextract(hfield1("cc", mp), GCC | gf);
350 head.h_mailx_orig_bcc = lextract(hfield1("bcc", mp), GBCC | gf);
352 /* First of all check for Reply-To: then Mail-Followup-To:, because these,
353 * if honoured, take precedence over anything else. We will join the
354 * resulting list together if so desired.
355 * So if we shall honour R-T: or M-F-T:, then these are our receivers! */
356 rt = a_crese_reply_to(mp);
357 mft = a_crese_mail_followup_to(mp);
359 if(rt != NULL || mft != NULL){
360 np = cat(rt, mft);
361 if(mft != NULL)
362 head.h_mft = namelist_dup(np, GTO | gf); /* xxx GTO: no "clone"! */
364 /* Optionally do not propagate a receiver that originally was in
365 * secondary Cc: to the primary To: list */
366 if(ok_blook(recipients_in_cc)){
367 a_crese_polite_rt_mft_move(mp, &head, np);
369 head.h_mailx_raw_cc = namelist_dup(head.h_cc, GCC | gf);
370 head.h_cc = n_alternates_remove(head.h_cc, FAL0);
371 }else
372 head.h_to = np;
374 head.h_mailx_raw_to = namelist_dup(head.h_to, GTO | gf);
375 head.h_to = n_alternates_remove(head.h_to, FAL0);
376 #ifdef HAVE_DEVEL
377 for(np = head.h_to; np != NULL; np = np->n_flink)
378 assert((np->n_type & GMASK) == GTO);
379 for(np = head.h_cc; np != NULL; np = np->n_flink)
380 assert((np->n_type & GMASK) == GCC);
381 #endif
382 goto jrecipients_done;
385 /* Otherwise do the normal From: / To: / Cc: dance */
387 if((cp2 = hfield1("from", mp)) == NULL)
388 cp2 = nameof(mp, 1);
390 /* Cc: */
391 np = NULL;
392 if(ok_blook(recipients_in_cc) && (cp = hfield1("to", mp)) != NULL)
393 np = lextract(cp, GCC | gf);
394 if((cp = hfield1("cc", mp)) != NULL){
395 struct name *x;
397 if((x = lextract(cp, GCC | gf)) != NULL)
398 np = cat(np, x);
400 if(np != NULL){
401 head.h_mailx_raw_cc = namelist_dup(np, GCC | gf);
402 head.h_cc = n_alternates_remove(np, FAL0);
405 /* To: */
406 np = NULL;
407 if(cp2 != NULL)
408 np = lextract(cp2, GTO | gf);
409 if(!ok_blook(recipients_in_cc) && (cp = hfield1("to", mp)) != NULL){
410 struct name *x;
412 if((x = lextract(cp, GTO | gf)) != NULL)
413 np = cat(np, x);
415 /* Delete my name from reply list, and with it, all my alternate names */
416 if(np != NULL){
417 head.h_mailx_raw_to = namelist_dup(np, GTO | gf);
418 np = n_alternates_remove(np, FAL0);
419 /* The user may have send this to himself, don't ignore that */
420 if(count(np) == 0){
421 np = lextract(cp2, GTO | gf);
422 head.h_mailx_raw_to = namelist_dup(np, GTO | gf);
425 head.h_to = np;
427 jrecipients_done:
429 /* For list replies we want to automatically recognize the list address
430 * given in the List-Post: header, so that we will not throw away a possible
431 * corresponding receiver: temporarily "`mlist' the List-Post: address" */
432 if((hf & HF_LIST_REPLY) && (cp = hfield1("list-post", mp)) != NULL){
433 struct name *x;
435 if((x = lextract(cp, GEXTRA | GSKIN)) == NULL || x->n_flink != NULL ||
436 (cp = url_mailto_to_address(x->n_name)) == NULL ||
437 /* XXX terribly wasteful to create a new name, and can't we find
438 * XXX a way to mitigate that?? */
439 is_addr_invalid(x = nalloc(cp, GEXTRA | GSKIN), EACM_STRICT)){
440 if(n_poption & n_PO_D_V)
441 n_err(_("Message contains invalid List-Post: header\n"));
442 }else{
443 /* A special case has been seen on e.g. ietf-announce@ietf.org:
444 * these usually post to multiple groups, with ietf-announce@
445 * in List-Post:, but with Reply-To: set to ietf@ietf.org (since
446 * -announce@ is only used for announcements, say).
447 * So our desire is to honour this request and actively overwrite
448 * List-Post: for our purpose; but only if its a single address.
449 * However, to avoid ambiguities with users that place themselve in
450 * Reply-To: and mailing lists which don't overwrite this (or only
451 * extend this, shall such exist), only do so if reply_to exists of
452 * a single address which points to the same domain as List-Post: */
453 if(rt != NULL && rt->n_flink == NULL &&
454 name_is_same_domain(x, rt))
455 cp = rt->n_name; /* rt is EACM_STRICT tested */
456 else
457 cp = x->n_name;
459 if(is_mlist(cp, FAL0) == MLIST_OTHER)
460 head.h_list_post = cp;
464 /* In case of list replies we actively sort out any non-list recipient */
465 if(hf & HF_LIST_REPLY){
466 struct name **nhpp, *nhp, *tail;
468 cp = head.h_list_post;
470 nhp = *(nhpp = &head.h_to);
471 head.h_to = NULL;
472 j_lt_redo:
473 for(tail = NULL; nhp != NULL;){
474 np = nhp;
475 nhp = nhp->n_flink;
477 if((cp != NULL && !asccasecmp(cp, np->n_name)) ||
478 is_mlist(np->n_name, FAL0) != MLIST_OTHER){
479 if((np->n_blink = tail) != NULL)
480 tail->n_flink = np;
481 else
482 *nhpp = np;
483 np->n_flink = NULL;
484 tail = np;
487 if(nhpp == &head.h_to){
488 nhp = *(nhpp = &head.h_cc);
489 head.h_cc = NULL;
490 goto j_lt_redo;
493 /* For `Lreply' only, fail immediately with DESTADDRREQ if there are no
494 * receivers at all! */
495 if(head.h_to == NULL && head.h_cc == NULL){
496 n_err(_("No recipients specified for `Lreply'\n"));
497 if(msgvec[1] == 0){
498 n_pstate_err_no = n_ERR_DESTADDRREQ;
499 msgvec = NULL;
500 goto jleave;
502 goto jskip_to_next;
506 /* Move Cc: to To: as appropriate! */
507 if(head.h_to == NULL && (np = head.h_cc) != NULL){
508 head.h_cc = NULL;
509 for(head.h_to = np; np != NULL; np = np->n_flink)
510 np->n_type = (np->n_type & ~GMASK) | GTO;
513 a_crese_make_ref_and_cs(mp, &head);
515 if(ok_blook(quote_as_attachment)){
516 head.h_attach = csalloc(1, sizeof *head.h_attach);
517 head.h_attach->a_msgno = *msgvec;
518 head.h_attach->a_content_description = _("Original message content");
521 if(mail1(&head, 1, mp, NULL, !!(hf & HF_RECIPIENT_RECORD), 0) != OKAY){
522 msgvec = NULL;
523 goto jleave;
525 if(ok_blook(markanswered) && !(mp->m_flag & MANSWERED))
526 mp->m_flag |= MANSWER | MANSWERED;
527 n_autorec_relax_gut();
529 jskip_to_next:
530 if(*++msgvec != 0){
531 /* TODO message (error) ring.., less sleep */
532 if(n_psonce & n_PSO_INTERACTIVE){
533 fprintf(n_stdout,
534 _("Waiting a second before proceeding to the next message..\n"));
535 fflush(n_stdout);
536 n_msleep(1000, FAL0);
538 goto jwork_msg;
541 jleave:
542 n_lofi_free(save_msgvec);
543 NYD2_LEAVE;
544 return (msgvec == NULL);
547 static int
548 (*a_crese_reply_or_Reply(char c))(int *, bool_t){
549 int (*rv)(int*, bool_t);
550 NYD2_ENTER;
552 rv = (ok_blook(flipr) ^ (c == 'R')) ? &a_crese_Reply : &a_crese_reply;
553 NYD2_LEAVE;
554 return rv;
557 static int
558 a_crese_reply(int *msgvec, bool_t recipient_record){
559 int rv;
560 NYD2_ENTER;
562 rv = a_crese_list_reply(msgvec,
563 (recipient_record ? HF_RECIPIENT_RECORD : HF_NONE));
564 NYD2_LEAVE;
565 return rv;
568 static int
569 a_crese_Reply(int *msgvec, bool_t recipient_record){
570 struct header head;
571 struct message *mp;
572 int *ap;
573 enum gfield gf;
574 NYD2_ENTER;
576 memset(&head, 0, sizeof head);
577 gf = ok_blook(fullnames) ? GFULL | GSKIN : GSKIN;
579 for(ap = msgvec; *ap != 0; ++ap){
580 struct name *np;
582 mp = &message[*ap - 1];
583 touch(mp);
584 setdot(mp);
586 if((np = a_crese_reply_to(mp)) == NULL){
587 char *cp;
589 if((cp = hfield1("from", mp)) == NULL)
590 cp = nameof(mp, 2);
591 np = lextract(cp, GTO | gf);
593 head.h_to = cat(head.h_to, np);
596 mp = &message[msgvec[0] - 1];
597 head.h_subject = hfield1("subject", mp);
598 head.h_subject = a_crese_reedit(head.h_subject);
599 a_crese_make_ref_and_cs(mp, &head);
600 head.h_mailx_command = "Reply";
601 head.h_mailx_orig_from = lextract(hfield1("from", mp), GIDENT | gf);
602 head.h_mailx_orig_to = lextract(hfield1("to", mp), GTO | gf);
603 head.h_mailx_orig_cc = lextract(hfield1("cc", mp), GCC | gf);
604 head.h_mailx_orig_bcc = lextract(hfield1("bcc", mp), GBCC | gf);
606 if(ok_blook(recipients_in_cc)){
607 a_crese_polite_rt_mft_move(mp, &head, head.h_to);
609 head.h_mailx_raw_cc = namelist_dup(head.h_cc, GCC | gf);
610 head.h_cc = n_alternates_remove(head.h_cc, FAL0);
612 head.h_mailx_raw_to = namelist_dup(head.h_to, GTO | gf);
613 head.h_to = n_alternates_remove(head.h_to, FAL0);
615 if(ok_blook(quote_as_attachment)){
616 head.h_attach = csalloc(1, sizeof *head.h_attach);
617 head.h_attach->a_msgno = *msgvec;
618 head.h_attach->a_content_description = _("Original message content");
621 if(mail1(&head, 1, mp, NULL, recipient_record, 0) != OKAY){
622 msgvec = NULL;
623 goto jleave;
626 if(ok_blook(markanswered) && !(mp->m_flag & MANSWERED))
627 mp->m_flag |= MANSWER | MANSWERED;
628 jleave:
629 NYD2_LEAVE;
630 return (msgvec == NULL);
633 static int
634 a_crese_fwd(char *str, int recipient_record){
635 struct header head;
636 struct message *mp;
637 enum gfield gf;
638 bool_t f, forward_as_attachment;
639 char *recipient;
640 int rv, *msgvec;
641 NYD2_ENTER;
643 rv = 1;
645 if((recipient = laststring(str, &f, TRU1)) == NULL){
646 n_err(_("No recipient specified.\n"));
647 n_pstate_err_no = n_ERR_DESTADDRREQ;
648 goto jleave;
651 forward_as_attachment = ok_blook(forward_as_attachment);
652 gf = ok_blook(fullnames) ? GFULL | GSKIN : GSKIN;
653 msgvec = n_autorec_alloc((msgCount + 2) * sizeof *msgvec);
655 n_pstate_err_no = n_ERR_NODATA;
656 if(!f){
657 *msgvec = first(0, MMNORM);
658 if(*msgvec != 0)
659 msgvec[1] = 0;
660 }else if(getmsglist(str, msgvec, 0) < 0)
661 goto jleave;
663 if(*msgvec == 0){
664 n_err(_("No applicable messages.\n"));
665 goto jleave;
667 if(msgvec[1] != 0){
668 n_err(_("Cannot forward multiple messages at once\n"));
669 n_pstate_err_no = n_ERR_NOTSUP;
670 goto jleave;
673 memset(&head, 0, sizeof head);
674 head.h_to = lextract(recipient,
675 (GTO | (ok_blook(fullnames) ? GFULL : GSKIN)));
677 mp = &message[*msgvec - 1];
678 touch(mp);
679 setdot(mp);
680 head.h_subject = hfield1("subject", mp);
681 head.h_subject = a_crese__fwdedit(head.h_subject);
682 head.h_mailx_command = "forward";
683 head.h_mailx_raw_to = namelist_dup(head.h_to, GTO | gf);
684 head.h_mailx_orig_from = lextract(hfield1("from", mp), GIDENT | gf);
685 head.h_mailx_orig_to = lextract(hfield1("to", mp), GTO | gf);
686 head.h_mailx_orig_cc = lextract(hfield1("cc", mp), GCC | gf);
687 head.h_mailx_orig_bcc = lextract(hfield1("bcc", mp), GBCC | gf);
689 if(forward_as_attachment){
690 head.h_attach = csalloc(1, sizeof *head.h_attach);
691 head.h_attach->a_msgno = *msgvec;
692 head.h_attach->a_content_description = _("Forwarded message");
695 rv = (mail1(&head, 1, (forward_as_attachment ? NULL : mp), NULL,
696 recipient_record, 1) != OKAY); /* reverse! */
697 jleave:
698 NYD2_LEAVE;
699 return rv;
702 static char *
703 a_crese__fwdedit(char *subj){
704 struct str in, out;
705 char *newsubj;
706 NYD2_ENTER;
708 newsubj = NULL;
710 if(subj == NULL || *subj == '\0')
711 goto jleave;
713 in.s = subj;
714 in.l = strlen(subj);
715 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
717 newsubj = n_autorec_alloc(out.l + 6);
718 if(!ascncasecmp(out.s, "Fwd: ", sizeof("Fwd: ") -1)) /* TODO EXTEND SUPP.. */
719 memcpy(newsubj, out.s, out.l +1);
720 else{
721 memcpy(newsubj, "Fwd: ", 5); /* TODO ..a la subject_re_trim()! */
722 memcpy(&newsubj[5], out.s, out.l +1);
725 free(out.s);
726 jleave:
727 NYD2_LEAVE;
728 return newsubj;
731 static int
732 a_crese_resend1(void *vp, bool_t add_resent){
733 struct header head;
734 struct name *myto, *myrawto;
735 enum gfield gf;
736 char *name, *str;
737 int *ip, *msgvec;
738 bool_t fail;
739 NYD2_ENTER;
741 fail = TRU1;
743 str = vp;
744 msgvec = n_autorec_alloc((msgCount + 2) * sizeof *msgvec);
745 name = laststring(str, &fail, TRU1);
746 if(name == NULL){
747 n_err(_("No recipient specified.\n"));
748 n_pstate_err_no = n_ERR_DESTADDRREQ;
749 goto jleave;
752 n_pstate_err_no = n_ERR_NODATA;
754 if(!fail){
755 *msgvec = first(0, MMNORM);
756 if(*msgvec != 0)
757 msgvec[1] = 0;
758 }else if(getmsglist(str, msgvec, 0) < 0)
759 goto jleave;
761 if(*msgvec == 0){
762 n_err(_("No applicable messages.\n"));
763 goto jleave;
766 fail = TRU1;
767 gf = ok_blook(fullnames) ? GFULL | GSKIN : GSKIN;
769 myrawto = nalloc(name, GTO | gf);
770 myto = usermap(namelist_dup(myrawto, myrawto->n_type), FAL0);
771 myto = n_alternates_remove(myto, TRU1);
772 if(myto == NULL){
773 n_pstate_err_no = n_ERR_DESTADDRREQ;
774 goto jleave;
777 n_autorec_relax_create();
778 for(ip = msgvec; *ip != 0 && UICMP(z, PTR2SIZE(ip - msgvec), <, msgCount);
779 ++ip){
780 struct message *mp;
782 mp = &message[*ip - 1];
783 touch(mp);
784 setdot(mp);
786 memset(&head, 0, sizeof head);
787 head.h_to = myto;
788 head.h_mailx_command = "resend";
789 head.h_mailx_raw_to = myrawto;
790 head.h_mailx_orig_from = lextract(hfield1("from", mp), GIDENT | gf);
791 head.h_mailx_orig_to = lextract(hfield1("to", mp), GTO | gf);
792 head.h_mailx_orig_cc = lextract(hfield1("cc", mp), GCC | gf);
793 head.h_mailx_orig_bcc = lextract(hfield1("bcc", mp), GBCC | gf);
795 if(resend_msg(mp, &head, add_resent) != OKAY){
796 /* n_autorec_relax_gut(); XXX but is handled automatically? */
797 goto jleave;
799 n_autorec_relax_unroll();
801 n_autorec_relax_gut();
803 fail = FAL0;
804 n_pstate_err_no = n_ERR_NONE;
805 jleave:
806 NYD2_LEAVE;
807 return (fail != FAL0);
810 FL int
811 c_reply(void *vp){
812 int rv;
813 NYD_ENTER;
815 rv = (*a_crese_reply_or_Reply('r'))(vp, FAL0);
816 NYD_LEAVE;
817 return rv;
820 FL int
821 c_replyall(void *vp){
822 int rv;
823 NYD_ENTER;
825 rv = a_crese_reply(vp, FAL0);
826 NYD_LEAVE;
827 return rv;
830 FL int
831 c_replysender(void *vp){
832 int rv;
833 NYD_ENTER;
835 rv = a_crese_Reply(vp, FAL0);
836 NYD_LEAVE;
837 return rv;
840 FL int
841 c_Reply(void *vp){
842 int rv;
843 NYD_ENTER;
845 rv = (*a_crese_reply_or_Reply('R'))(vp, FAL0);
846 NYD_LEAVE;
847 return rv;
850 FL int
851 c_Lreply(void *vp){
852 int rv;
853 NYD_ENTER;
855 rv = a_crese_list_reply(vp, HF_LIST_REPLY);
856 NYD_LEAVE;
857 return rv;
860 FL int
861 c_followup(void *vp){
862 int rv;
863 NYD_ENTER;
865 rv = (*a_crese_reply_or_Reply('r'))(vp, TRU1);
866 NYD_LEAVE;
867 return rv;
870 FL int
871 c_followupall(void *vp){
872 int rv;
873 NYD_ENTER;
875 rv = a_crese_reply(vp, TRU1);
876 NYD_LEAVE;
877 return rv;
880 FL int
881 c_followupsender(void *vp){
882 int rv;
883 NYD_ENTER;
885 rv = a_crese_Reply(vp, TRU1);
886 NYD_LEAVE;
887 return rv;
890 FL int
891 c_Followup(void *vp){
892 int rv;
893 NYD_ENTER;
895 rv = (*a_crese_reply_or_Reply('R'))(vp, TRU1);
896 NYD_LEAVE;
897 return rv;
900 FL int
901 c_forward(void *vp){
902 int rv;
903 NYD_ENTER;
905 rv = a_crese_fwd(vp, 0);
906 NYD_LEAVE;
907 return rv;
910 FL int
911 c_Forward(void *vp){
912 int rv;
913 NYD_ENTER;
915 rv = a_crese_fwd(vp, 1);
916 NYD_LEAVE;
917 return rv;
920 FL int
921 c_resend(void *vp){
922 int rv;
923 NYD_ENTER;
925 rv = a_crese_resend1(vp, TRU1);
926 NYD_LEAVE;
927 return rv;
930 FL int
931 c_Resend(void *vp){
932 int rv;
933 NYD_ENTER;
935 rv = a_crese_resend1(vp, FAL0);
936 NYD_LEAVE;
937 return rv;
940 /* s-it-mode */