cc-test.sh: do a simple \`Resend' test
[s-mailx.git] / cmd3.c
blobf9c72e5b05149bce54d2937dd420ce607d43770c
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Still more user commands.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
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 cmd3
38 #ifndef HAVE_AMALGAMATION
39 # include "nail.h"
40 #endif
42 static char *_bang_buf;
43 static size_t _bang_size;
45 /* Modify subject we reply to to begin with Re: if it does not already */
46 static char * _reedit(char *subj);
48 /* Expand the shell escape by expanding unescaped !'s into the last issued
49 * command where possible */
50 static void _bangexp(char **str, size_t *size);
52 static void make_ref_and_cs(struct message *mp, struct header *head);
54 /* `reply' and `Lreply' workhorse */
55 static int _list_reply(int *msgvec, enum header_flags hf);
57 /* Get PTF to implementation of command c (i.e., take care for *flipr*) */
58 static int (* _reply_or_Reply(char c))(int *, bool_t);
60 /* Reply to a single message. Extract each name from the message header and
61 * send them off to mail1() */
62 static int _reply(int *msgvec, bool_t recipient_record);
64 /* Reply to a series of messages by simply mailing to the senders and not
65 * messing around with the To: and Cc: lists as in normal reply */
66 static int _Reply(int *msgvec, bool_t recipient_record);
68 /* Forward a message to a new recipient, in the sense of RFC 2822 */
69 static int _fwd(char *str, int recipient_record);
71 /* Modify the subject we are replying to to begin with Fwd: */
72 static char * __fwdedit(char *subj);
74 /* Do the real work of resending */
75 static int _resend1(void *v, bool_t add_resent);
77 /* c_file, c_File */
78 static int _c_file(void *v, enum fedit_mode fm);
80 static char *
81 _reedit(char *subj)
83 struct str in, out;
84 char *newsubj = NULL;
85 NYD_ENTER;
87 if (subj == NULL || *subj == '\0')
88 goto jleave;
90 in.s = subj;
91 in.l = strlen(subj);
92 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
94 if ((newsubj = subject_re_trim(out.s)) != out.s)
95 newsubj = savestr(out.s);
96 else {
97 /* RFC mandates english "Re: " */
98 newsubj = salloc(out.l + 4 +1);
99 sstpcpy(sstpcpy(newsubj, "Re: "), out.s);
102 free(out.s);
103 jleave:
104 NYD_LEAVE;
105 return newsubj;
108 static void
109 _bangexp(char **str, size_t *size)
111 char *bangbuf;
112 int changed = 0;
113 bool_t dobang;
114 size_t sz, i, j, bangbufsize;
115 NYD_ENTER;
117 dobang = ok_blook(bang);
119 bangbuf = smalloc(bangbufsize = *size);
120 i = j = 0;
121 while ((*str)[i]) {
122 if (dobang) {
123 if ((*str)[i] == '!') {
124 sz = strlen(_bang_buf);
125 bangbuf = srealloc(bangbuf, bangbufsize += sz);
126 ++changed;
127 memcpy(bangbuf + j, _bang_buf, sz + 1);
128 j += sz;
129 i++;
130 continue;
133 if ((*str)[i] == '\\' && (*str)[i + 1] == '!') {
134 bangbuf[j++] = '!';
135 i += 2;
136 ++changed;
138 bangbuf[j++] = (*str)[i++];
140 bangbuf[j] = '\0';
141 if (changed) {
142 printf("!%s\n", bangbuf);
143 fflush(stdout);
145 sz = j + 1;
146 if (sz > *size)
147 *str = srealloc(*str, *size = sz);
148 memcpy(*str, bangbuf, sz);
149 if (sz > _bang_size)
150 _bang_buf = srealloc(_bang_buf, _bang_size = sz);
151 memcpy(_bang_buf, bangbuf, sz);
152 free(bangbuf);
153 NYD_LEAVE;
156 static void
157 make_ref_and_cs(struct message *mp, struct header *head) /* TODO rewrite FAST */
159 char *oldref, *oldmsgid, *newref, *cp;
160 size_t oldreflen = 0, oldmsgidlen = 0, reflen;
161 unsigned i;
162 struct name *n;
163 NYD_ENTER;
165 oldref = hfield1("references", mp);
166 oldmsgid = hfield1("message-id", mp);
167 if (oldmsgid == NULL || *oldmsgid == '\0') {
168 head->h_ref = NULL;
169 goto jleave;
172 reflen = 1;
173 if (oldref) {
174 oldreflen = strlen(oldref);
175 reflen += oldreflen + 2;
177 if (oldmsgid) {
178 oldmsgidlen = strlen(oldmsgid);
179 reflen += oldmsgidlen;
182 newref = smalloc(reflen);
183 if (oldref != NULL) {
184 memcpy(newref, oldref, oldreflen +1);
185 if (oldmsgid != NULL) {
186 newref[oldreflen++] = ',';
187 newref[oldreflen++] = ' ';
188 memcpy(newref + oldreflen, oldmsgid, oldmsgidlen +1);
190 } else if (oldmsgid)
191 memcpy(newref, oldmsgid, oldmsgidlen +1);
192 n = extract(newref, GREF);
193 free(newref);
195 /* Limit number of references TODO better on parser side */
196 while (n->n_flink != NULL)
197 n = n->n_flink;
198 for (i = 1; i <= REFERENCES_MAX; ++i) {
199 if (n->n_blink != NULL)
200 n = n->n_blink;
201 else
202 break;
204 n->n_blink = NULL;
205 head->h_ref = n;
206 if (ok_blook(reply_in_same_charset) &&
207 (cp = hfield1("content-type", mp)) != NULL)
208 head->h_charset = mime_param_get("charset", cp);
209 jleave:
210 NYD_LEAVE;
213 static int
214 _list_reply(int *msgvec, enum header_flags hf)
216 struct header head;
217 struct message *mp;
218 char const *reply_to, *rcv, *cp;
219 enum gfield gf;
220 struct name *rt, *mft, *np;
221 int rv = 1;
222 NYD_ENTER;
224 jnext_msg:
225 mp = message + *msgvec - 1;
226 touch(mp);
227 setdot(mp);
229 memset(&head, 0, sizeof head);
230 head.h_flags = hf;
232 head.h_subject = _reedit(hfield1("subject", mp));
233 gf = ok_blook(fullnames) ? GFULL : GSKIN;
234 rt = mft = NULL;
236 rcv = NULL;
237 if ((reply_to = hfield1("reply-to", mp)) != NULL &&
238 (cp = ok_vlook(reply_to_honour)) != NULL &&
239 (rt = checkaddrs(lextract(reply_to, GTO | gf), EACM_STRICT, NULL)
240 ) != NULL) {
241 char const *tr = _("Reply-To \"%s%s\"");
242 size_t l = strlen(tr) + strlen(rt->n_name) + 3 +1;
243 char *sp = salloc(l);
245 snprintf(sp, l, tr, rt->n_name, (rt->n_flink != NULL ? "..." : ""));
246 if (quadify(cp, UIZ_MAX, sp, TRU1) > FAL0)
247 rcv = reply_to;
250 if (rcv == NULL && (rcv = hfield1("from", mp)) == NULL)
251 rcv = nameof(mp, 1);
253 /* Cc: */
254 np = NULL;
255 if (ok_blook(recipients_in_cc) && (cp = hfield1("to", mp)) != NULL)
256 np = lextract(cp, GCC | gf);
257 if ((cp = hfield1("cc", mp)) != NULL)
258 np = cat(np, lextract(cp, GCC | gf));
259 if (np != NULL)
260 head.h_cc = delete_alternates(np);
262 /* To: */
263 np = NULL;
264 if (rcv != NULL)
265 np = (rcv == reply_to) ? namelist_dup(rt, GTO | gf)
266 : lextract(rcv, GTO | gf);
267 if (!ok_blook(recipients_in_cc) && (cp = hfield1("to", mp)) != NULL)
268 np = cat(np, lextract(cp, GTO | gf));
269 /* Delete my name from reply list, and with it, all my alternate names */
270 np = delete_alternates(np);
271 if (count(np) == 0)
272 np = lextract(rcv, GTO | gf);
273 head.h_to = np;
275 /* The user may have send this to himself, don't ignore that */
276 namelist_vaporise_head(&head, EACM_NORMAL, FAL0, NULL);
277 if (head.h_to == NULL)
278 head.h_to = np;
280 /* Mail-Followup-To: */
281 mft = NULL;
282 if (ok_vlook(followup_to_honour) != NULL &&
283 (cp = hfield1("mail-followup-to", mp)) != NULL &&
284 (mft = np = checkaddrs(lextract(cp, GTO | gf), EACM_STRICT, NULL)
285 ) != NULL) {
286 char const *tr = _("Followup-To \"%s%s\"");
287 size_t l = strlen(tr) + strlen(np->n_name) + 3 +1;
288 char *sp = salloc(l);
290 snprintf(sp, l, tr, np->n_name, (np->n_flink != NULL ? "..." : ""));
291 if (quadify(ok_vlook(followup_to_honour), UIZ_MAX, sp, TRU1) > FAL0) {
292 head.h_cc = NULL;
293 head.h_to = np;
294 head.h_mft =
295 mft = namelist_vaporise_head(&head, EACM_STRICT, FAL0, NULL);
296 } else
297 mft = NULL;
300 /* Special massage for list (follow-up) messages */
301 if (mft != NULL || (hf & HF_LIST_REPLY) || ok_blook(followup_to)) {
302 /* Learn about a possibly sending mailing list; use do for break; */
303 if ((cp = hfield1("list-post", mp)) != NULL) do {
304 struct name *x = lextract(cp, GEXTRA | GSKIN);
305 if (x == NULL || x->n_flink != NULL ||
306 !is_prefix("mailto:", x->n_name) ||
307 /* XXX the mailto: prefix causes failure (":" invalid character)
308 * XXX which is why need to recreate a struct name with an
309 * XXX updated name; this is terribly wasteful and can't we find
310 * XXX a way to mitigate that?? */
311 is_addr_invalid(x = nalloc(x->n_name + sizeof("mailto:") -1,
312 GEXTRA | GSKIN), EACM_STRICT)) {
313 if (options & OPT_D_V)
314 n_err(_("Message contains invalid \"List-Post:\" header\n"));
315 cp = NULL;
316 break;
318 cp = x->n_name;
320 /* A special case has been seen on e.g. ietf-announce@ietf.org:
321 * these usually post to multiple groups, with ietf-announce@
322 * in List-Post:, but with Reply-To: set to ietf@ietf.org (since
323 * -announce@ is only used for announcements, say).
324 * So our desire is to honour this request and actively overwrite
325 * List-Post: for our purpose; but only if its a single address.
326 * However, to avoid ambiguities with users that place themselve in
327 * Reply-To: and mailing lists which don't overwrite this (or only
328 * extend this, shall such exist), only do so if reply_to exists of
329 * a single address which points to the same domain as List-Post: */
330 if (reply_to != NULL && rt->n_flink == NULL &&
331 name_is_same_domain(x, rt))
332 cp = rt->n_name; /* rt is EACM_STRICT tested */
334 /* "Automatically `mlist'" the List-Post: address temporarily */
335 if (is_mlist(cp, FAL0) == MLIST_OTHER)
336 head.h_list_post = cp;
337 else
338 cp = NULL;
339 } while (0);
341 /* In case of list replies we actively sort out any non-list recipient,
342 * but _only_ if we did not honour a MFT:, assuming that members of MFT
343 * were there for a reason; cp is still List-Post:/eqivalent */
344 if ((hf & HF_LIST_REPLY) && mft == NULL) {
345 struct name *nhp = head.h_to;
346 head.h_to = NULL;
347 j_lt_redo:
348 while (nhp != NULL) {
349 np = nhp;
350 nhp = nhp->n_flink;
352 if ((cp != NULL && !asccasecmp(cp, np->n_name)) ||
353 is_mlist(np->n_name, FAL0) != MLIST_OTHER) {
354 np->n_type = (np->n_type & ~GMASK) | GTO;
355 np->n_flink = head.h_to;
356 head.h_to = np;
359 if ((nhp = head.h_cc) != NULL) {
360 head.h_cc = NULL;
361 goto j_lt_redo;
366 make_ref_and_cs(mp, &head);
368 if (ok_blook(quote_as_attachment)) {
369 head.h_attach = csalloc(1, sizeof *head.h_attach);
370 head.h_attach->a_msgno = *msgvec;
371 head.h_attach->a_content_description = _("Original message content");
374 if (mail1(&head, 1, mp, NULL, !!(hf & HF_RECIPIENT_RECORD), 0) == OKAY &&
375 ok_blook(markanswered) && !(mp->m_flag & MANSWERED))
376 mp->m_flag |= MANSWER | MANSWERED;
378 if (*++msgvec != 0) {
379 /* TODO message (error) ring.., less sleep */
380 printf(_("Waiting a second before proceeding to the next message..\n"));
381 fflush(stdout);
382 sleep(1);
383 goto jnext_msg;
385 rv = 0;
386 NYD_LEAVE;
387 return rv;
390 static int
391 (*_reply_or_Reply(char c))(int *, bool_t)
393 int (*rv)(int*, bool_t);
394 NYD_ENTER;
396 rv = (ok_blook(flipr) ^ (c == 'R')) ? &_Reply : &_reply;
397 NYD_LEAVE;
398 return rv;
401 static int
402 _reply(int *msgvec, bool_t recipient_record)
404 int rv;
405 NYD_ENTER;
407 rv = _list_reply(msgvec, recipient_record ? HF_RECIPIENT_RECORD : HF_NONE);
408 NYD_LEAVE;
409 return rv;
412 static int
413 _Reply(int *msgvec, bool_t recipient_record)
415 struct header head;
416 struct message *mp;
417 int *ap;
418 char *cp;
419 enum gfield gf;
420 NYD_ENTER;
422 memset(&head, 0, sizeof head);
423 gf = ok_blook(fullnames) ? GFULL : GSKIN;
425 for (ap = msgvec; *ap != 0; ++ap) {
426 char const *rp;
427 struct name *rt;
429 mp = message + *ap - 1;
430 touch(mp);
431 setdot(mp);
433 if ((rp = hfield1("reply-to", mp)) != NULL &&
434 (cp = ok_vlook(reply_to_honour)) != NULL &&
435 (rt = checkaddrs(lextract(rp, GTO | gf), EACM_STRICT, NULL)
436 ) != NULL) {
437 char const *tr = _("Reply-To \"%s%s\"");
438 size_t l = strlen(tr) + strlen(rt->n_name) + 3 +1;
439 char *sp = salloc(l);
441 snprintf(sp, l, tr, rt->n_name, (rt->n_flink != NULL ? "..." : ""));
442 if (quadify(cp, UIZ_MAX, sp, TRU1) > FAL0) {
443 head.h_to = cat(head.h_to, rt);
444 continue;
448 if ((cp = hfield1("from", mp)) == NULL)
449 cp = nameof(mp, 2);
450 head.h_to = cat(head.h_to, lextract(cp, GTO | gf));
452 if (head.h_to == NULL)
453 goto jleave;
455 mp = message + msgvec[0] - 1;
456 head.h_subject = hfield1("subject", mp);
457 head.h_subject = _reedit(head.h_subject);
458 make_ref_and_cs(mp, &head);
460 if (ok_blook(quote_as_attachment)) {
461 head.h_attach = csalloc(1, sizeof *head.h_attach);
462 head.h_attach->a_msgno = *msgvec;
463 head.h_attach->a_content_description = _("Original message content");
466 if (mail1(&head, 1, mp, NULL, recipient_record, 0) == OKAY &&
467 ok_blook(markanswered) && !(mp->m_flag & MANSWERED))
468 mp->m_flag |= MANSWER | MANSWERED;
469 jleave:
470 NYD_LEAVE;
471 return 0;
474 static int
475 _fwd(char *str, int recipient_record)
477 struct header head;
478 int *msgvec, rv = 1;
479 char *recipient;
480 struct message *mp;
481 bool_t f, forward_as_attachment;
482 NYD_ENTER;
484 if ((recipient = laststring(str, &f, TRU1)) == NULL) {
485 puts(_("No recipient specified."));
486 goto jleave;
489 forward_as_attachment = ok_blook(forward_as_attachment);
490 msgvec = salloc((msgCount + 2) * sizeof *msgvec);
492 if (!f) {
493 *msgvec = first(0, MMNORM);
494 if (*msgvec == 0) {
495 if (pstate & PS_HOOK_MASK) {
496 rv = 0;
497 goto jleave;
499 printf(_("No messages to forward.\n"));
500 goto jleave;
502 msgvec[1] = 0;
503 } else if (getmsglist(str, msgvec, 0) < 0)
504 goto jleave;
506 if (*msgvec == 0) {
507 if (pstate & PS_HOOK_MASK) {
508 rv = 0;
509 goto jleave;
511 printf(_("No applicable messages.\n"));
512 goto jleave;
514 if (msgvec[1] != 0) {
515 printf(_("Cannot forward multiple messages at once\n"));
516 goto jleave;
519 memset(&head, 0, sizeof head);
520 if ((head.h_to = lextract(recipient,
521 (GTO | (ok_blook(fullnames) ? GFULL : GSKIN)))) == NULL)
522 goto jleave;
524 mp = message + *msgvec - 1;
526 if (forward_as_attachment) {
527 head.h_attach = csalloc(1, sizeof *head.h_attach);
528 head.h_attach->a_msgno = *msgvec;
529 head.h_attach->a_content_description = _("Forwarded message");
530 } else {
531 touch(mp);
532 setdot(mp);
534 head.h_subject = hfield1("subject", mp);
535 head.h_subject = __fwdedit(head.h_subject);
536 mail1(&head, 1, (forward_as_attachment ? NULL : mp), NULL, recipient_record,
538 rv = 0;
539 jleave:
540 NYD_LEAVE;
541 return rv;
544 static char *
545 __fwdedit(char *subj)
547 struct str in, out;
548 char *newsubj = NULL;
549 NYD_ENTER;
551 if (subj == NULL || *subj == '\0')
552 goto jleave;
554 in.s = subj;
555 in.l = strlen(subj);
556 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
558 newsubj = salloc(out.l + 6);
559 memcpy(newsubj, "Fwd: ", 5); /* XXX localizable */
560 memcpy(newsubj + 5, out.s, out.l +1);
561 free(out.s);
562 jleave:
563 NYD_LEAVE;
564 return newsubj;
567 static int
568 _resend1(void *v, bool_t add_resent)
570 char *name, *str;
571 struct name *to, *sn;
572 int *ip, *msgvec;
573 bool_t f = TRU1;
574 NYD_ENTER;
576 str = v;
577 msgvec = salloc((msgCount + 2) * sizeof *msgvec);
578 name = laststring(str, &f, TRU1);
579 if (name == NULL) {
580 puts(_("No recipient specified."));
581 goto jleave;
584 if (!f) {
585 *msgvec = first(0, MMNORM);
586 if (*msgvec == 0) {
587 if (pstate & PS_HOOK_MASK) {
588 f = FAL0;
589 goto jleave;
591 puts(_("No applicable messages."));
592 goto jleave;
594 msgvec[1] = 0;
595 } else if (getmsglist(str, msgvec, 0) < 0)
596 goto jleave;
598 if (*msgvec == 0) {
599 if (pstate & PS_HOOK_MASK) {
600 f = FAL0;
601 goto jleave;
603 printf("No applicable messages.\n");
604 goto jleave;
607 sn = nalloc(name, GTO | GSKIN);
608 to = usermap(sn, FAL0);
609 for (ip = msgvec; *ip != 0 && UICMP(z, PTR2SIZE(ip - msgvec), <, msgCount);
610 ++ip)
611 if (resend_msg(message + *ip - 1, to, add_resent) != OKAY)
612 goto jleave;
613 f = FAL0;
614 jleave:
615 NYD_LEAVE;
616 return (f != FAL0);
619 static int
620 _c_file(void *v, enum fedit_mode fm)
622 char **argv = v;
623 int i;
624 NYD2_ENTER;
626 if (*argv == NULL) {
627 newfileinfo();
628 i = 0;
629 goto jleave;
632 if (pstate & PS_HOOK_MASK) {
633 n_err(_("Cannot change folder from within a hook\n"));
634 i = 1;
635 goto jleave;
638 save_mbox_for_possible_quitstuff();
640 i = setfile(*argv, fm);
641 if (i < 0) {
642 i = 1;
643 goto jleave;
645 assert(!(fm & FEDIT_NEWMAIL));
646 check_folder_hook(FAL0);
648 if (i > 0 && !ok_blook(emptystart)) {
649 i = 1;
650 goto jleave;
652 announce(ok_blook(bsdcompat) || ok_blook(bsdannounce));
653 i = 0;
654 jleave:
655 NYD2_LEAVE;
656 return i;
659 FL int
660 c_shell(void *v)
662 char const *sh = NULL;
663 char *str = v, *cmd;
664 size_t cmdsize;
665 sigset_t mask;
666 sighandler_type sigint;
667 NYD_ENTER;
669 cmd = smalloc(cmdsize = strlen(str) +1);
670 memcpy(cmd, str, cmdsize);
671 _bangexp(&cmd, &cmdsize);
672 if ((sh = ok_vlook(SHELL)) == NULL)
673 sh = XSHELL;
675 sigint = safe_signal(SIGINT, SIG_IGN);
676 sigemptyset(&mask);
677 run_command(sh, &mask, -1, -1, "-c", cmd, NULL);
678 safe_signal(SIGINT, sigint);
679 printf("!\n");
681 free(cmd);
682 NYD_LEAVE;
683 return 0;
686 FL int
687 c_dosh(void *v)
689 sighandler_type sigint;
690 char const *sh;
691 NYD_ENTER;
692 UNUSED(v);
694 if ((sh = ok_vlook(SHELL)) == NULL)
695 sh = XSHELL;
697 sigint = safe_signal(SIGINT, SIG_IGN);
698 run_command(sh, 0, -1, -1, NULL, NULL, NULL);
699 safe_signal(SIGINT, sigint);
700 putchar('\n');
701 NYD_LEAVE;
702 return 0;
705 FL int
706 c_help(void *v)
708 int ret = 0;
709 char *arg;
710 NYD_ENTER;
712 arg = *(char**)v;
714 if (arg != NULL) {
715 #ifdef HAVE_DOCSTRINGS
716 ret = !print_comm_docstr(arg);
717 if (ret)
718 n_err(_("Unknown command: `%s'\n"), arg);
719 #else
720 ret = c_cmdnotsupp(NULL);
721 #endif
722 goto jleave;
725 /* Very ugly, but take care for compiler supported string lengths :( */
726 printf(_("%s commands:\n"), progname);
727 puts(_(
728 "type <message list> type messages\n"
729 "next goto and type next message\n"
730 "from <message list> give head lines of messages\n"
731 "headers print out active message headers\n"
732 "delete <message list> delete messages\n"
733 "undelete <message list> undelete messages\n"));
734 puts(_(
735 "save <message list> folder append messages to folder and mark as saved\n"
736 "copy <message list> folder append messages to folder without marking them\n"
737 "write <message list> file append message texts to file, save attachments\n"
738 "preserve <message list> keep incoming messages in mailbox even if saved\n"
739 "Reply <message list> reply to message senders\n"
740 "reply <message list> reply to message senders and all recipients\n"));
741 puts(_(
742 "mail addresses mail to specific recipients\n"
743 "file folder change to another folder\n"
744 "quit quit and apply changes to folder\n"
745 "xit quit and discard changes made to folder\n"
746 "! shell escape\n"
747 "cd <directory> chdir to directory or home if none given\n"
748 "list list names of all available commands\n"));
749 printf(_(
750 "A <message list> consists of integers, ranges of same, or other criteria\n"
751 "separated by spaces. If omitted, %s uses the last message typed.\n"),
752 progname);
754 jleave:
755 NYD_LEAVE;
756 return ret;
759 FL int
760 c_cwd(void *v)
762 char buf[PATH_MAX]; /* TODO getcwd(3) may return a larger value */
763 NYD_ENTER;
765 if (getcwd(buf, sizeof buf) != NULL) {
766 puts(buf);
767 v = (void*)0x1;
768 } else {
769 n_perr(_("getcwd"), 0);
770 v = NULL;
772 NYD_LEAVE;
773 return (v == NULL);
776 FL int
777 c_chdir(void *v)
779 char **arglist = v;
780 char const *cp;
781 NYD_ENTER;
783 if (*arglist == NULL)
784 cp = homedir;
785 else if ((cp = file_expand(*arglist)) == NULL)
786 goto jleave;
787 if (chdir(cp) == -1) {
788 n_perr(cp, 0);
789 cp = NULL;
791 jleave:
792 NYD_LEAVE;
793 return (cp == NULL);
796 FL int
797 c_reply(void *v)
799 int rv;
800 NYD_ENTER;
802 rv = (*_reply_or_Reply('r'))(v, FAL0);
803 NYD_LEAVE;
804 return rv;
807 FL int
808 c_replyall(void *v)
810 int rv;
811 NYD_ENTER;
813 rv = _reply(v, FAL0);
814 NYD_LEAVE;
815 return rv;
818 FL int
819 c_replysender(void *v)
821 int rv;
822 NYD_ENTER;
824 rv = _Reply(v, FAL0);
825 NYD_LEAVE;
826 return rv;
829 FL int
830 c_Reply(void *v)
832 int rv;
833 NYD_ENTER;
835 rv = (*_reply_or_Reply('R'))(v, FAL0);
836 NYD_LEAVE;
837 return rv;
840 FL int
841 c_Lreply(void *v)
843 int rv;
844 NYD_ENTER;
846 rv = _list_reply(v, HF_LIST_REPLY);
847 NYD_LEAVE;
848 return rv;
851 FL int
852 c_followup(void *v)
854 int rv;
855 NYD_ENTER;
857 rv = (*_reply_or_Reply('r'))(v, TRU1);
858 NYD_LEAVE;
859 return rv;
862 FL int
863 c_followupall(void *v)
865 int rv;
866 NYD_ENTER;
868 rv = _reply(v, TRU1);
869 NYD_LEAVE;
870 return rv;
873 FL int
874 c_followupsender(void *v)
876 int rv;
877 NYD_ENTER;
879 rv = _Reply(v, TRU1);
880 NYD_LEAVE;
881 return rv;
884 FL int
885 c_Followup(void *v)
887 int rv;
888 NYD_ENTER;
890 rv = (*_reply_or_Reply('R'))(v, TRU1);
891 NYD_LEAVE;
892 return rv;
895 FL int
896 c_forward(void *v)
898 int rv;
899 NYD_ENTER;
901 rv = _fwd(v, 0);
902 NYD_LEAVE;
903 return rv;
906 FL int
907 c_Forward(void *v)
909 int rv;
910 NYD_ENTER;
912 rv = _fwd(v, 1);
913 NYD_LEAVE;
914 return rv;
917 FL int
918 c_resend(void *v)
920 int rv;
921 NYD_ENTER;
923 rv = _resend1(v, TRU1);
924 NYD_LEAVE;
925 return rv;
928 FL int
929 c_Resend(void *v)
931 int rv;
932 NYD_ENTER;
934 rv = _resend1(v, FAL0);
935 NYD_LEAVE;
936 return rv;
939 FL int
940 c_preserve(void *v)
942 int *msgvec = v, *ip, mesg, rv = 1;
943 struct message *mp;
944 NYD_ENTER;
946 if (pstate & PS_EDIT) {
947 printf(_("Cannot \"preserve\" in a system mailbox\n"));
948 goto jleave;
951 for (ip = msgvec; *ip != 0; ++ip) {
952 mesg = *ip;
953 mp = message + mesg - 1;
954 mp->m_flag |= MPRESERVE;
955 mp->m_flag &= ~MBOX;
956 setdot(mp);
957 pstate |= PS_DID_PRINT_DOT;
959 rv = 0;
960 jleave:
961 NYD_LEAVE;
962 return rv;
965 FL int
966 c_unread(void *v)
968 int *msgvec = v, *ip;
969 NYD_ENTER;
971 for (ip = msgvec; *ip != 0; ++ip) {
972 setdot(message + *ip - 1);
973 dot->m_flag &= ~(MREAD | MTOUCH);
974 dot->m_flag |= MSTATUS;
975 #ifdef HAVE_IMAP
976 if (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)
977 imap_unread(message + *ip - 1, *ip); /* TODO return? */
978 #endif
979 pstate |= PS_DID_PRINT_DOT;
981 NYD_LEAVE;
982 return 0;
985 FL int
986 c_seen(void *v)
988 int *msgvec = v, *ip;
989 NYD_ENTER;
991 for (ip = msgvec; *ip != 0; ++ip) {
992 struct message *mp = message + *ip - 1;
993 setdot(mp);
994 touch(mp);
996 NYD_LEAVE;
997 return 0;
1000 FL int
1001 c_messize(void *v)
1003 int *msgvec = v, *ip, mesg;
1004 struct message *mp;
1005 NYD_ENTER;
1007 for (ip = msgvec; *ip != 0; ++ip) {
1008 mesg = *ip;
1009 mp = message + mesg - 1;
1010 printf("%d: ", mesg);
1011 if (mp->m_xlines > 0)
1012 printf("%ld", mp->m_xlines);
1013 else
1014 putchar(' ');
1015 printf("/%lu\n", (ul_i)mp->m_xsize);
1017 NYD_LEAVE;
1018 return 0;
1021 FL int
1022 c_file(void *v)
1024 int rv;
1025 NYD_ENTER;
1027 rv = _c_file(v, FEDIT_NONE);
1028 NYD_LEAVE;
1029 return rv;
1032 FL int
1033 c_File(void *v)
1035 int rv;
1036 NYD_ENTER;
1038 rv = _c_file(v, FEDIT_RDONLY);
1039 NYD_LEAVE;
1040 return rv;
1043 FL int
1044 c_echo(void *v)
1046 char const **argv = v, **ap, *cp;
1047 int c;
1048 NYD_ENTER;
1050 for (ap = argv; *ap != NULL; ++ap) {
1051 cp = *ap;
1052 if ((cp = fexpand(cp, FEXP_NSHORTCUT)) != NULL) {
1053 if (ap != argv)
1054 putchar(' ');
1055 c = 0;
1056 while (*cp != '\0' && (c = expand_shell_escape(&cp, FAL0)) > 0)
1057 putchar(c);
1058 /* \c ends overall processing */
1059 if (c < 0)
1060 goto jleave;
1063 putchar('\n');
1064 jleave:
1065 NYD_LEAVE;
1066 return 0;
1069 FL int
1070 c_newmail(void *v)
1072 int val = 1, mdot;
1073 NYD_ENTER;
1074 UNUSED(v);
1076 if (
1077 #ifdef HAVE_IMAP
1078 (mb.mb_type != MB_IMAP || imap_newmail(1)) &&
1079 #endif
1080 (val = setfile(mailname,
1081 FEDIT_NEWMAIL | ((mb.mb_perm & MB_DELE) ? 0 : FEDIT_RDONLY))
1082 ) == 0) {
1083 mdot = getmdot(1);
1084 setdot(message + mdot - 1);
1086 NYD_LEAVE;
1087 return val;
1090 FL int
1091 c_flag(void *v)
1093 struct message *m;
1094 int *msgvec = v, *ip;
1095 NYD_ENTER;
1097 for (ip = msgvec; *ip != 0; ++ip) {
1098 m = message + *ip - 1;
1099 setdot(m);
1100 if (!(m->m_flag & (MFLAG | MFLAGGED)))
1101 m->m_flag |= MFLAG | MFLAGGED;
1103 NYD_LEAVE;
1104 return 0;
1107 FL int
1108 c_unflag(void *v)
1110 struct message *m;
1111 int *msgvec = v, *ip;
1112 NYD_ENTER;
1114 for (ip = msgvec; *ip != 0; ++ip) {
1115 m = message + *ip - 1;
1116 setdot(m);
1117 if (m->m_flag & (MFLAG | MFLAGGED)) {
1118 m->m_flag &= ~(MFLAG | MFLAGGED);
1119 m->m_flag |= MUNFLAG;
1122 NYD_LEAVE;
1123 return 0;
1126 FL int
1127 c_answered(void *v)
1129 struct message *m;
1130 int *msgvec = v, *ip;
1131 NYD_ENTER;
1133 for (ip = msgvec; *ip != 0; ++ip) {
1134 m = message + *ip - 1;
1135 setdot(m);
1136 if (!(m->m_flag & (MANSWER | MANSWERED)))
1137 m->m_flag |= MANSWER | MANSWERED;
1139 NYD_LEAVE;
1140 return 0;
1143 FL int
1144 c_unanswered(void *v)
1146 struct message *m;
1147 int *msgvec = v, *ip;
1148 NYD_ENTER;
1150 for (ip = msgvec; *ip != 0; ++ip) {
1151 m = message + *ip - 1;
1152 setdot(m);
1153 if (m->m_flag & (MANSWER | MANSWERED)) {
1154 m->m_flag &= ~(MANSWER | MANSWERED);
1155 m->m_flag |= MUNANSWER;
1158 NYD_LEAVE;
1159 return 0;
1162 FL int
1163 c_draft(void *v)
1165 struct message *m;
1166 int *msgvec = v, *ip;
1167 NYD_ENTER;
1169 for (ip = msgvec; *ip != 0; ++ip) {
1170 m = message + *ip - 1;
1171 setdot(m);
1172 if (!(m->m_flag & (MDRAFT | MDRAFTED)))
1173 m->m_flag |= MDRAFT | MDRAFTED;
1175 NYD_LEAVE;
1176 return 0;
1179 FL int
1180 c_undraft(void *v)
1182 struct message *m;
1183 int *msgvec = v, *ip;
1184 NYD_ENTER;
1186 for (ip = msgvec; *ip != 0; ++ip) {
1187 m = message + *ip - 1;
1188 setdot(m);
1189 if (m->m_flag & (MDRAFT | MDRAFTED)) {
1190 m->m_flag &= ~(MDRAFT | MDRAFTED);
1191 m->m_flag |= MUNDRAFT;
1194 NYD_LEAVE;
1195 return 0;
1198 FL int
1199 c_noop(void *v)
1201 int rv = 0;
1202 NYD_ENTER;
1203 UNUSED(v);
1205 switch (mb.mb_type) {
1206 case MB_IMAP:
1207 #ifdef HAVE_IMAP
1208 imap_noop();
1209 #else
1210 rv = c_cmdnotsupp(NULL);
1211 #endif
1212 break;
1213 case MB_POP3:
1214 #ifdef HAVE_POP3
1215 pop3_noop();
1216 #else
1217 rv = c_cmdnotsupp(NULL);
1218 #endif
1219 break;
1220 default:
1221 break;
1223 NYD_LEAVE;
1224 return rv;
1227 FL int
1228 c_remove(void *v)
1230 char const *fmt;
1231 size_t fmt_len;
1232 char **args = v, *name;
1233 int ec = 0;
1234 NYD_ENTER;
1236 if (*args == NULL) {
1237 n_err(_("Synopsis: `remove' mailbox ...\n"));
1238 ec = 1;
1239 goto jleave;
1242 fmt = _("Remove \"%s\" (y/n) ? ");
1243 fmt_len = strlen(fmt);
1244 do {
1245 if ((name = expand(*args)) == NULL)
1246 continue;
1248 if (!strcmp(name, mailname)) {
1249 n_err(_("Cannot remove current mailbox \"%s\"\n"), name);
1250 ec |= 1;
1251 continue;
1254 size_t vl = strlen(name) + fmt_len +1;
1255 char *vb = ac_alloc(vl);
1256 bool_t asw;
1257 snprintf(vb, vl, fmt, name);
1258 asw = getapproval(vb, TRU1);
1259 ac_free(vb);
1260 if (!asw)
1261 continue;
1264 switch (which_protocol(name)) {
1265 case PROTO_FILE:
1266 if (unlink(name) == -1) { /* TODO do not handle .gz .bz2 .xz.. */
1267 n_perr(name, 0);
1268 ec |= 1;
1270 break;
1271 case PROTO_POP3:
1272 n_err(_("Cannot remove POP3 mailbox \"%s\"\n"),name);
1273 ec |= 1;
1274 break;
1275 case PROTO_IMAP:
1276 #ifdef HAVE_IMAP
1277 if (imap_remove(name) != OKAY)
1278 #endif
1279 ec |= 1;
1280 break;
1281 case PROTO_MAILDIR:
1282 if (maildir_remove(name) != OKAY)
1283 ec |= 1;
1284 break;
1285 case PROTO_UNKNOWN:
1286 n_err(_("Unknown protocol in \"%s\"; not removed\n"), name);
1287 ec |= 1;
1288 break;
1290 } while (*++args != NULL);
1291 jleave:
1292 NYD_LEAVE;
1293 return ec;
1296 FL int
1297 c_rename(void *v)
1299 char **args = v, *old, *new;
1300 enum protocol oldp, newp;
1301 int ec;
1302 NYD_ENTER;
1304 ec = 1;
1306 if (args[0] == NULL || args[1] == NULL || args[2] != NULL) {
1307 n_err(_("Synopsis: `rename' old new\n"));
1308 goto jleave;
1311 if ((old = expand(args[0])) == NULL)
1312 goto jleave;
1313 oldp = which_protocol(old);
1314 if ((new = expand(args[1])) == NULL)
1315 goto jleave;
1316 newp = which_protocol(new);
1318 if (!strcmp(old, mailname) || !strcmp(new, mailname)) {
1319 n_err(_("Cannot rename current mailbox \"%s\"\n"), old);
1320 goto jleave;
1322 if ((oldp == PROTO_IMAP || newp == PROTO_IMAP) && oldp != newp) {
1323 fprintf(stderr, _("Can only rename folders of same type.\n"));
1324 goto jleave;
1327 ec = 0;
1329 if (newp == PROTO_POP3)
1330 goto jnopop3;
1331 switch (oldp) {
1332 case PROTO_FILE:
1333 if (link(old, new) == -1) {
1334 switch (errno) {
1335 case EACCES:
1336 case EEXIST:
1337 case ENAMETOOLONG:
1338 case ENOENT:
1339 case ENOSPC:
1340 case EXDEV:
1341 n_perr(new, 0);
1342 break;
1343 default:
1344 n_perr(old, 0);
1345 break;
1347 ec |= 1;
1348 } else if (unlink(old) == -1) {
1349 n_perr(old, 0);
1350 ec |= 1;
1352 break;
1353 case PROTO_MAILDIR:
1354 if (rename(old, new) == -1) {
1355 n_perr(old, 0);
1356 ec |= 1;
1358 break;
1359 case PROTO_POP3:
1360 jnopop3:
1361 n_err(_("Cannot rename POP3 mailboxes\n"));
1362 ec |= 1;
1363 break;
1364 #ifdef HAVE_IMAP
1365 case PROTO_IMAP:
1366 if (imap_rename(old, new) != OKAY)
1367 ec |= 1;
1368 break;
1369 #endif
1370 case PROTO_UNKNOWN:
1371 default:
1372 n_err(_("Unknown protocol in \"%s\" and \"%s\"; not renamed\n"),
1373 old, new);
1374 ec |= 1;
1375 break;
1377 jleave:
1378 NYD_LEAVE;
1379 return ec;
1382 FL int
1383 c_urlencode(void *v) /* XXX IDNA?? */
1385 char **ap;
1386 NYD_ENTER;
1388 for (ap = v; *ap != NULL; ++ap) {
1389 char *in = *ap, *out = urlxenc(in, FAL0);
1391 printf(" in: <%s> (%" PRIuZ " bytes)\nout: <%s> (%" PRIuZ " bytes)\n",
1392 in, strlen(in), out, strlen(out));
1394 NYD_LEAVE;
1395 return 0;
1398 FL int
1399 c_urldecode(void *v) /* XXX IDNA?? */
1401 char **ap;
1402 NYD_ENTER;
1404 for (ap = v; *ap != NULL; ++ap) {
1405 char *in = *ap, *out = urlxdec(in);
1407 printf(" in: <%s> (%" PRIuZ " bytes)\nout: <%s> (%" PRIuZ " bytes)\n",
1408 in, strlen(in), out, strlen(out));
1410 NYD_LEAVE;
1411 return 0;
1414 /* s-it-mode */