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>.
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
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
38 #ifndef HAVE_AMALGAMATION
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
);
78 static int _c_file(void *v
, enum fedit_mode fm
);
87 if (subj
== NULL
|| *subj
== '\0')
92 mime_fromhdr(&in
, &out
, TD_ISPR
| TD_ICONV
);
94 if ((newsubj
= subject_re_trim(out
.s
)) != out
.s
)
95 newsubj
= savestr(out
.s
);
97 /* RFC mandates english "Re: " */
98 newsubj
= salloc(out
.l
+ 4 +1);
99 sstpcpy(sstpcpy(newsubj
, "Re: "), out
.s
);
109 _bangexp(char **str
, size_t *size
)
114 size_t sz
, i
, j
, bangbufsize
;
117 dobang
= ok_blook(bang
);
119 bangbuf
= smalloc(bangbufsize
= *size
);
123 if ((*str
)[i
] == '!') {
124 sz
= strlen(_bang_buf
);
125 bangbuf
= srealloc(bangbuf
, bangbufsize
+= sz
);
127 memcpy(bangbuf
+ j
, _bang_buf
, sz
+ 1);
133 if ((*str
)[i
] == '\\' && (*str
)[i
+ 1] == '!') {
138 bangbuf
[j
++] = (*str
)[i
++];
142 printf("!%s\n", bangbuf
);
147 *str
= srealloc(*str
, *size
= sz
);
148 memcpy(*str
, bangbuf
, sz
);
150 _bang_buf
= srealloc(_bang_buf
, _bang_size
= sz
);
151 memcpy(_bang_buf
, bangbuf
, sz
);
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
;
165 oldref
= hfield1("references", mp
);
166 oldmsgid
= hfield1("message-id", mp
);
167 if (oldmsgid
== NULL
|| *oldmsgid
== '\0') {
174 oldreflen
= strlen(oldref
);
175 reflen
+= oldreflen
+ 2;
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);
191 memcpy(newref
, oldmsgid
, oldmsgidlen
+1);
192 n
= extract(newref
, GREF
);
195 /* Limit number of references TODO better on parser side */
196 while (n
->n_flink
!= NULL
)
198 for (i
= 1; i
<= REFERENCES_MAX
; ++i
) {
199 if (n
->n_blink
!= NULL
)
206 if (ok_blook(reply_in_same_charset
) &&
207 (cp
= hfield1("content-type", mp
)) != NULL
)
208 head
->h_charset
= mime_param_get("charset", cp
);
214 _list_reply(int *msgvec
, enum header_flags hf
)
218 char const *reply_to
, *rcv
, *cp
;
220 struct name
*rt
, *mft
, *np
;
224 /* TODO Since we may recur and do stuff with message lists we need to save
225 * TODO away the argument vector as long as that isn't done by machinery */
228 for (i
= 0; msgvec
[i
] != 0; ++i
)
231 save_msgvec
= ac_alloc(sizeof(*save_msgvec
) * i
);
233 save_msgvec
[i
] = msgvec
[i
];
234 msgvec
= save_msgvec
;
238 mp
= message
+ *msgvec
- 1;
242 memset(&head
, 0, sizeof head
);
245 head
.h_subject
= _reedit(hfield1("subject", mp
));
246 gf
= ok_blook(fullnames
) ? GFULL
: GSKIN
;
250 if ((reply_to
= hfield1("reply-to", mp
)) != NULL
&&
251 (cp
= ok_vlook(reply_to_honour
)) != NULL
&&
252 (rt
= checkaddrs(lextract(reply_to
, GTO
| gf
), EACM_STRICT
, NULL
)
254 char const *tr
= _("Reply-To \"%s%s\"");
255 size_t l
= strlen(tr
) + strlen(rt
->n_name
) + 3 +1;
256 char *sp
= salloc(l
);
258 snprintf(sp
, l
, tr
, rt
->n_name
, (rt
->n_flink
!= NULL
? "..." : ""));
259 if (quadify(cp
, UIZ_MAX
, sp
, TRU1
) > FAL0
)
263 if (rcv
== NULL
&& (rcv
= hfield1("from", mp
)) == NULL
)
268 if (ok_blook(recipients_in_cc
) && (cp
= hfield1("to", mp
)) != NULL
)
269 np
= lextract(cp
, GCC
| gf
);
270 if ((cp
= hfield1("cc", mp
)) != NULL
)
271 np
= cat(np
, lextract(cp
, GCC
| gf
));
273 head
.h_cc
= delete_alternates(np
);
278 np
= (rcv
== reply_to
) ? namelist_dup(rt
, GTO
| gf
)
279 : lextract(rcv
, GTO
| gf
);
280 if (!ok_blook(recipients_in_cc
) && (cp
= hfield1("to", mp
)) != NULL
)
281 np
= cat(np
, lextract(cp
, GTO
| gf
));
282 /* Delete my name from reply list, and with it, all my alternate names */
283 np
= delete_alternates(np
);
285 np
= lextract(rcv
, GTO
| gf
);
288 /* The user may have send this to himself, don't ignore that */
289 namelist_vaporise_head(&head
, EACM_NORMAL
, FAL0
, NULL
);
290 if (head
.h_to
== NULL
)
293 /* Mail-Followup-To: */
295 if (ok_vlook(followup_to_honour
) != NULL
&&
296 (cp
= hfield1("mail-followup-to", mp
)) != NULL
&&
297 (mft
= np
= checkaddrs(lextract(cp
, GTO
| gf
), EACM_STRICT
, NULL
)
299 char const *tr
= _("Followup-To \"%s%s\"");
300 size_t l
= strlen(tr
) + strlen(np
->n_name
) + 3 +1;
301 char *sp
= salloc(l
);
303 snprintf(sp
, l
, tr
, np
->n_name
, (np
->n_flink
!= NULL
? "..." : ""));
304 if (quadify(ok_vlook(followup_to_honour
), UIZ_MAX
, sp
, TRU1
) > FAL0
) {
308 mft
= namelist_vaporise_head(&head
, EACM_STRICT
, FAL0
, NULL
);
313 /* Special massage for list (follow-up) messages */
314 if (mft
!= NULL
|| (hf
& HF_LIST_REPLY
) || ok_blook(followup_to
)) {
315 /* Learn about a possibly sending mailing list; use do for break; */
316 if ((cp
= hfield1("list-post", mp
)) != NULL
) do {
319 if ((x
= lextract(cp
, GEXTRA
| GSKIN
)) == NULL
|| x
->n_flink
!= NULL
||
320 (cp
= url_mailto_to_address(x
->n_name
)) == NULL
||
321 /* XXX terribly wasteful to create a new name, and can't we find
322 * XXX a way to mitigate that?? */
323 is_addr_invalid(x
= nalloc(cp
, GEXTRA
| GSKIN
), EACM_STRICT
)) {
324 if (options
& OPT_D_V
)
325 n_err(_("Message contains invalid \"List-Post:\" header\n"));
331 /* A special case has been seen on e.g. ietf-announce@ietf.org:
332 * these usually post to multiple groups, with ietf-announce@
333 * in List-Post:, but with Reply-To: set to ietf@ietf.org (since
334 * -announce@ is only used for announcements, say).
335 * So our desire is to honour this request and actively overwrite
336 * List-Post: for our purpose; but only if its a single address.
337 * However, to avoid ambiguities with users that place themselve in
338 * Reply-To: and mailing lists which don't overwrite this (or only
339 * extend this, shall such exist), only do so if reply_to exists of
340 * a single address which points to the same domain as List-Post: */
341 if (reply_to
!= NULL
&& rt
->n_flink
== NULL
&&
342 name_is_same_domain(x
, rt
))
343 cp
= rt
->n_name
; /* rt is EACM_STRICT tested */
345 /* "Automatically `mlist'" the List-Post: address temporarily */
346 if (is_mlist(cp
, FAL0
) == MLIST_OTHER
)
347 head
.h_list_post
= cp
;
352 /* In case of list replies we actively sort out any non-list recipient,
353 * but _only_ if we did not honour a MFT:, assuming that members of MFT
354 * were there for a reason; cp is still List-Post:/eqivalent */
355 if ((hf
& HF_LIST_REPLY
) && mft
== NULL
) {
356 struct name
*nhp
= head
.h_to
;
359 while (nhp
!= NULL
) {
363 if ((cp
!= NULL
&& !asccasecmp(cp
, np
->n_name
)) ||
364 is_mlist(np
->n_name
, FAL0
) != MLIST_OTHER
) {
365 np
->n_type
= (np
->n_type
& ~GMASK
) | GTO
;
366 np
->n_flink
= head
.h_to
;
370 if ((nhp
= head
.h_cc
) != NULL
) {
377 make_ref_and_cs(mp
, &head
);
379 if (ok_blook(quote_as_attachment
)) {
380 head
.h_attach
= csalloc(1, sizeof *head
.h_attach
);
381 head
.h_attach
->a_msgno
= *msgvec
;
382 head
.h_attach
->a_content_description
= _("Original message content");
385 if (mail1(&head
, 1, mp
, NULL
, !!(hf
& HF_RECIPIENT_RECORD
), 0) == OKAY
&&
386 ok_blook(markanswered
) && !(mp
->m_flag
& MANSWERED
))
387 mp
->m_flag
|= MANSWER
| MANSWERED
;
389 if (*++msgvec
!= 0) {
390 /* TODO message (error) ring.., less sleep */
391 printf(_("Waiting a second before proceeding to the next message..\n"));
397 ac_free(save_msgvec
);
403 (*_reply_or_Reply(char c
))(int *, bool_t
)
405 int (*rv
)(int*, bool_t
);
408 rv
= (ok_blook(flipr
) ^ (c
== 'R')) ? &_Reply
: &_reply
;
414 _reply(int *msgvec
, bool_t recipient_record
)
419 rv
= _list_reply(msgvec
, recipient_record
? HF_RECIPIENT_RECORD
: HF_NONE
);
425 _Reply(int *msgvec
, bool_t recipient_record
)
434 memset(&head
, 0, sizeof head
);
435 gf
= ok_blook(fullnames
) ? GFULL
: GSKIN
;
437 for (ap
= msgvec
; *ap
!= 0; ++ap
) {
441 mp
= message
+ *ap
- 1;
445 if ((rp
= hfield1("reply-to", mp
)) != NULL
&&
446 (cp
= ok_vlook(reply_to_honour
)) != NULL
&&
447 (rt
= checkaddrs(lextract(rp
, GTO
| gf
), EACM_STRICT
, NULL
)
449 char const *tr
= _("Reply-To \"%s%s\"");
450 size_t l
= strlen(tr
) + strlen(rt
->n_name
) + 3 +1;
451 char *sp
= salloc(l
);
453 snprintf(sp
, l
, tr
, rt
->n_name
, (rt
->n_flink
!= NULL
? "..." : ""));
454 if (quadify(cp
, UIZ_MAX
, sp
, TRU1
) > FAL0
) {
455 head
.h_to
= cat(head
.h_to
, rt
);
460 if ((cp
= hfield1("from", mp
)) == NULL
)
462 head
.h_to
= cat(head
.h_to
, lextract(cp
, GTO
| gf
));
464 if (head
.h_to
== NULL
)
467 mp
= message
+ msgvec
[0] - 1;
468 head
.h_subject
= hfield1("subject", mp
);
469 head
.h_subject
= _reedit(head
.h_subject
);
470 make_ref_and_cs(mp
, &head
);
472 if (ok_blook(quote_as_attachment
)) {
473 head
.h_attach
= csalloc(1, sizeof *head
.h_attach
);
474 head
.h_attach
->a_msgno
= *msgvec
;
475 head
.h_attach
->a_content_description
= _("Original message content");
478 if (mail1(&head
, 1, mp
, NULL
, recipient_record
, 0) == OKAY
&&
479 ok_blook(markanswered
) && !(mp
->m_flag
& MANSWERED
))
480 mp
->m_flag
|= MANSWER
| MANSWERED
;
487 _fwd(char *str
, int recipient_record
)
493 bool_t f
, forward_as_attachment
;
496 if ((recipient
= laststring(str
, &f
, TRU1
)) == NULL
) {
497 puts(_("No recipient specified."));
501 forward_as_attachment
= ok_blook(forward_as_attachment
);
502 msgvec
= salloc((msgCount
+ 2) * sizeof *msgvec
);
505 *msgvec
= first(0, MMNORM
);
507 if (pstate
& PS_HOOK_MASK
) {
511 printf(_("No messages to forward.\n"));
515 } else if (getmsglist(str
, msgvec
, 0) < 0)
519 if (pstate
& PS_HOOK_MASK
) {
523 printf(_("No applicable messages.\n"));
526 if (msgvec
[1] != 0) {
527 printf(_("Cannot forward multiple messages at once\n"));
531 memset(&head
, 0, sizeof head
);
532 if ((head
.h_to
= lextract(recipient
,
533 (GTO
| (ok_blook(fullnames
) ? GFULL
: GSKIN
)))) == NULL
)
536 mp
= message
+ *msgvec
- 1;
538 if (forward_as_attachment
) {
539 head
.h_attach
= csalloc(1, sizeof *head
.h_attach
);
540 head
.h_attach
->a_msgno
= *msgvec
;
541 head
.h_attach
->a_content_description
= _("Forwarded message");
546 head
.h_subject
= hfield1("subject", mp
);
547 head
.h_subject
= __fwdedit(head
.h_subject
);
548 mail1(&head
, 1, (forward_as_attachment
? NULL
: mp
), NULL
, recipient_record
,
557 __fwdedit(char *subj
)
560 char *newsubj
= NULL
;
563 if (subj
== NULL
|| *subj
== '\0')
568 mime_fromhdr(&in
, &out
, TD_ISPR
| TD_ICONV
);
570 newsubj
= salloc(out
.l
+ 6);
571 memcpy(newsubj
, "Fwd: ", 5); /* XXX localizable */
572 memcpy(newsubj
+ 5, out
.s
, out
.l
+1);
580 _resend1(void *v
, bool_t add_resent
)
583 struct name
*to
, *sn
;
589 msgvec
= salloc((msgCount
+ 2) * sizeof *msgvec
);
590 name
= laststring(str
, &f
, TRU1
);
592 puts(_("No recipient specified."));
597 *msgvec
= first(0, MMNORM
);
599 if (pstate
& PS_HOOK_MASK
) {
603 puts(_("No applicable messages."));
607 } else if (getmsglist(str
, msgvec
, 0) < 0)
611 if (pstate
& PS_HOOK_MASK
) {
615 printf("No applicable messages.\n");
619 sn
= nalloc(name
, GTO
| GSKIN
);
620 to
= usermap(sn
, FAL0
);
621 for (ip
= msgvec
; *ip
!= 0 && UICMP(z
, PTR2SIZE(ip
- msgvec
), <, msgCount
);
623 if (resend_msg(message
+ *ip
- 1, to
, add_resent
) != OKAY
)
632 _c_file(void *v
, enum fedit_mode fm
)
644 if (pstate
& PS_HOOK_MASK
) {
645 n_err(_("Cannot change folder from within a hook\n"));
650 save_mbox_for_possible_quitstuff();
652 i
= setfile(*argv
, fm
);
657 assert(!(fm
& FEDIT_NEWMAIL
));
658 check_folder_hook(FAL0
);
660 if (i
> 0 && !ok_blook(emptystart
)) {
664 announce(ok_blook(bsdcompat
) || ok_blook(bsdannounce
));
674 char const *sh
= NULL
;
678 sighandler_type sigint
;
681 cmd
= smalloc(cmdsize
= strlen(str
) +1);
682 memcpy(cmd
, str
, cmdsize
);
683 _bangexp(&cmd
, &cmdsize
);
684 if ((sh
= ok_vlook(SHELL
)) == NULL
)
687 sigint
= safe_signal(SIGINT
, SIG_IGN
);
689 run_command(sh
, &mask
, -1, -1, "-c", cmd
, NULL
);
690 safe_signal(SIGINT
, sigint
);
701 sighandler_type sigint
;
706 if ((sh
= ok_vlook(SHELL
)) == NULL
)
709 sigint
= safe_signal(SIGINT
, SIG_IGN
);
710 run_command(sh
, 0, -1, -1, NULL
, NULL
, NULL
);
711 safe_signal(SIGINT
, sigint
);
727 #ifdef HAVE_DOCSTRINGS
728 ret
= !print_comm_docstr(arg
);
730 n_err(_("Unknown command: `%s'\n"), arg
);
732 ret
= c_cmdnotsupp(NULL
);
737 /* Very ugly, but take care for compiler supported string lengths :( */
738 fputs(progname
, stdout
);
740 " commands -- \"<msglist>\" denotes message specifications,\n"
741 "e.g., \"1-5\", \":n\" or \".\", separated by spaces:\n"), stdout
);
744 "type <msglist> type (alias: `print') messages (honour `retain' etc.)\n"
745 "Type <msglist> like `type' but always show all headers\n"
746 "next goto and type next message\n"
747 "from <msglist> print header summary for the given list (\"search\")\n"
748 "headers header summary for messages surrounding \"dot\"\n"
749 "delete <msglist> delete messages (can be `undelete'd)\n"),
754 "save <msglist> folder append messages to folder and mark as saved\n"
755 "copy <msglist> folder like `save', but don't mark them (`move' moves)\n"
756 "write <msglist> file write message contents to file (prompts for parts)\n"
757 "Reply <msglist> reply to message senders only\n"
758 "reply <msglist> like `Reply', but address all recipients\n"
759 "Lreply <msglist> forced mailing-list `reply' (see `mlist')\n"),
764 "mail <recipients> compose a mail for the given recipients\n"
765 "file folder change to another mailbox\n"
766 "File folder like `file', but open readonly\n"
767 "quit quit and apply changes to the current mailbox\n"
768 "xit or exit like `quit', but discard changes\n"
769 "!shell command shell escape\n"
770 "list list names of all available commands\n"),
781 char buf
[PATH_MAX
]; /* TODO getcwd(3) may return a larger value */
784 if (getcwd(buf
, sizeof buf
) != NULL
) {
788 n_perr(_("getcwd"), 0);
802 if (*arglist
== NULL
)
804 else if ((cp
= file_expand(*arglist
)) == NULL
)
806 if (chdir(cp
) == -1) {
821 rv
= (*_reply_or_Reply('r'))(v
, FAL0
);
832 rv
= _reply(v
, FAL0
);
838 c_replysender(void *v
)
843 rv
= _Reply(v
, FAL0
);
854 rv
= (*_reply_or_Reply('R'))(v
, FAL0
);
865 rv
= _list_reply(v
, HF_LIST_REPLY
);
876 rv
= (*_reply_or_Reply('r'))(v
, TRU1
);
882 c_followupall(void *v
)
887 rv
= _reply(v
, TRU1
);
893 c_followupsender(void *v
)
898 rv
= _Reply(v
, TRU1
);
909 rv
= (*_reply_or_Reply('R'))(v
, TRU1
);
942 rv
= _resend1(v
, TRU1
);
953 rv
= _resend1(v
, FAL0
);
961 int *msgvec
= v
, *ip
, mesg
, rv
= 1;
965 if (pstate
& PS_EDIT
) {
966 printf(_("Cannot \"preserve\" in a system mailbox\n"));
970 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
972 mp
= message
+ mesg
- 1;
973 mp
->m_flag
|= MPRESERVE
;
976 pstate
|= PS_DID_PRINT_DOT
;
987 int *msgvec
= v
, *ip
;
990 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
991 setdot(message
+ *ip
- 1);
992 dot
->m_flag
&= ~(MREAD
| MTOUCH
);
993 dot
->m_flag
|= MSTATUS
;
995 if (mb
.mb_type
== MB_IMAP
|| mb
.mb_type
== MB_CACHE
)
996 imap_unread(message
+ *ip
- 1, *ip
); /* TODO return? */
998 pstate
|= PS_DID_PRINT_DOT
;
1007 int *msgvec
= v
, *ip
;
1010 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
1011 struct message
*mp
= message
+ *ip
- 1;
1022 int *msgvec
= v
, *ip
, mesg
;
1026 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
1028 mp
= message
+ mesg
- 1;
1029 printf("%d: ", mesg
);
1030 if (mp
->m_xlines
> 0)
1031 printf("%ld", mp
->m_xlines
);
1034 printf("/%lu\n", (ul_i
)mp
->m_xsize
);
1046 rv
= _c_file(v
, FEDIT_NONE
);
1057 rv
= _c_file(v
, FEDIT_RDONLY
);
1065 char const **argv
= v
, **ap
, *cp
;
1069 for (ap
= argv
; *ap
!= NULL
; ++ap
) {
1071 if ((cp
= fexpand(cp
, FEXP_NSHORTCUT
)) != NULL
) {
1075 while (*cp
!= '\0' && (c
= expand_shell_escape(&cp
, FAL0
)) > 0)
1077 /* \c ends overall processing */
1097 (mb
.mb_type
!= MB_IMAP
|| imap_newmail(1)) &&
1099 (val
= setfile(mailname
,
1100 FEDIT_NEWMAIL
| ((mb
.mb_perm
& MB_DELE
) ? 0 : FEDIT_RDONLY
))
1103 setdot(message
+ mdot
- 1);
1113 int *msgvec
= v
, *ip
;
1116 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
1117 m
= message
+ *ip
- 1;
1119 if (!(m
->m_flag
& (MFLAG
| MFLAGGED
)))
1120 m
->m_flag
|= MFLAG
| MFLAGGED
;
1130 int *msgvec
= v
, *ip
;
1133 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
1134 m
= message
+ *ip
- 1;
1136 if (m
->m_flag
& (MFLAG
| MFLAGGED
)) {
1137 m
->m_flag
&= ~(MFLAG
| MFLAGGED
);
1138 m
->m_flag
|= MUNFLAG
;
1149 int *msgvec
= v
, *ip
;
1152 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
1153 m
= message
+ *ip
- 1;
1155 if (!(m
->m_flag
& (MANSWER
| MANSWERED
)))
1156 m
->m_flag
|= MANSWER
| MANSWERED
;
1163 c_unanswered(void *v
)
1166 int *msgvec
= v
, *ip
;
1169 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
1170 m
= message
+ *ip
- 1;
1172 if (m
->m_flag
& (MANSWER
| MANSWERED
)) {
1173 m
->m_flag
&= ~(MANSWER
| MANSWERED
);
1174 m
->m_flag
|= MUNANSWER
;
1185 int *msgvec
= v
, *ip
;
1188 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
1189 m
= message
+ *ip
- 1;
1191 if (!(m
->m_flag
& (MDRAFT
| MDRAFTED
)))
1192 m
->m_flag
|= MDRAFT
| MDRAFTED
;
1202 int *msgvec
= v
, *ip
;
1205 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
1206 m
= message
+ *ip
- 1;
1208 if (m
->m_flag
& (MDRAFT
| MDRAFTED
)) {
1209 m
->m_flag
&= ~(MDRAFT
| MDRAFTED
);
1210 m
->m_flag
|= MUNDRAFT
;
1224 switch (mb
.mb_type
) {
1229 rv
= c_cmdnotsupp(NULL
);
1236 rv
= c_cmdnotsupp(NULL
);
1251 char **args
= v
, *name
;
1255 if (*args
== NULL
) {
1256 n_err(_("Synopsis: remove: <mailbox>...\n"));
1261 fmt
= _("Remove \"%s\" (y/n) ? ");
1262 fmt_len
= strlen(fmt
);
1264 if ((name
= expand(*args
)) == NULL
)
1267 if (!strcmp(name
, mailname
)) {
1268 n_err(_("Cannot remove current mailbox \"%s\"\n"), name
);
1273 size_t vl
= strlen(name
) + fmt_len
+1;
1274 char *vb
= ac_alloc(vl
);
1276 snprintf(vb
, vl
, fmt
, name
);
1277 asw
= getapproval(vb
, TRU1
);
1283 switch (which_protocol(name
)) {
1285 if (unlink(name
) == -1) { /* TODO do not handle .gz .bz2 .xz.. */
1291 n_err(_("Cannot remove POP3 mailbox \"%s\"\n"),name
);
1296 if (imap_remove(name
) != OKAY
)
1301 if (maildir_remove(name
) != OKAY
)
1305 n_err(_("Unknown protocol in \"%s\"; not removed\n"), name
);
1309 } while (*++args
!= NULL
);
1318 char **args
= v
, *old
, *new;
1319 enum protocol oldp
, newp
;
1325 if (args
[0] == NULL
|| args
[1] == NULL
|| args
[2] != NULL
) {
1326 n_err(_("Synopsis: rename: <old> <new>\n"));
1330 if ((old
= expand(args
[0])) == NULL
)
1332 oldp
= which_protocol(old
);
1333 if ((new = expand(args
[1])) == NULL
)
1335 newp
= which_protocol(new);
1337 if (!strcmp(old
, mailname
) || !strcmp(new, mailname
)) {
1338 n_err(_("Cannot rename current mailbox \"%s\"\n"), old
);
1341 if ((oldp
== PROTO_IMAP
|| newp
== PROTO_IMAP
) && oldp
!= newp
) {
1342 fprintf(stderr
, _("Can only rename folders of same type.\n"));
1348 if (newp
== PROTO_POP3
)
1352 if (link(old
, new) == -1) {
1367 } else if (unlink(old
) == -1) {
1373 if (rename(old
, new) == -1) {
1380 n_err(_("Cannot rename POP3 mailboxes\n"));
1385 if (imap_rename(old
, new) != OKAY
)
1391 n_err(_("Unknown protocol in \"%s\" and \"%s\"; not renamed\n"),
1402 c_urlencode(void *v
) /* XXX IDNA?? */
1407 for (ap
= v
; *ap
!= NULL
; ++ap
) {
1408 char *in
= *ap
, *out
= urlxenc(in
, FAL0
);
1410 printf(" in: <%s> (%" PRIuZ
" bytes)\nout: <%s> (%" PRIuZ
" bytes)\n",
1411 in
, strlen(in
), out
, strlen(out
));
1418 c_urldecode(void *v
) /* XXX IDNA?? */
1423 for (ap
= v
; *ap
!= NULL
; ++ap
) {
1424 char *in
= *ap
, *out
= urlxdec(in
);
1426 printf(" in: <%s> (%" PRIuZ
" bytes)\nout: <%s> (%" PRIuZ
" bytes)\n",
1427 in
, strlen(in
), out
, strlen(out
));