README: review
[s-mailx.git] / cmd-resend.c
blobd291672bc93a8d2a702662135e3b9742e70576c9
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 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
6 * SPDX-License-Identifier: BSD-3-Clause
7 */
8 /*
9 * Copyright (c) 1980, 1993
10 * The Regents of the University of California. All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
36 #undef n_FILE
37 #define n_FILE cmd_resend
39 #ifndef HAVE_AMALGAMATION
40 # include "nail.h"
41 #endif
43 /* Modify subject we reply to to begin with Re: if it does not already */
44 static char *a_crese_reedit(char const *subj);
46 /* Fetch these headers, as appropriate */
47 static struct name *a_crese_reply_to(struct message *mp);
48 static struct name *a_crese_mail_followup_to(struct message *mp);
50 /* We honoured Reply-To: and/or Mail-Followup-To:, but *recipients-in-cc* is
51 * set so try to keep "secondary" addressees in Cc:, if possible, */
52 static void a_crese_polite_rt_mft_move(struct message *mp, struct header *hp,
53 struct name *np);
55 /* References and charset, as appropriate */
56 static void a_crese_make_ref_and_cs(struct message *mp, struct header *head);
58 /* `reply' and `Lreply' workhorse */
59 static int a_crese_list_reply(int *msgvec, enum header_flags hf);
61 /* Get PTF to implementation of command c (i.e., take care for *flipr*) */
62 static int (*a_crese_reply_or_Reply(char c))(int *, bool_t);
64 /* Reply to a single message. Extract each name from the message header and
65 * send them off to mail1() */
66 static int a_crese_reply(int *msgvec, bool_t recipient_record);
68 /* Reply to a series of messages by simply mailing to the senders and not
69 * messing around with the To: and Cc: lists as in normal reply */
70 static int a_crese_Reply(int *msgvec, bool_t recipient_record);
72 /* Forward a message to a new recipient, in the sense of RFC 2822 */
73 static int a_crese_fwd(void *vp, bool_t recipient_record);
75 /* Modify the subject we are replying to to begin with Fwd: */
76 static char *a_crese__fwdedit(char *subj);
78 /* Do the real work of resending */
79 static int a_crese_resend1(void *v, bool_t add_resent);
81 static char *
82 a_crese_reedit(char const *subj){
83 char *newsubj;
84 NYD2_ENTER;
86 newsubj = NULL;
88 if(subj != NULL && *subj != '\0'){
89 struct str in, out;
90 size_t i;
91 char const *cp;
93 in.l = strlen(in.s = n_UNCONST(subj));
94 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
96 i = strlen(cp = subject_re_trim(out.s)) +1;
97 /* RFC mandates english "Re: " */
98 newsubj = n_autorec_alloc(sizeof("Re: ") -1 + i);
99 memcpy(newsubj, "Re: ", sizeof("Re: ") -1);
100 memcpy(&newsubj[sizeof("Re: ") -1], cp, i);
102 n_free(out.s);
104 NYD2_LEAVE;
105 return newsubj;
108 static struct name *
109 a_crese_reply_to(struct message *mp){
110 char const *cp, *cp2;
111 struct name *rt, *np;
112 enum gfield gf;
113 NYD2_ENTER;
115 gf = ok_blook(fullnames) ? GFULL | GSKIN : GSKIN;
116 rt = NULL;
118 if((cp = ok_vlook(reply_to_honour)) != NULL &&
119 (cp2 = hfield1("reply-to", mp)) != NULL &&
120 (rt = checkaddrs(lextract(cp2, GTO | gf), EACM_STRICT, NULL)
121 ) != NULL){
122 char *sp;
123 size_t l;
124 char const *tr;
126 if((n_psonce & n_PSO_INTERACTIVE) && !(n_pstate & n_PS_ROBOT)){
127 fprintf(n_stdout, _("Reply-To: header contains:"));
128 for(np = rt; np != NULL; np = np->n_flink)
129 fprintf(n_stdout, " %s", np->n_name);
130 putc('\n', n_stdout);
133 tr = _("Reply-To %s%s");
134 l = strlen(tr) + strlen(rt->n_name) + 3 +1;
135 sp = n_lofi_alloc(l);
137 snprintf(sp, l, tr, rt->n_name, (rt->n_flink != NULL ? "..." : n_empty));
138 if(n_quadify(cp, UIZ_MAX, sp, TRU1) <= FAL0)
139 rt = NULL;
141 n_lofi_free(sp);
143 NYD2_LEAVE;
144 return rt;
147 static struct name *
148 a_crese_mail_followup_to(struct message *mp){
149 char const *cp, *cp2;
150 struct name *mft, *np;
151 enum gfield gf;
152 NYD2_ENTER;
154 gf = ok_blook(fullnames) ? GFULL | GSKIN : GSKIN;
155 mft = NULL;
157 if((cp = ok_vlook(followup_to_honour)) != NULL &&
158 (cp2 = hfield1("mail-followup-to", mp)) != NULL &&
159 (mft = checkaddrs(lextract(cp2, GTO | gf), EACM_STRICT, NULL)
160 ) != NULL){
161 char *sp;
162 size_t l;
163 char const *tr;
165 if((n_psonce & n_PSO_INTERACTIVE) && !(n_pstate & n_PS_ROBOT)){
166 fprintf(n_stdout, _("Mail-Followup-To: header contains:"));
167 for(np = mft; np != NULL; np = np->n_flink)
168 fprintf(n_stdout, " %s", np->n_name);
169 putc('\n', n_stdout);
172 tr = _("Followup-To %s%s");
173 l = strlen(tr) + strlen(mft->n_name) + 3 +1;
174 sp = n_lofi_alloc(l);
176 snprintf(sp, l, tr, mft->n_name,
177 (mft->n_flink != NULL ? "..." : n_empty));
178 if(n_quadify(cp, UIZ_MAX, sp, TRU1) <= FAL0)
179 mft = NULL;
181 n_lofi_free(sp);
183 NYD2_LEAVE;
184 return mft;
187 static void
188 a_crese_polite_rt_mft_move(struct message *mp, struct header *hp,
189 struct name *np){
190 bool_t once;
191 NYD2_ENTER;
192 n_UNUSED(mp);
194 if(np == hp->h_to)
195 hp->h_to = NULL;
196 if(np == hp->h_cc)
197 hp->h_cc = NULL;
199 /* We may find that in the end To: is empty but Cc: is not, in which case we
200 * upgrade Cc: to To: and jump back and redo the thing slightly different */
201 once = FAL0;
202 jredo:
203 while(np != NULL){
204 enum gfield gf;
205 struct name *nnp, **xpp, *xp;
207 nnp = np;
208 np = np->n_flink;
210 if(once){
211 gf = GTO;
212 xpp = &hp->h_to;
213 }else{
214 gf = GCC;
215 xpp = &hp->h_cc;
218 /* Try primary, then secondary */
219 for(xp = hp->h_mailx_orig_to; xp != NULL; xp = xp->n_flink)
220 if(!asccasecmp(xp->n_name, nnp->n_name))
221 goto jlink;
223 if(once){
224 gf = GCC;
225 xpp = &hp->h_cc;
228 for(xp = hp->h_mailx_orig_cc; xp != NULL; xp = xp->n_flink)
229 if(!asccasecmp(xp->n_name, nnp->n_name))
230 goto jlink;
232 /* If this receiver came in only via R-T: or M-F-T:, place her/him/it in
233 * To: due to lack of a better place */
234 gf = GTO;
235 xpp = &hp->h_to;
236 jlink:
237 /* Link it at the end to not loose original sort order */
238 if((xp = *xpp) != NULL)
239 while(xp->n_flink != NULL)
240 xp = xp->n_flink;
242 if((nnp->n_blink = xp) != NULL)
243 xp->n_flink = nnp;
244 else
245 *xpp = nnp;
246 nnp->n_flink = NULL;
247 nnp->n_type = (nnp->n_type & ~GMASK) | gf;
250 /* If afterwards only Cc: data remains, upgrade all of it to To: */
251 if(hp->h_to == NULL){
252 np = hp->h_cc;
253 hp->h_cc = NULL;
254 if(!once){
255 hp->h_to = NULL;
256 once = TRU1;
257 goto jredo;
258 }else
259 for(hp->h_to = np; np != NULL; np = np->n_flink)
260 np->n_type = (np->n_type & ~GMASK) | GTO;
262 NYD2_LEAVE;
265 static void
266 a_crese_make_ref_and_cs(struct message *mp, struct header *head) /* TODO ASAP*/
268 char const *ccp;
269 char *oldref, *oldmsgid, *newref;
270 size_t oldreflen = 0, oldmsgidlen = 0, reflen;
271 unsigned i;
272 struct name *n;
273 NYD2_ENTER;
275 oldref = hfield1("references", mp);
276 oldmsgid = hfield1("message-id", mp);
277 if (oldmsgid == NULL || *oldmsgid == '\0') {
278 head->h_ref = NULL;
279 goto jleave;
282 reflen = 1;
283 if (oldref) {
284 oldreflen = strlen(oldref);
285 reflen += oldreflen + 2;
287 if (oldmsgid) {
288 oldmsgidlen = strlen(oldmsgid);
289 reflen += oldmsgidlen;
292 newref = n_alloc(reflen);
293 if (oldref != NULL) {
294 memcpy(newref, oldref, oldreflen +1);
295 if (oldmsgid != NULL) {
296 newref[oldreflen++] = ',';
297 newref[oldreflen++] = ' ';
298 memcpy(newref + oldreflen, oldmsgid, oldmsgidlen +1);
300 } else if (oldmsgid)
301 memcpy(newref, oldmsgid, oldmsgidlen +1);
302 n = extract(newref, GREF);
303 n_free(newref);
305 /* Limit number of references TODO better on parser side */
306 while (n->n_flink != NULL)
307 n = n->n_flink;
308 for (i = 1; i <= REFERENCES_MAX; ++i) {
309 if (n->n_blink != NULL)
310 n = n->n_blink;
311 else
312 break;
314 n->n_blink = NULL;
315 head->h_ref = n;
316 if (ok_blook(reply_in_same_charset) &&
317 (ccp = hfield1("content-type", mp)) != NULL){
318 if((head->h_charset = ccp = mime_param_get("charset", ccp)) != NULL){
319 if((ccp = n_iconv_normalize_name(ccp)) != NULL)
320 ccp = n_charsetalias_expand(ccp);
321 head->h_charset = ccp;
324 jleave:
325 NYD2_LEAVE;
328 static int
329 a_crese_list_reply(int *msgvec, enum header_flags hf){
330 struct header head;
331 struct message *mp;
332 char const *cp, *cp2;
333 enum gfield gf;
334 struct name *rt, *mft, *np;
335 NYD2_ENTER;
337 n_pstate_err_no = n_ERR_NONE;
339 gf = ok_blook(fullnames) ? GFULL | GSKIN : GSKIN;
341 jwork_msg:
342 n_autorec_relax_create();
343 mp = &message[*msgvec - 1];
344 touch(mp);
345 setdot(mp);
347 memset(&head, 0, sizeof head);
348 head.h_flags = hf;
349 head.h_subject = a_crese_reedit(hfield1("subject", mp));
350 head.h_mailx_command = (hf & HF_LIST_REPLY) ? "Lreply" : "reply";
351 head.h_mailx_orig_from = lextract(hfield1("from", mp), GIDENT | gf);
352 head.h_mailx_orig_to = lextract(hfield1("to", mp), GTO | gf);
353 head.h_mailx_orig_cc = lextract(hfield1("cc", mp), GCC | gf);
354 head.h_mailx_orig_bcc = lextract(hfield1("bcc", mp), GBCC | gf);
356 /* First of all check for Reply-To: then Mail-Followup-To:, because these,
357 * if honoured, take precedence over anything else. We will join the
358 * resulting list together if so desired.
359 * So if we shall honour R-T: or M-F-T:, then these are our receivers! */
360 rt = a_crese_reply_to(mp);
361 mft = a_crese_mail_followup_to(mp);
363 if(rt != NULL || mft != NULL){
364 np = cat(rt, mft);
365 if(mft != NULL)
366 head.h_mft = n_namelist_dup(np, GTO | gf); /* xxx GTO: no "clone"! */
368 /* Optionally do not propagate a receiver that originally was in
369 * secondary Cc: to the primary To: list */
370 if(ok_blook(recipients_in_cc)){
371 a_crese_polite_rt_mft_move(mp, &head, np);
373 head.h_mailx_raw_cc = n_namelist_dup(head.h_cc, GCC | gf);
374 head.h_cc = n_alternates_remove(head.h_cc, FAL0);
375 }else
376 head.h_to = np;
378 head.h_mailx_raw_to = n_namelist_dup(head.h_to, GTO | gf);
379 head.h_to = n_alternates_remove(head.h_to, FAL0);
380 #ifdef HAVE_DEVEL
381 for(np = head.h_to; np != NULL; np = np->n_flink)
382 assert((np->n_type & GMASK) == GTO);
383 for(np = head.h_cc; np != NULL; np = np->n_flink)
384 assert((np->n_type & GMASK) == GCC);
385 #endif
386 goto jrecipients_done;
389 /* Otherwise do the normal From: / To: / Cc: dance */
391 cp2 = n_header_senderfield_of(mp);
393 /* Cc: */
394 np = NULL;
395 if(ok_blook(recipients_in_cc) && (cp = hfield1("to", mp)) != NULL)
396 np = lextract(cp, GCC | gf);
397 if((cp = hfield1("cc", mp)) != NULL){
398 struct name *x;
400 if((x = lextract(cp, GCC | gf)) != NULL)
401 np = cat(np, x);
403 if(np != NULL){
404 head.h_mailx_raw_cc = n_namelist_dup(np, GCC | gf);
405 head.h_cc = n_alternates_remove(np, FAL0);
408 /* To: */
409 np = NULL;
410 if(cp2 != NULL)
411 np = lextract(cp2, GTO | gf);
412 if(!ok_blook(recipients_in_cc) && (cp = hfield1("to", mp)) != NULL){
413 struct name *x;
415 if((x = lextract(cp, GTO | gf)) != NULL)
416 np = cat(np, x);
418 /* Delete my name from reply list, and with it, all my alternate names */
419 if(np != NULL){
420 head.h_mailx_raw_to = n_namelist_dup(np, GTO | gf);
421 np = n_alternates_remove(np, FAL0);
422 /* The user may have send this to himself, don't ignore that */
423 if(count(np) == 0){
424 np = lextract(cp2, GTO | gf);
425 head.h_mailx_raw_to = n_namelist_dup(np, GTO | gf);
428 head.h_to = np;
430 jrecipients_done:
432 /* For list replies we want to automatically recognize the list address
433 * given in the List-Post: header, so that we will not throw away a possible
434 * corresponding receiver: temporarily "`mlist' the List-Post: address" */
435 if((hf & HF_LIST_REPLY) && (cp = hfield1("list-post", mp)) != NULL){
436 struct name *x;
438 if((x = lextract(cp, GEXTRA | GSKIN)) == NULL || x->n_flink != NULL ||
439 (cp = url_mailto_to_address(x->n_name)) == NULL ||
440 /* XXX terribly wasteful to create a new name, and can't we find
441 * XXX a way to mitigate that?? */
442 is_addr_invalid(x = nalloc(cp, GEXTRA | GSKIN), EACM_STRICT)){
443 if(n_poption & n_PO_D_V)
444 n_err(_("Message contains invalid List-Post: header\n"));
445 }else{
446 /* A special case has been seen on e.g. ietf-announce@ietf.org:
447 * these usually post to multiple groups, with ietf-announce@
448 * in List-Post:, but with Reply-To: set to ietf@ietf.org (since
449 * -announce@ is only used for announcements, say).
450 * So our desire is to honour this request and actively overwrite
451 * List-Post: for our purpose; but only if its a single address.
452 * However, to avoid ambiguities with users that place themselve in
453 * Reply-To: and mailing lists which don't overwrite this (or only
454 * extend this, shall such exist), only do so if reply_to exists of
455 * a single address which points to the same domain as List-Post: */
456 if(rt != NULL && rt->n_flink == NULL &&
457 name_is_same_domain(x, rt))
458 cp = rt->n_name; /* rt is EACM_STRICT tested */
459 else
460 cp = x->n_name;
462 /* XXX is_mlist_mp()?? */
463 if(is_mlist(cp, FAL0) == MLIST_OTHER)
464 head.h_list_post = cp;
468 /* In case of list replies we actively sort out any non-list recipient */
469 if(hf & HF_LIST_REPLY){
470 struct name **nhpp, *nhp, *tail;
472 cp = head.h_list_post;
474 nhp = *(nhpp = &head.h_to);
475 head.h_to = NULL;
476 j_lt_redo:
477 for(tail = NULL; nhp != NULL;){
478 np = nhp;
479 nhp = nhp->n_flink;
481 /* XXX is_mlist_mp()?? */
482 if((cp != NULL && !asccasecmp(cp, np->n_name)) ||
483 is_mlist(np->n_name, FAL0) != MLIST_OTHER){
484 if((np->n_blink = tail) != NULL)
485 tail->n_flink = np;
486 else
487 *nhpp = np;
488 np->n_flink = NULL;
489 tail = np;
492 if(nhpp == &head.h_to){
493 nhp = *(nhpp = &head.h_cc);
494 head.h_cc = NULL;
495 goto j_lt_redo;
498 /* For `Lreply' only, fail immediately with DESTADDRREQ if there are no
499 * receivers at all! */
500 if(head.h_to == NULL && head.h_cc == NULL){
501 n_err(_("No recipients specified for `Lreply'\n"));
502 if(msgvec[1] == 0){
503 n_pstate_err_no = n_ERR_DESTADDRREQ;
504 msgvec = NULL;
505 goto jleave;
507 goto jskip_to_next;
511 /* Move Cc: to To: as appropriate! */
512 if(head.h_to == NULL && (np = head.h_cc) != NULL){
513 head.h_cc = NULL;
514 for(head.h_to = np; np != NULL; np = np->n_flink)
515 np->n_type = (np->n_type & ~GMASK) | GTO;
518 a_crese_make_ref_and_cs(mp, &head);
520 if(n_mail1((n_MAILSEND_HEADERS_PRINT |
521 (hf & HF_RECIPIENT_RECORD ? n_MAILSEND_RECORD_RECIPIENT : 0)),
522 &head, mp, NULL) != OKAY){
523 msgvec = NULL;
524 goto jleave;
526 if(ok_blook(markanswered) && !(mp->m_flag & MANSWERED))
527 mp->m_flag |= MANSWER | MANSWERED;
528 n_autorec_relax_gut();
530 jskip_to_next:
531 if(*++msgvec != 0){
532 /* TODO message (error) ring.., less sleep */
533 if(n_psonce & n_PSO_INTERACTIVE){
534 fprintf(n_stdout,
535 _("Waiting a second before proceeding to the next message..\n"));
536 fflush(n_stdout);
537 n_msleep(1000, FAL0);
539 goto jwork_msg;
542 jleave:
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 np = lextract(n_header_senderfield_of(mp), GTO | gf);
588 head.h_to = cat(head.h_to, np);
591 mp = &message[msgvec[0] - 1];
592 head.h_subject = hfield1("subject", mp);
593 head.h_subject = a_crese_reedit(head.h_subject);
594 a_crese_make_ref_and_cs(mp, &head);
595 head.h_mailx_command = "Reply";
596 head.h_mailx_orig_from = lextract(hfield1("from", mp), GIDENT | gf);
597 head.h_mailx_orig_to = lextract(hfield1("to", mp), GTO | gf);
598 head.h_mailx_orig_cc = lextract(hfield1("cc", mp), GCC | gf);
599 head.h_mailx_orig_bcc = lextract(hfield1("bcc", mp), GBCC | gf);
601 if(ok_blook(recipients_in_cc)){
602 a_crese_polite_rt_mft_move(mp, &head, head.h_to);
604 head.h_mailx_raw_cc = n_namelist_dup(head.h_cc, GCC | gf);
605 head.h_cc = n_alternates_remove(head.h_cc, FAL0);
607 head.h_mailx_raw_to = n_namelist_dup(head.h_to, GTO | gf);
608 head.h_to = n_alternates_remove(head.h_to, FAL0);
610 if(ok_blook(quote_as_attachment)){
611 head.h_attach = n_autorec_calloc(1, sizeof *head.h_attach);
612 head.h_attach->a_msgno = *msgvec;
613 head.h_attach->a_content_description = _("Original message content");
616 if(n_mail1(((recipient_record ? n_MAILSEND_RECORD_RECIPIENT : 0) |
617 n_MAILSEND_HEADERS_PRINT), &head, mp, NULL) != OKAY){
618 msgvec = NULL;
619 goto jleave;
622 if(ok_blook(markanswered) && !(mp->m_flag & MANSWERED))
623 mp->m_flag |= MANSWER | MANSWERED;
624 jleave:
625 NYD2_LEAVE;
626 return (msgvec == NULL);
629 static int
630 a_crese_fwd(void *vp, bool_t recipient_record){
631 struct header head;
632 struct message *mp;
633 enum gfield gf;
634 bool_t forward_as_attachment;
635 int *msgvec, rv;
636 struct n_cmd_arg *cap;
637 struct n_cmd_arg_ctx *cacp;
638 NYD2_ENTER;
640 cacp = vp;
641 cap = cacp->cac_arg;
642 msgvec = cap->ca_arg.ca_msglist;
643 cap = cap->ca_next;
644 rv = 1;
646 if(cap->ca_arg.ca_str.s[0] == '\0'){
647 if(!(n_pstate & (n_PS_HOOK_MASK | n_PS_ROBOT)) || (n_poption & n_PO_D_V))
648 n_err(_("No recipient specified.\n"));
649 n_pstate_err_no = n_ERR_DESTADDRREQ;
650 goto jleave;
653 forward_as_attachment = ok_blook(forward_as_attachment);
654 gf = ok_blook(fullnames) ? GFULL | GSKIN : GSKIN;
656 memset(&head, 0, sizeof head);
657 head.h_to = lextract(cap->ca_arg.ca_str.s,
658 (GTO | (ok_blook(fullnames) ? GFULL : GSKIN)));
660 mp = &message[*msgvec - 1];
661 touch(mp);
662 setdot(mp);
663 head.h_subject = hfield1("subject", mp);
664 head.h_subject = a_crese__fwdedit(head.h_subject);
665 head.h_mailx_command = "forward";
666 head.h_mailx_raw_to = n_namelist_dup(head.h_to, GTO | gf);
667 head.h_mailx_orig_from = lextract(hfield1("from", mp), GIDENT | gf);
668 head.h_mailx_orig_to = lextract(hfield1("to", mp), GTO | gf);
669 head.h_mailx_orig_cc = lextract(hfield1("cc", mp), GCC | gf);
670 head.h_mailx_orig_bcc = lextract(hfield1("bcc", mp), GBCC | gf);
672 if(forward_as_attachment){
673 head.h_attach = n_autorec_calloc(1, sizeof *head.h_attach);
674 head.h_attach->a_msgno = *msgvec;
675 head.h_attach->a_content_description = _("Forwarded message");
678 rv = (n_mail1((n_MAILSEND_IS_FWD |
679 (recipient_record ? n_MAILSEND_RECORD_RECIPIENT : 0) |
680 n_MAILSEND_HEADERS_PRINT), &head,
681 (forward_as_attachment ? NULL : mp), NULL) != OKAY); /* reverse! */
682 jleave:
683 NYD2_LEAVE;
684 return rv;
687 static char *
688 a_crese__fwdedit(char *subj){
689 struct str in, out;
690 char *newsubj;
691 NYD2_ENTER;
693 newsubj = NULL;
695 if(subj == NULL || *subj == '\0')
696 goto jleave;
698 in.s = subj;
699 in.l = strlen(subj);
700 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
702 newsubj = n_autorec_alloc(out.l + 6);
703 if(!ascncasecmp(out.s, "Fwd: ", sizeof("Fwd: ") -1)) /* TODO EXTEND SUPP. */
704 memcpy(newsubj, out.s, out.l +1);
705 else{
706 memcpy(newsubj, "Fwd: ", 5); /* TODO ..a la subject_re_trim()! */
707 memcpy(&newsubj[5], out.s, out.l +1);
710 n_free(out.s);
711 jleave:
712 NYD2_LEAVE;
713 return newsubj;
716 static int
717 a_crese_resend1(void *vp, bool_t add_resent){
718 struct header head;
719 struct name *myto, *myrawto;
720 enum gfield gf;
721 int *msgvec, rv, *ip;
722 struct n_cmd_arg *cap;
723 struct n_cmd_arg_ctx *cacp;
724 NYD2_ENTER;
726 cacp = vp;
727 cap = cacp->cac_arg;
728 msgvec = cap->ca_arg.ca_msglist;
729 cap = cap->ca_next;
730 rv = 1;
731 n_pstate_err_no = n_ERR_DESTADDRREQ;
733 if(cap->ca_arg.ca_str.s[0] == '\0'){
734 if(!(n_pstate & (n_PS_HOOK_MASK | n_PS_ROBOT)) || (n_poption & n_PO_D_V))
735 n_err(_("No recipient specified.\n"));
736 goto jleave;
739 gf = ok_blook(fullnames) ? GFULL | GSKIN : GSKIN;
741 myrawto = nalloc(cap->ca_arg.ca_str.s, GTO | gf);
742 myto = usermap(n_namelist_dup(myrawto, myrawto->n_type), FAL0);
743 if(!ok_blook(posix))
744 myto = n_alternates_remove(myto, TRU1);
745 if(myto == NULL)
746 goto jleave;
748 n_autorec_relax_create();
749 for(ip = msgvec; *ip != 0; ++ip){
750 struct message *mp;
752 mp = &message[*ip - 1];
753 touch(mp);
754 setdot(mp);
756 memset(&head, 0, sizeof head);
757 head.h_to = myto;
758 head.h_mailx_command = "resend";
759 head.h_mailx_raw_to = myrawto;
760 head.h_mailx_orig_from = lextract(hfield1("from", mp), GIDENT | gf);
761 head.h_mailx_orig_to = lextract(hfield1("to", mp), GTO | gf);
762 head.h_mailx_orig_cc = lextract(hfield1("cc", mp), GCC | gf);
763 head.h_mailx_orig_bcc = lextract(hfield1("bcc", mp), GBCC | gf);
765 if(resend_msg(mp, &head, add_resent) != OKAY){
766 /* n_autorec_relax_gut(); XXX but is handled automatically? */
767 goto jleave;
769 n_autorec_relax_unroll();
771 n_autorec_relax_gut();
773 n_pstate_err_no = n_ERR_NONE;
774 rv = 0;
775 jleave:
776 NYD2_LEAVE;
777 return rv;
780 FL int
781 c_reply(void *vp){
782 int rv;
783 NYD_ENTER;
785 rv = (*a_crese_reply_or_Reply('r'))(vp, FAL0);
786 NYD_LEAVE;
787 return rv;
790 FL int
791 c_replyall(void *vp){
792 int rv;
793 NYD_ENTER;
795 rv = a_crese_reply(vp, FAL0);
796 NYD_LEAVE;
797 return rv;
800 FL int
801 c_replysender(void *vp){
802 int rv;
803 NYD_ENTER;
805 rv = a_crese_Reply(vp, FAL0);
806 NYD_LEAVE;
807 return rv;
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_Lreply(void *vp){
822 int rv;
823 NYD_ENTER;
825 rv = a_crese_list_reply(vp, HF_LIST_REPLY);
826 NYD_LEAVE;
827 return rv;
830 FL int
831 c_followup(void *vp){
832 int rv;
833 NYD_ENTER;
835 rv = (*a_crese_reply_or_Reply('r'))(vp, TRU1);
836 NYD_LEAVE;
837 return rv;
840 FL int
841 c_followupall(void *vp){
842 int rv;
843 NYD_ENTER;
845 rv = a_crese_reply(vp, TRU1);
846 NYD_LEAVE;
847 return rv;
850 FL int
851 c_followupsender(void *vp){
852 int rv;
853 NYD_ENTER;
855 rv = a_crese_Reply(vp, TRU1);
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_forward(void *vp){
872 int rv;
873 NYD_ENTER;
875 rv = a_crese_fwd(vp, FAL0);
876 NYD_LEAVE;
877 return rv;
880 FL int
881 c_Forward(void *vp){
882 int rv;
883 NYD_ENTER;
885 rv = a_crese_fwd(vp, TRU1);
886 NYD_LEAVE;
887 return rv;
890 FL int
891 c_resend(void *vp){
892 int rv;
893 NYD_ENTER;
895 rv = a_crese_resend1(vp, TRU1);
896 NYD_LEAVE;
897 return rv;
900 FL int
901 c_Resend(void *vp){
902 int rv;
903 NYD_ENTER;
905 rv = a_crese_resend1(vp, FAL0);
906 NYD_LEAVE;
907 return rv;
910 /* s-it-mode */