is_addr_invalid(): do not say doesn't, say does not
[s-mailx.git] / cmd-resend.c
blobc3db3a30e18f967a6d24a36f32a539ed09d333a0
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 */
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 n_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) && !(n_pstate & n_PS_ROBOT)){
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(n_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) && !(n_pstate & n_PS_ROBOT)){
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,
174 (mft->n_flink != NULL ? "..." : n_empty));
175 if(n_quadify(cp, UIZ_MAX, sp, TRU1) <= FAL0)
176 mft = NULL;
178 n_lofi_free(sp);
180 NYD2_LEAVE;
181 return mft;
184 static void
185 a_crese_polite_rt_mft_move(struct message *mp, struct header *hp,
186 struct name *np){
187 bool_t once;
188 NYD2_ENTER;
189 n_UNUSED(mp);
191 if(np == hp->h_to)
192 hp->h_to = NULL;
193 if(np == hp->h_cc)
194 hp->h_cc = NULL;
196 /* We may find that in the end To: is empty but Cc: is not, in which case we
197 * upgrade Cc: to To:, and jump back and redo the thing slightly different */
198 once = FAL0;
199 jredo:
200 while(np != NULL){
201 enum gfield gf;
202 struct name *nnp, **xpp, *xp;
204 nnp = np;
205 np = np->n_flink;
207 if(once){
208 gf = GTO;
209 xpp = &hp->h_to;
210 }else{
211 gf = GCC;
212 xpp = &hp->h_cc;
215 /* Try primary, then secondary */
216 for(xp = hp->h_mailx_orig_to; xp != NULL; xp = xp->n_flink)
217 if(!asccasecmp(xp->n_name, nnp->n_name))
218 goto jlink;
220 if(once){
221 gf = GCC;
222 xpp = &hp->h_cc;
225 for(xp = hp->h_mailx_orig_cc; xp != NULL; xp = xp->n_flink)
226 if(!asccasecmp(xp->n_name, nnp->n_name))
227 goto jlink;
229 /* If this receiver came in only via R-T: or M-F-T:, place her/him/it in
230 * To: due to lack of a better place */
231 gf = GTO;
232 xpp = &hp->h_to;
233 jlink:
234 /* Link it at the end to not loose original sort order */
235 if((xp = *xpp) != NULL)
236 while(xp->n_flink != NULL)
237 xp = xp->n_flink;
239 if((nnp->n_blink = xp) != NULL)
240 xp->n_flink = nnp;
241 else
242 *xpp = nnp;
243 nnp->n_flink = NULL;
244 nnp->n_type = (nnp->n_type & ~GMASK) | gf;
247 /* If afterwards only Cc: data remains, upgrade all of it to To: */
248 if(hp->h_to == NULL){
249 np = hp->h_cc;
250 hp->h_cc = NULL;
251 if(!once){
252 hp->h_to = NULL;
253 once = TRU1;
254 goto jredo;
255 }else
256 for(hp->h_to = np; np != NULL; np = np->n_flink)
257 np->n_type = (np->n_type & ~GMASK) | GTO;
259 NYD2_LEAVE;
262 static void
263 a_crese_make_ref_and_cs(struct message *mp, struct header *head) /* TODO ASAP */
265 char const *ccp;
266 char *oldref, *oldmsgid, *newref;
267 size_t oldreflen = 0, oldmsgidlen = 0, reflen;
268 unsigned i;
269 struct name *n;
270 NYD2_ENTER;
272 oldref = hfield1("references", mp);
273 oldmsgid = hfield1("message-id", mp);
274 if (oldmsgid == NULL || *oldmsgid == '\0') {
275 head->h_ref = NULL;
276 goto jleave;
279 reflen = 1;
280 if (oldref) {
281 oldreflen = strlen(oldref);
282 reflen += oldreflen + 2;
284 if (oldmsgid) {
285 oldmsgidlen = strlen(oldmsgid);
286 reflen += oldmsgidlen;
289 newref = n_alloc(reflen);
290 if (oldref != NULL) {
291 memcpy(newref, oldref, oldreflen +1);
292 if (oldmsgid != NULL) {
293 newref[oldreflen++] = ',';
294 newref[oldreflen++] = ' ';
295 memcpy(newref + oldreflen, oldmsgid, oldmsgidlen +1);
297 } else if (oldmsgid)
298 memcpy(newref, oldmsgid, oldmsgidlen +1);
299 n = extract(newref, GREF);
300 n_free(newref);
302 /* Limit number of references TODO better on parser side */
303 while (n->n_flink != NULL)
304 n = n->n_flink;
305 for (i = 1; i <= REFERENCES_MAX; ++i) {
306 if (n->n_blink != NULL)
307 n = n->n_blink;
308 else
309 break;
311 n->n_blink = NULL;
312 head->h_ref = n;
313 if (ok_blook(reply_in_same_charset) &&
314 (ccp = hfield1("content-type", mp)) != NULL){
315 if((head->h_charset = ccp = mime_param_get("charset", ccp)) != NULL){
316 if((ccp = n_iconv_normalize_name(ccp)) != NULL)
317 ccp = n_charsetalias_expand(ccp);
318 head->h_charset = ccp;
321 jleave:
322 NYD2_LEAVE;
325 static int
326 a_crese_list_reply(int *msgvec, enum header_flags hf){
327 struct header head;
328 struct message *mp;
329 char const *cp, *cp2;
330 enum gfield gf;
331 struct name *rt, *mft, *np;
332 int *save_msgvec;
333 NYD2_ENTER;
335 n_pstate_err_no = n_ERR_NONE;
337 /* TODO Since we may recur and do stuff with message lists we need to save
338 * TODO away the argument vector as long as that isn't done by machinery */
339 /* C99 */{
340 size_t i;
341 for(i = 0; msgvec[i] != 0; ++i)
343 ++i;
344 save_msgvec = n_lofi_alloc(sizeof(*save_msgvec) * i);
345 while(i-- > 0)
346 save_msgvec[i] = msgvec[i];
347 msgvec = save_msgvec;
350 gf = ok_blook(fullnames) ? GFULL | GSKIN : GSKIN;
352 jwork_msg:
353 n_autorec_relax_create();
354 mp = &message[*msgvec - 1];
355 touch(mp);
356 setdot(mp);
358 memset(&head, 0, sizeof head);
359 head.h_flags = hf;
360 head.h_subject = a_crese_reedit(hfield1("subject", mp));
361 head.h_mailx_command = (hf & HF_LIST_REPLY) ? "Lreply" : "reply";
362 head.h_mailx_orig_from = lextract(hfield1("from", mp), GIDENT | gf);
363 head.h_mailx_orig_to = lextract(hfield1("to", mp), GTO | gf);
364 head.h_mailx_orig_cc = lextract(hfield1("cc", mp), GCC | gf);
365 head.h_mailx_orig_bcc = lextract(hfield1("bcc", mp), GBCC | gf);
367 /* First of all check for Reply-To: then Mail-Followup-To:, because these,
368 * if honoured, take precedence over anything else. We will join the
369 * resulting list together if so desired.
370 * So if we shall honour R-T: or M-F-T:, then these are our receivers! */
371 rt = a_crese_reply_to(mp);
372 mft = a_crese_mail_followup_to(mp);
374 if(rt != NULL || mft != NULL){
375 np = cat(rt, mft);
376 if(mft != NULL)
377 head.h_mft = namelist_dup(np, GTO | gf); /* xxx GTO: no "clone"! */
379 /* Optionally do not propagate a receiver that originally was in
380 * secondary Cc: to the primary To: list */
381 if(ok_blook(recipients_in_cc)){
382 a_crese_polite_rt_mft_move(mp, &head, np);
384 head.h_mailx_raw_cc = namelist_dup(head.h_cc, GCC | gf);
385 head.h_cc = n_alternates_remove(head.h_cc, FAL0);
386 }else
387 head.h_to = np;
389 head.h_mailx_raw_to = namelist_dup(head.h_to, GTO | gf);
390 head.h_to = n_alternates_remove(head.h_to, FAL0);
391 #ifdef HAVE_DEVEL
392 for(np = head.h_to; np != NULL; np = np->n_flink)
393 assert((np->n_type & GMASK) == GTO);
394 for(np = head.h_cc; np != NULL; np = np->n_flink)
395 assert((np->n_type & GMASK) == GCC);
396 #endif
397 goto jrecipients_done;
400 /* Otherwise do the normal From: / To: / Cc: dance */
402 cp2 = n_header_senderfield_of(mp);
404 /* Cc: */
405 np = NULL;
406 if(ok_blook(recipients_in_cc) && (cp = hfield1("to", mp)) != NULL)
407 np = lextract(cp, GCC | gf);
408 if((cp = hfield1("cc", mp)) != NULL){
409 struct name *x;
411 if((x = lextract(cp, GCC | gf)) != NULL)
412 np = cat(np, x);
414 if(np != NULL){
415 head.h_mailx_raw_cc = namelist_dup(np, GCC | gf);
416 head.h_cc = n_alternates_remove(np, FAL0);
419 /* To: */
420 np = NULL;
421 if(cp2 != NULL)
422 np = lextract(cp2, GTO | gf);
423 if(!ok_blook(recipients_in_cc) && (cp = hfield1("to", mp)) != NULL){
424 struct name *x;
426 if((x = lextract(cp, GTO | gf)) != NULL)
427 np = cat(np, x);
429 /* Delete my name from reply list, and with it, all my alternate names */
430 if(np != NULL){
431 head.h_mailx_raw_to = namelist_dup(np, GTO | gf);
432 np = n_alternates_remove(np, FAL0);
433 /* The user may have send this to himself, don't ignore that */
434 if(count(np) == 0){
435 np = lextract(cp2, GTO | gf);
436 head.h_mailx_raw_to = namelist_dup(np, GTO | gf);
439 head.h_to = np;
441 jrecipients_done:
443 /* For list replies we want to automatically recognize the list address
444 * given in the List-Post: header, so that we will not throw away a possible
445 * corresponding receiver: temporarily "`mlist' the List-Post: address" */
446 if((hf & HF_LIST_REPLY) && (cp = hfield1("list-post", mp)) != NULL){
447 struct name *x;
449 if((x = lextract(cp, GEXTRA | GSKIN)) == NULL || x->n_flink != NULL ||
450 (cp = url_mailto_to_address(x->n_name)) == NULL ||
451 /* XXX terribly wasteful to create a new name, and can't we find
452 * XXX a way to mitigate that?? */
453 is_addr_invalid(x = nalloc(cp, GEXTRA | GSKIN), EACM_STRICT)){
454 if(n_poption & n_PO_D_V)
455 n_err(_("Message contains invalid List-Post: header\n"));
456 }else{
457 /* A special case has been seen on e.g. ietf-announce@ietf.org:
458 * these usually post to multiple groups, with ietf-announce@
459 * in List-Post:, but with Reply-To: set to ietf@ietf.org (since
460 * -announce@ is only used for announcements, say).
461 * So our desire is to honour this request and actively overwrite
462 * List-Post: for our purpose; but only if its a single address.
463 * However, to avoid ambiguities with users that place themselve in
464 * Reply-To: and mailing lists which don't overwrite this (or only
465 * extend this, shall such exist), only do so if reply_to exists of
466 * a single address which points to the same domain as List-Post: */
467 if(rt != NULL && rt->n_flink == NULL &&
468 name_is_same_domain(x, rt))
469 cp = rt->n_name; /* rt is EACM_STRICT tested */
470 else
471 cp = x->n_name;
473 /* XXX is_mlist_mp()?? */
474 if(is_mlist(cp, FAL0) == MLIST_OTHER)
475 head.h_list_post = cp;
479 /* In case of list replies we actively sort out any non-list recipient */
480 if(hf & HF_LIST_REPLY){
481 struct name **nhpp, *nhp, *tail;
483 cp = head.h_list_post;
485 nhp = *(nhpp = &head.h_to);
486 head.h_to = NULL;
487 j_lt_redo:
488 for(tail = NULL; nhp != NULL;){
489 np = nhp;
490 nhp = nhp->n_flink;
492 /* XXX is_mlist_mp()?? */
493 if((cp != NULL && !asccasecmp(cp, np->n_name)) ||
494 is_mlist(np->n_name, FAL0) != MLIST_OTHER){
495 if((np->n_blink = tail) != NULL)
496 tail->n_flink = np;
497 else
498 *nhpp = np;
499 np->n_flink = NULL;
500 tail = np;
503 if(nhpp == &head.h_to){
504 nhp = *(nhpp = &head.h_cc);
505 head.h_cc = NULL;
506 goto j_lt_redo;
509 /* For `Lreply' only, fail immediately with DESTADDRREQ if there are no
510 * receivers at all! */
511 if(head.h_to == NULL && head.h_cc == NULL){
512 n_err(_("No recipients specified for `Lreply'\n"));
513 if(msgvec[1] == 0){
514 n_pstate_err_no = n_ERR_DESTADDRREQ;
515 msgvec = NULL;
516 goto jleave;
518 goto jskip_to_next;
522 /* Move Cc: to To: as appropriate! */
523 if(head.h_to == NULL && (np = head.h_cc) != NULL){
524 head.h_cc = NULL;
525 for(head.h_to = np; np != NULL; np = np->n_flink)
526 np->n_type = (np->n_type & ~GMASK) | GTO;
529 a_crese_make_ref_and_cs(mp, &head);
531 if(ok_blook(quote_as_attachment)){
532 head.h_attach = n_autorec_calloc(1, sizeof *head.h_attach);
533 head.h_attach->a_msgno = *msgvec;
534 head.h_attach->a_content_description = _("Original message content");
537 if(mail1(&head, 1, mp, NULL, !!(hf & HF_RECIPIENT_RECORD), FAL0) != OKAY){
538 msgvec = NULL;
539 goto jleave;
541 if(ok_blook(markanswered) && !(mp->m_flag & MANSWERED))
542 mp->m_flag |= MANSWER | MANSWERED;
543 n_autorec_relax_gut();
545 jskip_to_next:
546 if(*++msgvec != 0){
547 /* TODO message (error) ring.., less sleep */
548 if(n_psonce & n_PSO_INTERACTIVE){
549 fprintf(n_stdout,
550 _("Waiting a second before proceeding to the next message..\n"));
551 fflush(n_stdout);
552 n_msleep(1000, FAL0);
554 goto jwork_msg;
557 jleave:
558 n_lofi_free(save_msgvec);
559 NYD2_LEAVE;
560 return (msgvec == NULL);
563 static int
564 (*a_crese_reply_or_Reply(char c))(int *, bool_t){
565 int (*rv)(int*, bool_t);
566 NYD2_ENTER;
568 rv = (ok_blook(flipr) ^ (c == 'R')) ? &a_crese_Reply : &a_crese_reply;
569 NYD2_LEAVE;
570 return rv;
573 static int
574 a_crese_reply(int *msgvec, bool_t recipient_record){
575 int rv;
576 NYD2_ENTER;
578 rv = a_crese_list_reply(msgvec,
579 (recipient_record ? HF_RECIPIENT_RECORD : HF_NONE));
580 NYD2_LEAVE;
581 return rv;
584 static int
585 a_crese_Reply(int *msgvec, bool_t recipient_record){
586 struct header head;
587 struct message *mp;
588 int *ap;
589 enum gfield gf;
590 NYD2_ENTER;
592 memset(&head, 0, sizeof head);
593 gf = ok_blook(fullnames) ? GFULL | GSKIN : GSKIN;
595 for(ap = msgvec; *ap != 0; ++ap){
596 struct name *np;
598 mp = &message[*ap - 1];
599 touch(mp);
600 setdot(mp);
602 if((np = a_crese_reply_to(mp)) == NULL)
603 np = lextract(n_header_senderfield_of(mp), GTO | gf);
604 head.h_to = cat(head.h_to, np);
607 mp = &message[msgvec[0] - 1];
608 head.h_subject = hfield1("subject", mp);
609 head.h_subject = a_crese_reedit(head.h_subject);
610 a_crese_make_ref_and_cs(mp, &head);
611 head.h_mailx_command = "Reply";
612 head.h_mailx_orig_from = lextract(hfield1("from", mp), GIDENT | gf);
613 head.h_mailx_orig_to = lextract(hfield1("to", mp), GTO | gf);
614 head.h_mailx_orig_cc = lextract(hfield1("cc", mp), GCC | gf);
615 head.h_mailx_orig_bcc = lextract(hfield1("bcc", mp), GBCC | gf);
617 if(ok_blook(recipients_in_cc)){
618 a_crese_polite_rt_mft_move(mp, &head, head.h_to);
620 head.h_mailx_raw_cc = namelist_dup(head.h_cc, GCC | gf);
621 head.h_cc = n_alternates_remove(head.h_cc, FAL0);
623 head.h_mailx_raw_to = namelist_dup(head.h_to, GTO | gf);
624 head.h_to = n_alternates_remove(head.h_to, FAL0);
626 if(ok_blook(quote_as_attachment)){
627 head.h_attach = n_autorec_calloc(1, sizeof *head.h_attach);
628 head.h_attach->a_msgno = *msgvec;
629 head.h_attach->a_content_description = _("Original message content");
632 if(mail1(&head, 1, mp, NULL, recipient_record, FAL0) != OKAY){
633 msgvec = NULL;
634 goto jleave;
637 if(ok_blook(markanswered) && !(mp->m_flag & MANSWERED))
638 mp->m_flag |= MANSWER | MANSWERED;
639 jleave:
640 NYD2_LEAVE;
641 return (msgvec == NULL);
644 static int
645 a_crese_fwd(char *str, int recipient_record){
646 struct header head;
647 struct message *mp;
648 enum gfield gf;
649 bool_t f, forward_as_attachment;
650 char *recipient;
651 int rv, *msgvec;
652 NYD2_ENTER;
654 rv = 1;
656 if((recipient = laststring(str, &f, TRU1)) == NULL){
657 n_err(_("No recipient specified.\n"));
658 n_pstate_err_no = n_ERR_DESTADDRREQ;
659 goto jleave;
662 forward_as_attachment = ok_blook(forward_as_attachment);
663 gf = ok_blook(fullnames) ? GFULL | GSKIN : GSKIN;
664 msgvec = n_autorec_alloc((msgCount + 2) * sizeof *msgvec);
666 n_pstate_err_no = n_ERR_NODATA;
667 if(!f){
668 *msgvec = first(0, MMNORM);
669 if(*msgvec != 0)
670 msgvec[1] = 0;
671 }else if(getmsglist(str, msgvec, 0) < 0)
672 goto jleave;
674 if(*msgvec == 0){
675 n_err(_("No applicable messages.\n"));
676 goto jleave;
678 if(msgvec[1] != 0){
679 n_err(_("Cannot forward multiple messages at once\n"));
680 n_pstate_err_no = n_ERR_NOTSUP;
681 goto jleave;
684 memset(&head, 0, sizeof head);
685 head.h_to = lextract(recipient,
686 (GTO | (ok_blook(fullnames) ? GFULL : GSKIN)));
688 mp = &message[*msgvec - 1];
689 touch(mp);
690 setdot(mp);
691 head.h_subject = hfield1("subject", mp);
692 head.h_subject = a_crese__fwdedit(head.h_subject);
693 head.h_mailx_command = "forward";
694 head.h_mailx_raw_to = namelist_dup(head.h_to, GTO | gf);
695 head.h_mailx_orig_from = lextract(hfield1("from", mp), GIDENT | gf);
696 head.h_mailx_orig_to = lextract(hfield1("to", mp), GTO | gf);
697 head.h_mailx_orig_cc = lextract(hfield1("cc", mp), GCC | gf);
698 head.h_mailx_orig_bcc = lextract(hfield1("bcc", mp), GBCC | gf);
700 if(forward_as_attachment){
701 head.h_attach = n_autorec_calloc(1, sizeof *head.h_attach);
702 head.h_attach->a_msgno = *msgvec;
703 head.h_attach->a_content_description = _("Forwarded message");
706 rv = (mail1(&head, 1, (forward_as_attachment ? NULL : mp), NULL,
707 recipient_record, TRU1) != OKAY); /* reverse! */
708 jleave:
709 NYD2_LEAVE;
710 return rv;
713 static char *
714 a_crese__fwdedit(char *subj){
715 struct str in, out;
716 char *newsubj;
717 NYD2_ENTER;
719 newsubj = NULL;
721 if(subj == NULL || *subj == '\0')
722 goto jleave;
724 in.s = subj;
725 in.l = strlen(subj);
726 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
728 newsubj = n_autorec_alloc(out.l + 6);
729 if(!ascncasecmp(out.s, "Fwd: ", sizeof("Fwd: ") -1)) /* TODO EXTEND SUPP.. */
730 memcpy(newsubj, out.s, out.l +1);
731 else{
732 memcpy(newsubj, "Fwd: ", 5); /* TODO ..a la subject_re_trim()! */
733 memcpy(&newsubj[5], out.s, out.l +1);
736 n_free(out.s);
737 jleave:
738 NYD2_LEAVE;
739 return newsubj;
742 static int
743 a_crese_resend1(void *vp, bool_t add_resent){
744 struct header head;
745 struct name *myto, *myrawto;
746 enum gfield gf;
747 char *name, *str;
748 int *ip, *msgvec;
749 bool_t fail;
750 NYD2_ENTER;
752 fail = TRU1;
754 str = vp;
755 msgvec = n_autorec_alloc((msgCount + 2) * sizeof *msgvec);
756 name = laststring(str, &fail, TRU1);
757 if(name == NULL){
758 n_err(_("No recipient specified.\n"));
759 n_pstate_err_no = n_ERR_DESTADDRREQ;
760 goto jleave;
763 n_pstate_err_no = n_ERR_NODATA;
765 if(!fail){
766 *msgvec = first(0, MMNORM);
767 if(*msgvec != 0)
768 msgvec[1] = 0;
769 }else if(getmsglist(str, msgvec, 0) < 0)
770 goto jleave;
772 if(*msgvec == 0){
773 n_err(_("No applicable messages.\n"));
774 goto jleave;
777 fail = TRU1;
778 gf = ok_blook(fullnames) ? GFULL | GSKIN : GSKIN;
780 myrawto = nalloc(name, GTO | gf);
781 myto = usermap(namelist_dup(myrawto, myrawto->n_type), FAL0);
782 myto = n_alternates_remove(myto, TRU1);
783 if(myto == NULL){
784 n_pstate_err_no = n_ERR_DESTADDRREQ;
785 goto jleave;
788 n_autorec_relax_create();
789 for(ip = msgvec; *ip != 0 && UICMP(z, PTR2SIZE(ip - msgvec), <, msgCount);
790 ++ip){
791 struct message *mp;
793 mp = &message[*ip - 1];
794 touch(mp);
795 setdot(mp);
797 memset(&head, 0, sizeof head);
798 head.h_to = myto;
799 head.h_mailx_command = "resend";
800 head.h_mailx_raw_to = myrawto;
801 head.h_mailx_orig_from = lextract(hfield1("from", mp), GIDENT | gf);
802 head.h_mailx_orig_to = lextract(hfield1("to", mp), GTO | gf);
803 head.h_mailx_orig_cc = lextract(hfield1("cc", mp), GCC | gf);
804 head.h_mailx_orig_bcc = lextract(hfield1("bcc", mp), GBCC | gf);
806 if(resend_msg(mp, &head, add_resent) != OKAY){
807 /* n_autorec_relax_gut(); XXX but is handled automatically? */
808 goto jleave;
810 n_autorec_relax_unroll();
812 n_autorec_relax_gut();
814 fail = FAL0;
815 n_pstate_err_no = n_ERR_NONE;
816 jleave:
817 NYD2_LEAVE;
818 return (fail != FAL0);
821 FL int
822 c_reply(void *vp){
823 int rv;
824 NYD_ENTER;
826 rv = (*a_crese_reply_or_Reply('r'))(vp, FAL0);
827 NYD_LEAVE;
828 return rv;
831 FL int
832 c_replyall(void *vp){
833 int rv;
834 NYD_ENTER;
836 rv = a_crese_reply(vp, FAL0);
837 NYD_LEAVE;
838 return rv;
841 FL int
842 c_replysender(void *vp){
843 int rv;
844 NYD_ENTER;
846 rv = a_crese_Reply(vp, FAL0);
847 NYD_LEAVE;
848 return rv;
851 FL int
852 c_Reply(void *vp){
853 int rv;
854 NYD_ENTER;
856 rv = (*a_crese_reply_or_Reply('R'))(vp, FAL0);
857 NYD_LEAVE;
858 return rv;
861 FL int
862 c_Lreply(void *vp){
863 int rv;
864 NYD_ENTER;
866 rv = a_crese_list_reply(vp, HF_LIST_REPLY);
867 NYD_LEAVE;
868 return rv;
871 FL int
872 c_followup(void *vp){
873 int rv;
874 NYD_ENTER;
876 rv = (*a_crese_reply_or_Reply('r'))(vp, TRU1);
877 NYD_LEAVE;
878 return rv;
881 FL int
882 c_followupall(void *vp){
883 int rv;
884 NYD_ENTER;
886 rv = a_crese_reply(vp, TRU1);
887 NYD_LEAVE;
888 return rv;
891 FL int
892 c_followupsender(void *vp){
893 int rv;
894 NYD_ENTER;
896 rv = a_crese_Reply(vp, TRU1);
897 NYD_LEAVE;
898 return rv;
901 FL int
902 c_Followup(void *vp){
903 int rv;
904 NYD_ENTER;
906 rv = (*a_crese_reply_or_Reply('R'))(vp, TRU1);
907 NYD_LEAVE;
908 return rv;
911 FL int
912 c_forward(void *vp){
913 int rv;
914 NYD_ENTER;
916 rv = a_crese_fwd(vp, 0);
917 NYD_LEAVE;
918 return rv;
921 FL int
922 c_Forward(void *vp){
923 int rv;
924 NYD_ENTER;
926 rv = a_crese_fwd(vp, 1);
927 NYD_LEAVE;
928 return rv;
931 FL int
932 c_resend(void *vp){
933 int rv;
934 NYD_ENTER;
936 rv = a_crese_resend1(vp, TRU1);
937 NYD_LEAVE;
938 return rv;
941 FL int
942 c_Resend(void *vp){
943 int rv;
944 NYD_ENTER;
946 rv = a_crese_resend1(vp, FAL0);
947 NYD_LEAVE;
948 return rv;
951 /* s-it-mode */