1 /*
2 * Heirloom mailx - a mail user agent derived from Berkeley Mail.
3 *
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 */
6 /*
7 * Copyright (c) 1980, 1993
8 * The Regents of the University of California. All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 */
38
39 #ifndef lint
40 #ifdef DOSCCS
41 static char sccsid[] = "@(#)cmd3.c 2.86 (gritter) 10/1/07";
42 #endif
43 #endif /* not lint */
44
45 #include <math.h>
46 #include <float.h>
47 #include "rcv.h"
48 #include "extern.h"
49 #include <unistd.h>
50 #include <errno.h>
51
52 /*
53 * Mail -- a mail program
54 *
55 * Still more user commands.
56 */
57
58 static int bangexp(char **str, size_t *size);
59 static void make_ref_and_cs(struct message *mp, struct header *head);
60 static int (*respond_or_Respond(int c))(int *, int);
61 static int respond_internal(int *msgvec, int recipient_record);
62 static char *reedit(char *subj);
63 static char *fwdedit(char *subj);
64 static void onpipe(int signo);
65 static void asort(char **list);
66 static int diction(const void *a, const void *b);
67 static int file1(char *name);
68 static int shellecho(const char *cp);
69 static int Respond_internal(int *msgvec, int recipient_record);
70 static int resend1(void *v, int add_resent);
71 static void list_shortcuts(void);
72 static enum okay delete_shortcut(const char *str);
73 static float huge(void);
74
75 /*
76 * Process a shell escape by saving signals, ignoring signals,
77 * and forking a sh -c
78 */
79 int
shell(void * v)80 shell(void *v)
81 {
82 char *str = v;
83 sighandler_type sigint = safe_signal(SIGINT, SIG_IGN);
84 char *shell;
85 char *cmd;
86 size_t cmdsize;
87
88 cmd = smalloc(cmdsize = strlen(str) + 1);
89 strcpy(cmd, str);
90 if (bangexp(&cmd, &cmdsize) < 0)
91 return 1;
92 if ((shell = value("SHELL")) == NULL)
93 shell = SHELL;
94 run_command(shell, 0, -1, -1, "-c", cmd, NULL);
95 safe_signal(SIGINT, sigint);
96 printf("!\n");
97 free(cmd);
98 return 0;
99 }
100
101 /*
102 * Fork an interactive shell.
103 */
104 /*ARGSUSED*/
105 int
dosh(void * v)106 dosh(void *v)
107 {
108 sighandler_type sigint = safe_signal(SIGINT, SIG_IGN);
109 char *shell;
110
111 if ((shell = value("SHELL")) == NULL)
112 shell = SHELL;
113 run_command(shell, 0, -1, -1, NULL, NULL, NULL);
114 safe_signal(SIGINT, sigint);
115 putchar('\n');
116 return 0;
117 }
118
119 /*
120 * Expand the shell escape by expanding unescaped !'s into the
121 * last issued command where possible.
122 */
123
124 static char *lastbang;
125 static size_t lastbangsize;
126
127 static int
bangexp(char ** str,size_t * size)128 bangexp(char **str, size_t *size)
129 {
130 char *bangbuf;
131 int changed = 0;
132 int dobang = value("bang") != NULL;
133 size_t sz, i, j, bangbufsize;
134
135 bangbuf = smalloc(bangbufsize = *size);
136 i = j = 0;
137 while ((*str)[i]) {
138 if (dobang) {
139 if ((*str)[i] == '!') {
140 sz = strlen(lastbang);
141 bangbuf = srealloc(bangbuf, bangbufsize += sz);
142 changed++;
143 strcpy(&bangbuf[j], lastbang);
144 j += sz;
145 i++;
146 continue;
147 }
148 }
149 if ((*str)[i] == '\\' && (*str)[i + 1] == '!') {
150 bangbuf[j++] = '!';
151 i += 2;
152 changed++;
153 }
154 bangbuf[j++] = (*str)[i++];
155 }
156 bangbuf[j] = '\0';
157 if (changed) {
158 printf("!%s\n", bangbuf);
159 fflush(stdout);
160 }
161 sz = j;
162 if (sz >= *size)
163 *str = srealloc(*str, *size = sz + 1);
164 strcpy(*str, bangbuf);
165 if (sz >= lastbangsize)
166 lastbang = srealloc(lastbang, lastbangsize = sz + 1);
167 strcpy(lastbang, bangbuf);
168 free(bangbuf);
169 return(0);
170 }
171
172 /*ARGSUSED*/
173 int
help(void * v)174 help(void *v)
175 {
176 const char *helptext =
177 " %s commands\n\
178 type <message list> type messages\n\
179 next goto and type next message\n\
180 from <message list> give head lines of messages\n\
181 headers print out active message headers\n\
182 delete <message list> delete messages\n\
183 undelete <message list> undelete messages\n\
184 save <message list> folder append messages to folder and mark as saved\n\
185 copy <message list> folder append messages to folder without marking them\n\
186 write <message list> file append message texts to file, save attachments\n\
187 preserve <message list> keep incoming messages in mailbox even if saved\n\
188 Reply <message list> reply to message senders\n\
189 reply <message list> reply to message senders and all recipients\n\
190 mail addresses mail to specific recipients\n\
191 file folder change to another folder\n\
192 quit quit and apply changes to folder\n\
193 xit quit and discard changes made to folder\n\
194 ! shell escape\n\
195 cd <directory> chdir to directory or home if none given\n\
196 list list names of all available commands\n\
197 \n\
198 A <message list> consists of integers, ranges of same, or other criteria\n\
199 separated by spaces. If omitted, %s uses the last message typed.\n";
200
201 fprintf(stdout, helptext, progname, progname);
202 return(0);
203 }
204
205 /*
206 * Change user's working directory.
207 */
208 int
schdir(void * v)209 schdir(void *v)
210 {
211 char **arglist = v;
212 char *cp;
213
214 if (*arglist == NULL)
215 cp = homedir;
216 else
217 if ((cp = expand(*arglist)) == NULL)
218 return(1);
219 if (chdir(cp) < 0) {
220 perror(cp);
221 return(1);
222 }
223 return 0;
224 }
225
226 static void
make_ref_and_cs(struct message * mp,struct header * head)227 make_ref_and_cs(struct message *mp, struct header *head)
228 {
229 char *oldref, *oldmsgid, *newref, *cp;
230 size_t reflen;
231 unsigned i;
232 struct name *n;
233
234 oldref = hfield("references", mp);
235 oldmsgid = hfield("message-id", mp);
236 if (oldmsgid == NULL || *oldmsgid == '\0') {
237 head->h_ref = NULL;
238 return;
239 }
240 reflen = 1;
241 if (oldref)
242 reflen += strlen(oldref) + 2;
243 if (oldmsgid)
244 reflen += strlen(oldmsgid);
245 newref = ac_alloc(reflen);
246 if (oldref) {
247 strcpy(newref, oldref);
248 if (oldmsgid) {
249 strcat(newref, ", ");
250 strcat(newref, oldmsgid);
251 }
252 } else if (oldmsgid)
253 strcpy(newref, oldmsgid);
254 n = extract(newref, GREF);
255 ac_free(newref);
256 /*
257 * Limit the references to 21 entries.
258 */
259 while (n->n_flink != NULL)
260 n = n->n_flink;
261 for (i = 1; i < 21; i++) {
262 if (n->n_blink != NULL)
263 n = n->n_blink;
264 else
265 break;
266 }
267 n->n_blink = NULL;
268 head->h_ref = n;
269 if (value("reply-in-same-charset") != NULL &&
270 (cp = hfield("content-type", mp)) != NULL)
271 head->h_charset = mime_getparam("charset", cp);
272 }
273
274 static int
respond_or_Respond(int c)275 (*respond_or_Respond(int c))(int *, int)
276 {
277 int opt = 0;
278
279 opt += (value("Replyall") != NULL);
280 opt += (value("flipr") != NULL);
281 return ((opt == 1) ^ (c == 'R')) ? Respond_internal : respond_internal;
282 }
283
284 int
respond(void * v)285 respond(void *v)
286 {
287 return (respond_or_Respond('r'))((int *)v, 0);
288 }
289
290 int
respondall(void * v)291 respondall(void *v)
292 {
293 return respond_internal((int *)v, 0);
294 }
295
296 int
respondsender(void * v)297 respondsender(void *v)
298 {
299 return Respond_internal((int *)v, 0);
300 }
301
302 int
followup(void * v)303 followup(void *v)
304 {
305 return (respond_or_Respond('r'))((int *)v, 1);
306 }
307
308 int
followupall(void * v)309 followupall(void *v)
310 {
311 return respond_internal((int *)v, 1);
312 }
313
314 int
followupsender(void * v)315 followupsender(void *v)
316 {
317 return Respond_internal((int *)v, 1);
318 }
319
320 /*
321 * Reply to a list of messages. Extract each name from the
322 * message header and send them off to mail1()
323 */
324 static int
respond_internal(int * msgvec,int recipient_record)325 respond_internal(int *msgvec, int recipient_record)
326 {
327 int Eflag;
328 struct message *mp;
329 char *cp, *rcv;
330 enum gfield gf = value("fullnames") ? GFULL : GSKIN;
331 struct name *np = NULL;
332 struct header head;
333
334 memset(&head, 0, sizeof head);
335 if (msgvec[1] != 0) {
336 printf(catgets(catd, CATSET, 37,
337 "Sorry, can't reply to multiple messages at once\n"));
338 return(1);
339 }
340 mp = &message[msgvec[0] - 1];
341 touch(mp);
342 setdot(mp);
343 if ((rcv = hfield("reply-to", mp)) == NULL)
344 if ((rcv = hfield("from", mp)) == NULL)
345 rcv = nameof(mp, 1);
346 if (rcv != NULL)
347 np = sextract(rcv, GTO|gf);
348 if ((cp = hfield("to", mp)) != NULL)
349 np = cat(np, sextract(cp, GTO|gf));
350 np = elide(np);
351 /*
352 * Delete my name from the reply list,
353 * and with it, all my alternate names.
354 */
355 np = delete_alternates(np);
356 if (np == NULL)
357 np = sextract(rcv, GTO|gf);
358 head.h_to = np;
359 if ((head.h_subject = hfield("subject", mp)) == NULL)
360 head.h_subject = hfield("subj", mp);
361 head.h_subject = reedit(head.h_subject);
362 if ((cp = hfield("cc", mp)) != NULL) {
363 np = elide(sextract(cp, GCC|gf));
364 np = delete_alternates(np);
365 head.h_cc = np;
366 }
367 make_ref_and_cs(mp, &head);
368 Eflag = value("skipemptybody") != NULL;
369 if (mail1(&head, 1, mp, NULL, recipient_record, 0, 0, Eflag) == OKAY &&
370 value("markanswered") && (mp->m_flag & MANSWERED) == 0)
371 mp->m_flag |= MANSWER|MANSWERED;
372 return(0);
373 }
374
375 /*
376 * Modify the subject we are replying to to begin with Re: if
377 * it does not already.
378 */
379 static char *
reedit(char * subj)380 reedit(char *subj)
381 {
382 char *newsubj;
383 struct str in, out;
384
385 if (subj == NULL || *subj == '\0')
386 return NULL;
387 in.s = subj;
388 in.l = strlen(subj);
389 mime_fromhdr(&in, &out, TD_ISPR|TD_ICONV);
390 if ((out.s[0] == 'r' || out.s[0] == 'R') &&
391 (out.s[1] == 'e' || out.s[1] == 'E') &&
392 out.s[2] == ':')
393 return out.s;
394 newsubj = salloc(out.l + 5);
395 strcpy(newsubj, "Re: ");
396 strcpy(newsubj + 4, out.s);
397 return newsubj;
398 }
399
400 /*
401 * Forward a message to a new recipient, in the sense of RFC 2822.
402 */
403 static int
forward1(char * str,int recipient_record)404 forward1(char *str, int recipient_record)
405 {
406 int Eflag;
407 int *msgvec, f;
408 char *recipient;
409 struct message *mp;
410 struct header head;
411 int forward_as_attachment;
412
413 forward_as_attachment = value("forward-as-attachment") != NULL;
414 msgvec = salloc((msgCount + 2) * sizeof *msgvec);
415 if ((recipient = laststring(str, &f, 0)) == NULL) {
416 puts(catgets(catd, CATSET, 47, "No recipient specified."));
417 return 1;
418 }
419 if (!f) {
420 *msgvec = first(0, MMNORM);
421 if (*msgvec == 0) {
422 if (inhook)
423 return 0;
424 printf("No messages to forward.\n");
425 return 1;
426 }
427 msgvec[1] = 0;
428 }
429 if (f && getmsglist(str, msgvec, 0) < 0)
430 return 1;
431 if (*msgvec == 0) {
432 if (inhook)
433 return 0;
434 printf("No applicable messages.\n");
435 return 1;
436 }
437 if (msgvec[1] != 0) {
438 printf("Cannot forward multiple messages at once\n");
439 return 1;
440 }
441 memset(&head, 0, sizeof head);
442 if ((head.h_to = sextract(recipient,
443 GTO | (value("fullnames") ? GFULL : GSKIN))) == NULL)
444 return 1;
445 mp = &message[*msgvec - 1];
446 if (forward_as_attachment) {
447 head.h_attach = csalloc(1, sizeof *head.h_attach);
448 head.h_attach->a_msgno = *msgvec;
449 } else {
450 touch(mp);
451 setdot(mp);
452 }
453 if ((head.h_subject = hfield("subject", mp)) == NULL)
454 head.h_subject = hfield("subj", mp);
455 head.h_subject = fwdedit(head.h_subject);
456 Eflag = value("skipemptybody") != NULL;
457 mail1(&head, 1, forward_as_attachment ? NULL : mp,
458 NULL, recipient_record, 1, 0, Eflag);
459 return 0;
460 }
461
462 /*
463 * Modify the subject we are replying to to begin with Fwd:.
464 */
465 static char *
fwdedit(char * subj)466 fwdedit(char *subj)
467 {
468 char *newsubj;
469 struct str in, out;
470
471 if (subj == NULL || *subj == '\0')
472 return NULL;
473 in.s = subj;
474 in.l = strlen(subj);
475 mime_fromhdr(&in, &out, TD_ISPR|TD_ICONV);
476 newsubj = salloc(strlen(out.s) + 6);
477 strcpy(newsubj, "Fwd: ");
478 strcpy(&newsubj[5], out.s);
479 free(out.s);
480 return newsubj;
481 }
482
483 /*
484 * The 'forward' command.
485 */
486 int
forwardcmd(void * v)487 forwardcmd(void *v)
488 {
489 return forward1(v, 0);
490 }
491
492 /*
493 * Similar to forward, saving the message in a file named after the
494 * first recipient.
495 */
496 int
Forwardcmd(void * v)497 Forwardcmd(void *v)
498 {
499 return forward1(v, 1);
500 }
501
502 /*
503 * Preserve the named messages, so that they will be sent
504 * back to the system mailbox.
505 */
506 int
preserve(void * v)507 preserve(void *v)
508 {
509 int *msgvec = v;
510 struct message *mp;
511 int *ip, mesg;
512
513 if (edit) {
514 printf(catgets(catd, CATSET, 39,
515 "Cannot \"preserve\" in edit mode\n"));
516 return(1);
517 }
518 for (ip = msgvec; *ip != 0; ip++) {
519 mesg = *ip;
520 mp = &message[mesg-1];
521 mp->m_flag |= MPRESERVE;
522 mp->m_flag &= ~MBOX;
523 setdot(mp);
524 /*
525 * This is now Austin Group Request XCU #20.
526 */
527 did_print_dot = 1;
528 }
529 return(0);
530 }
531
532 /*
533 * Mark all given messages as unread.
534 */
535 int
unread(void * v)536 unread(void *v)
537 {
538 int *msgvec = v;
539 int *ip;
540
541 for (ip = msgvec; *ip != 0; ip++) {
542 setdot(&message[*ip-1]);
543 dot->m_flag &= ~(MREAD|MTOUCH);
544 dot->m_flag |= MSTATUS;
545 if (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)
546 imap_unread(&message[*ip-1], *ip);
547 /*
548 * The "unread" command is not part of POSIX mailx.
549 */
550 did_print_dot = 1;
551 }
552 return(0);
553 }
554
555 /*
556 * Mark all given messages as read.
557 */
558 int
seen(void * v)559 seen(void *v)
560 {
561 int *msgvec = v;
562 int *ip;
563
564 for (ip = msgvec; *ip; ip++) {
565 setdot(&message[*ip-1]);
566 touch(&message[*ip-1]);
567 }
568 return 0;
569 }
570
571 /*
572 * Print the size of each message.
573 */
574 int
messize(void * v)575 messize(void *v)
576 {
577 int *msgvec = v;
578 struct message *mp;
579 int *ip, mesg;
580
581 for (ip = msgvec; *ip != 0; ip++) {
582 mesg = *ip;
583 mp = &message[mesg-1];
584 printf("%d: ", mesg);
585 if (mp->m_xlines > 0)
586 printf("%ld", mp->m_xlines);
587 else
588 putchar(' ');
589 printf("/%lu\n", (unsigned long)mp->m_xsize);
590 }
591 return(0);
592 }
593
594 /*
595 * Quit quickly. If we are sourcing, just pop the input level
596 * by returning an error.
597 */
598 /*ARGSUSED*/
599 int
rexit(void * v)600 rexit(void *v)
601 {
602 if (sourcing)
603 return(1);
604 exit(0);
605 /*NOTREACHED*/
606 }
607
608 static sigjmp_buf pipejmp;
609
610 /*ARGSUSED*/
611 static void
onpipe(int signo)612 onpipe(int signo)
613 {
614 siglongjmp(pipejmp, 1);
615 }
616
617 /*
618 * Set or display a variable value. Syntax is similar to that
619 * of sh.
620 */
621 int
set(void * v)622 set(void *v)
623 {
624 char **arglist = v;
625 struct var *vp;
626 char *cp, *cp2;
627 char **ap, **p;
628 int errs, h, s;
629 FILE *obuf = stdout;
630 int bsdset = value("bsdcompat") != NULL || value("bsdset") != NULL;
631
632 (void)&cp;
633 (void)≈
634 (void)&obuf;
635 (void)&bsdset;
636 if (*arglist == NULL) {
637 for (h = 0, s = 1; h < HSHSIZE; h++)
638 for (vp = variables[h]; vp != NULL; vp = vp->v_link)
639 s++;
640 /*LINTED*/
641 ap = (char **)salloc(s * sizeof *ap);
642 for (h = 0, p = ap; h < HSHSIZE; h++)
643 for (vp = variables[h]; vp != NULL; vp = vp->v_link)
644 *p++ = vp->v_name;
645 *p = NULL;
646 asort(ap);
647 if (is_a_tty[0] && is_a_tty[1] && (cp = value("crt")) != NULL) {
648 if (s > (*cp == '\0' ? screensize() : atoi(cp)) + 3) {
649 cp = get_pager();
650 if (sigsetjmp(pipejmp, 1))
651 goto endpipe;
652 if ((obuf = Popen(cp, "w", NULL, 1)) == NULL) {
653 perror(cp);
654 obuf = stdout;
655 } else
656 safe_signal(SIGPIPE, onpipe);
657 }
658 }
659 for (p = ap; *p != NULL; p++) {
660 if (bsdset)
661 fprintf(obuf, "%s\t%s\n", *p, value(*p));
662 else {
663 if ((cp = value(*p)) != NULL && *cp)
664 fprintf(obuf, "%s=\"%s\"\n",
665 *p, value(*p));
666 else
667 fprintf(obuf, "%s\n", *p);
668 }
669 }
670 endpipe:
671 if (obuf != stdout) {
672 safe_signal(SIGPIPE, SIG_IGN);
673 Pclose(obuf);
674 safe_signal(SIGPIPE, dflpipe);
675 }
676 return(0);
677 }
678 errs = 0;
679 for (ap = arglist; *ap != NULL; ap++) {
680 char *varbuf;
681
682 varbuf = ac_alloc(strlen(*ap) + 1);
683 cp = *ap;
684 cp2 = varbuf;
685 while (*cp != '=' && *cp != '\0')
686 *cp2++ = *cp++;
687 *cp2 = '\0';
688 if (*cp == '\0')
689 cp = "";
690 else
691 cp++;
692 if (equal(varbuf, "")) {
693 printf(catgets(catd, CATSET, 41,
694 "Non-null variable name required\n"));
695 errs++;
696 ac_free(varbuf);
697 continue;
698 }
699 if (varbuf[0] == 'n' && varbuf[1] == 'o')
700 errs += unset_internal(&varbuf[2]);
701 else
702 assign(varbuf, cp);
703 ac_free(varbuf);
704 }
705 return(errs);
706 }
707
708 /*
709 * Unset a bunch of variable values.
710 */
711 int
unset(void * v)712 unset(void *v)
713 {
714 int errs;
715 char **ap;
716
717 errs = 0;
718 for (ap = (char **)v; *ap != NULL; ap++)
719 errs += unset_internal(*ap);
720 return(errs);
721 }
722
723 /*
724 * Put add users to a group.
725 */
726 int
group(void * v)727 group(void *v)
728 {
729 char **argv = v;
730 struct grouphead *gh;
731 struct group *gp;
732 int h;
733 int s;
734 char **ap, *gname, **p;
735
736 if (*argv == NULL) {
737 for (h = 0, s = 1; h < HSHSIZE; h++)
738 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
739 s++;
740 /*LINTED*/
741 ap = (char **)salloc(s * sizeof *ap);
742 for (h = 0, p = ap; h < HSHSIZE; h++)
743 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
744 *p++ = gh->g_name;
745 *p = NULL;
746 asort(ap);
747 for (p = ap; *p != NULL; p++)
748 printgroup(*p);
749 return(0);
750 }
751 if (argv[1] == NULL) {
752 printgroup(*argv);
753 return(0);
754 }
755 gname = *argv;
756 h = hash(gname);
757 if ((gh = findgroup(gname)) == NULL) {
758 gh = (struct grouphead *)scalloc(1, sizeof *gh);
759 gh->g_name = vcopy(gname);
760 gh->g_list = NULL;
761 gh->g_link = groups[h];
762 groups[h] = gh;
763 }
764
765 /*
766 * Insert names from the command list into the group.
767 * Who cares if there are duplicates? They get tossed
768 * later anyway.
769 */
770
771 for (ap = argv+1; *ap != NULL; ap++) {
772 gp = (struct group *)scalloc(1, sizeof *gp);
773 gp->ge_name = vcopy(*ap);
774 gp->ge_link = gh->g_list;
775 gh->g_list = gp;
776 }
777 return(0);
778 }
779
780 /*
781 * Delete the passed groups.
782 */
783 int
ungroup(void * v)784 ungroup(void *v)
785 {
786 char **argv = v;
787
788 if (*argv == NULL) {
789 printf(catgets(catd, CATSET, 209,
790 "Must specify alias or group to remove\n"));
791 return 1;
792 }
793 do
794 remove_group(*argv);
795 while (*++argv != NULL);
796 return 0;
797 }
798
799 /*
800 * Sort the passed string vecotor into ascending dictionary
801 * order.
802 */
803 static void
asort(char ** list)804 asort(char **list)
805 {
806 char **ap;
807
808 for (ap = list; *ap != NULL; ap++)
809 ;
810 if (ap-list < 2)
811 return;
812 qsort(list, ap-list, sizeof(*list), diction);
813 }
814
815 /*
816 * Do a dictionary order comparison of the arguments from
817 * qsort.
818 */
819 static int
diction(const void * a,const void * b)820 diction(const void *a, const void *b)
821 {
822 return(strcmp(*(char **)a, *(char **)b));
823 }
824
825 /*
826 * Change to another file. With no argument, print information about
827 * the current file.
828 */
829 int
cfile(void * v)830 cfile(void *v)
831 {
832 char **argv = v;
833
834 if (argv[0] == NULL) {
835 newfileinfo();
836 return 0;
837 }
838 strncpy(mboxname, expand("&"), sizeof mboxname)[sizeof mboxname-1]='\0';
839 return file1(*argv);
840 }
841
842 static int
file1(char * name)843 file1(char *name)
844 {
845 int i;
846
847 if (inhook) {
848 fprintf(stderr, "Cannot change folder from within a hook.\n");
849 return 1;
850 }
851 i = setfile(name, 0);
852 if (i < 0)
853 return 1;
854 callhook(mailname, 0);
855 if (i > 0 && value("emptystart") == NULL)
856 return 1;
857 announce(value("bsdcompat") != NULL || value("bsdannounce") != NULL);
858 return 0;
859 }
860
861 static int
shellecho(const char * cp)862 shellecho(const char *cp)
863 {
864 int cflag = 0, n;
865 char c;
866
867 while (*cp) {
868 if (*cp == '\\') {
869 switch (*++cp) {
870 case '\0':
871 return cflag;
872 case 'a':
873 putchar('\a');
874 break;
875 case 'b':
876 putchar('\b');
877 break;
878 case 'c':
879 cflag = 1;
880 break;
881 case 'f':
882 putchar('\f');
883 break;
884 case 'n':
885 putchar('\n');
886 break;
887 case 'r':
888 putchar('\r');
889 break;
890 case 't':
891 putchar('\t');
892 break;
893 case 'v':
894 putchar('\v');
895 break;
896 default:
897 putchar(*cp&0377);
898 break;
899 case '0':
900 c = 0;
901 n = 3;
902 while (n-- && octalchar(cp[1]&0377)) {
903 c <<= 3;
904 c |= cp[1] - '0';
905 cp++;
906 }
907 putchar(c);
908 }
909 } else
910 putchar(*cp & 0377);
911 cp++;
912 }
913 return cflag;
914 }
915
916 /*
917 * Expand file names like echo
918 */
919 int
echo(void * v)920 echo(void *v)
921 {
922 char **argv = v;
923 char **ap;
924 char *cp;
925 int cflag = 0;
926
927 for (ap = argv; *ap != NULL; ap++) {
928 cp = *ap;
929 if ((cp = expand(cp)) != NULL) {
930 if (ap != argv)
931 putchar(' ');
932 cflag |= shellecho(cp);
933 }
934 }
935 if (!cflag)
936 putchar('\n');
937 return 0;
938 }
939
940 int
Respond(void * v)941 Respond(void *v)
942 {
943 return (respond_or_Respond('R'))((int *)v, 0);
944 }
945
946 int
Followup(void * v)947 Followup(void *v)
948 {
949 return (respond_or_Respond('R'))((int *)v, 1);
950 }
951
952 /*
953 * Reply to a series of messages by simply mailing to the senders
954 * and not messing around with the To: and Cc: lists as in normal
955 * reply.
956 */
957 static int
Respond_internal(int * msgvec,int recipient_record)958 Respond_internal(int *msgvec, int recipient_record)
959 {
960 int Eflag;
961 struct header head;
962 struct message *mp;
963 enum gfield gf = value("fullnames") ? GFULL : GSKIN;
964 int *ap;
965 char *cp;
966
967 memset(&head, 0, sizeof head);
968 for (ap = msgvec; *ap != 0; ap++) {
969 mp = &message[*ap - 1];
970 touch(mp);
971 setdot(mp);
972 if ((cp = hfield("reply-to", mp)) == NULL)
973 if ((cp = hfield("from", mp)) == NULL)
974 cp = nameof(mp, 2);
975 head.h_to = cat(head.h_to, sextract(cp, GTO|gf));
976 }
977 if (head.h_to == NULL)
978 return 0;
979 mp = &message[msgvec[0] - 1];
980 if ((head.h_subject = hfield("subject", mp)) == NULL)
981 head.h_subject = hfield("subj", mp);
982 head.h_subject = reedit(head.h_subject);
983 make_ref_and_cs(mp, &head);
984 Eflag = value("skipemptybody") != NULL;
985 if (mail1(&head, 1, mp, NULL, recipient_record, 0, 0, Eflag) == OKAY &&
986 value("markanswered") && (mp->m_flag & MANSWERED) == 0)
987 mp->m_flag |= MANSWER|MANSWERED;
988 return 0;
989 }
990
991 /*
992 * Conditional commands. These allow one to parameterize one's
993 * .mailrc and do some things if sending, others if receiving.
994 */
995 int
ifcmd(void * v)996 ifcmd(void *v)
997 {
998 char **argv = v;
999 char *cp;
1000
1001 if (cond != CANY) {
1002 printf(catgets(catd, CATSET, 42, "Illegal nested \"if\"\n"));
1003 return(1);
1004 }
1005 cond = CANY;
1006 cp = argv[0];
1007 switch (*cp) {
1008 case 'r': case 'R':
1009 cond = CRCV;
1010 break;
1011
1012 case 's': case 'S':
1013 cond = CSEND;
1014 break;
1015
1016 case 't': case 'T':
1017 cond = CTERM;
1018 break;
1019
1020 default:
1021 printf(catgets(catd, CATSET, 43,
1022 "Unrecognized if-keyword: \"%s\"\n"), cp);
1023 return(1);
1024 }
1025 return(0);
1026 }
1027
1028 /*
1029 * Implement 'else'. This is pretty simple -- we just
1030 * flip over the conditional flag.
1031 */
1032 /*ARGSUSED*/
1033 int
elsecmd(void * v)1034 elsecmd(void *v)
1035 {
1036
1037 switch (cond) {
1038 case CANY:
1039 printf(catgets(catd, CATSET, 44,
1040 "\"Else\" without matching \"if\"\n"));
1041 return(1);
1042
1043 case CSEND:
1044 cond = CRCV;
1045 break;
1046
1047 case CRCV:
1048 cond = CSEND;
1049 break;
1050
1051 case CTERM:
1052 cond = CNONTERM;
1053 break;
1054
1055 default:
1056 printf(catgets(catd, CATSET, 45,
1057 "Mail's idea of conditions is screwed up\n"));
1058 cond = CANY;
1059 break;
1060 }
1061 return(0);
1062 }
1063
1064 /*
1065 * End of if statement. Just set cond back to anything.
1066 */
1067 /*ARGSUSED*/
1068 int
endifcmd(void * v)1069 endifcmd(void *v)
1070 {
1071
1072 if (cond == CANY) {
1073 printf(catgets(catd, CATSET, 46,
1074 "\"Endif\" without matching \"if\"\n"));
1075 return(1);
1076 }
1077 cond = CANY;
1078 return(0);
1079 }
1080
1081 /*
1082 * Set the list of alternate names.
1083 */
1084 int
alternates(void * v)1085 alternates(void *v)
1086 {
1087 char **namelist = v;
1088 int c;
1089 char **ap, **ap2, *cp;
1090
1091 c = argcount(namelist) + 1;
1092 if (c == 1) {
1093 if (altnames == 0)
1094 return(0);
1095 for (ap = altnames; *ap; ap++)
1096 printf("%s ", *ap);
1097 printf("\n");
1098 return(0);
1099 }
1100 if (altnames != 0)
1101 free(altnames);
1102 altnames = scalloc(c, sizeof (char *));
1103 for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) {
1104 cp = scalloc(strlen(*ap) + 1, sizeof (char));
1105 strcpy(cp, *ap);
1106 *ap2 = cp;
1107 }
1108 *ap2 = 0;
1109 return(0);
1110 }
1111
1112 /*
1113 * Do the real work of resending.
1114 */
1115 static int
resend1(void * v,int add_resent)1116 resend1(void *v, int add_resent)
1117 {
1118 char *name, *str;
1119 struct name *to;
1120 struct name *sn;
1121 int f, *ip, *msgvec;
1122
1123 str = (char *)v;
1124 /*LINTED*/
1125 msgvec = (int *)salloc((msgCount + 2) * sizeof *msgvec);
1126 name = laststring(str, &f, 1);
1127 if (name == NULL) {
1128 puts(catgets(catd, CATSET, 47, "No recipient specified."));
1129 return 1;
1130 }
1131 if (!f) {
1132 *msgvec = first(0, MMNORM);
1133 if (*msgvec == 0) {
1134 if (inhook)
1135 return 0;
1136 puts(catgets(catd, CATSET, 48,
1137 "No applicable messages."));
1138 return 1;
1139 }
1140 msgvec[1] = 0;
1141 } else if (getmsglist(str, msgvec, 0) < 0)
1142 return 1;
1143 if (*msgvec == 0) {
1144 if (inhook)
1145 return 0;
1146 printf("No applicable messages.\n");
1147 return 1;
1148 }
1149 sn = nalloc(name, GTO);
1150 to = usermap(sn);
1151 for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) {
1152 if (resend_msg(&message[*ip - 1], to, add_resent) != OKAY)
1153 return 1;
1154 }
1155 return 0;
1156 }
1157
1158 /*
1159 * Resend a message list to a third person.
1160 */
1161 int
resendcmd(void * v)1162 resendcmd(void *v)
1163 {
1164 return resend1(v, 1);
1165 }
1166
1167 /*
1168 * Resend a message list to a third person without adding headers.
1169 */
1170 int
Resendcmd(void * v)1171 Resendcmd(void *v)
1172 {
1173 return resend1(v, 0);
1174 }
1175
1176 /*
1177 * 'newmail' or 'inc' command: Check for new mail without writing old
1178 * mail back.
1179 */
1180 /*ARGSUSED*/
1181 int
newmail(void * v)1182 newmail(void *v)
1183 {
1184 int val = 1, mdot;
1185
1186 if ((mb.mb_type != MB_IMAP || imap_newmail(1)) &&
1187 (val = setfile(mailname, 1)) == 0) {
1188 mdot = getmdot(1);
1189 setdot(&message[mdot - 1]);
1190 }
1191 return val;
1192 }
1193
1194 static void
list_shortcuts(void)1195 list_shortcuts(void)
1196 {
1197 struct shortcut *s;
1198
1199 for (s = shortcuts; s; s = s->sh_next)
1200 printf("%s=%s\n", s->sh_short, s->sh_long);
1201 }
1202
1203 int
shortcut(void * v)1204 shortcut(void *v)
1205 {
1206 char **args = (char **)v;
1207 struct shortcut *s;
1208
1209 if (args[0] == NULL) {
1210 list_shortcuts();
1211 return 0;
1212 }
1213 if (args[1] == NULL) {
1214 fprintf(stderr, catgets(catd, CATSET, 220,
1215 "expansion name for shortcut missing\n"));
1216 return 1;
1217 }
1218 if (args[2] != NULL) {
1219 fprintf(stderr, catgets(catd, CATSET, 221,
1220 "too many arguments\n"));
1221 return 1;
1222 }
1223 if ((s = get_shortcut(args[0])) != NULL) {
1224 free(s->sh_long);
1225 s->sh_long = sstrdup(args[1]);
1226 } else {
1227 s = scalloc(1, sizeof *s);
1228 s->sh_short = sstrdup(args[0]);
1229 s->sh_long = sstrdup(args[1]);
1230 s->sh_next = shortcuts;
1231 shortcuts = s;
1232 }
1233 return 0;
1234 }
1235
1236 struct shortcut *
get_shortcut(const char * str)1237 get_shortcut(const char *str)
1238 {
1239 struct shortcut *s;
1240
1241 for (s = shortcuts; s; s = s->sh_next)
1242 if (strcmp(str, s->sh_short) == 0)
1243 break;
1244 return s;
1245 }
1246
1247 static enum okay
delete_shortcut(const char * str)1248 delete_shortcut(const char *str)
1249 {
1250 struct shortcut *sp, *sq;
1251
1252 for (sp = shortcuts, sq = NULL; sp; sq = sp, sp = sp->sh_next) {
1253 if (strcmp(sp->sh_short, str) == 0) {
1254 free(sp->sh_short);
1255 free(sp->sh_long);
1256 if (sq)
1257 sq->sh_next = sp->sh_next;
1258 if (sp == shortcuts)
1259 shortcuts = sp->sh_next;
1260 free(sp);
1261 return OKAY;
1262 }
1263 }
1264 return STOP;
1265 }
1266
1267 int
unshortcut(void * v)1268 unshortcut(void *v)
1269 {
1270 char **args = (char **)v;
1271 int errs = 0;
1272
1273 if (args[0] == NULL) {
1274 fprintf(stderr, catgets(catd, CATSET, 222,
1275 "need shortcut names to remove\n"));
1276 return 1;
1277 }
1278 while (*args != NULL) {
1279 if (delete_shortcut(*args) != OKAY) {
1280 errs = 1;
1281 fprintf(stderr, catgets(catd, CATSET, 223,
1282 "%s: no such shortcut\n"), *args);
1283 }
1284 args++;
1285 }
1286 return errs;
1287 }
1288
1289 struct oldaccount {
1290 struct oldaccount *ac_next; /* next account in list */
1291 char *ac_name; /* name of account */
1292 char **ac_vars; /* variables to set */
1293 };
1294
1295 static struct oldaccount *oldaccounts;
1296
1297 struct oldaccount *
get_oldaccount(const char * name)1298 get_oldaccount(const char *name)
1299 {
1300 struct oldaccount *a;
1301
1302 for (a = oldaccounts; a; a = a->ac_next)
1303 if (a->ac_name && strcmp(name, a->ac_name) == 0)
1304 break;
1305 return a;
1306 }
1307
1308 int
account(void * v)1309 account(void *v)
1310 {
1311 char **args = (char **)v;
1312 struct oldaccount *a;
1313 char *cp;
1314 int i, mc;
1315 FILE *fp = stdout;
1316
1317 if (args[0] == NULL) {
1318 if ((fp = Ftemp(&cp, "Ra", "w+", 0600, 1)) == NULL) {
1319 perror("tmpfile");
1320 return 1;
1321 }
1322 rm(cp);
1323 Ftfree(&cp);
1324 mc = listaccounts(fp);
1325 for (a = oldaccounts; a; a = a->ac_next)
1326 if (a->ac_name) {
1327 if (mc++)
1328 fputc('\n', fp);
1329 fprintf(fp, "%s:\n", a->ac_name);
1330 for (i = 0; a->ac_vars[i]; i++)
1331 fprintf(fp, "\t%s\n", a->ac_vars[i]);
1332 }
1333 if (mc)
1334 try_pager(fp);
1335 Fclose(fp);
1336 return 0;
1337 }
1338 if (args[1] && args[1][0] == '{' && args[1][1] == '\0') {
1339 if (args[2] != NULL) {
1340 fprintf(stderr, "Syntax is: account <name> {\n");
1341 return 1;
1342 }
1343 if ((a = get_oldaccount(args[0])) != NULL)
1344 a->ac_name = NULL;
1345 return define1(args[0], 1);
1346 }
1347 strncpy(mboxname, expand("&"), sizeof mboxname)[sizeof mboxname-1]='\0';
1348 if ((a = get_oldaccount(args[0])) == NULL) {
1349 if (args[1]) {
1350 a = scalloc(1, sizeof *a);
1351 a->ac_next = oldaccounts;
1352 oldaccounts = a;
1353 } else {
1354 if ((i = callaccount(args[0])) != CBAD)
1355 goto setf;
1356 printf("Account %s does not exist.\n", args[0]);
1357 return 1;
1358 }
1359 }
1360 if (args[1]) {
1361 delaccount(args[0]);
1362 a->ac_name = sstrdup(args[0]);
1363 for (i = 1; args[i]; i++);
1364 a->ac_vars = scalloc(i, sizeof *a->ac_vars);
1365 for (i = 0; args[i+1]; i++)
1366 a->ac_vars[i] = sstrdup(args[i+1]);
1367 } else {
1368 unset_allow_undefined = 1;
1369 set(a->ac_vars);
1370 unset_allow_undefined = 0;
1371 setf: if (!starting)
1372 return file1("%");
1373 }
1374 return 0;
1375 }
1376
1377 int
cflag(void * v)1378 cflag(void *v)
1379 {
1380 struct message *m;
1381 int *msgvec = v;
1382 int *ip;
1383
1384 for (ip = msgvec; *ip != 0; ip++) {
1385 m = &message[*ip-1];
1386 setdot(m);
1387 if ((m->m_flag & (MFLAG|MFLAGGED)) == 0)
1388 m->m_flag |= MFLAG|MFLAGGED;
1389 }
1390 return 0;
1391 }
1392
1393 int
cunflag(void * v)1394 cunflag(void *v)
1395 {
1396 struct message *m;
1397 int *msgvec = v;
1398 int *ip;
1399
1400 for (ip = msgvec; *ip != 0; ip++) {
1401 m = &message[*ip-1];
1402 setdot(m);
1403 if (m->m_flag & (MFLAG|MFLAGGED)) {
1404 m->m_flag &= ~(MFLAG|MFLAGGED);
1405 m->m_flag |= MUNFLAG;
1406 }
1407 }
1408 return 0;
1409 }
1410
1411 int
canswered(void * v)1412 canswered(void *v)
1413 {
1414 struct message *m;
1415 int *msgvec = v;
1416 int *ip;
1417
1418 for (ip = msgvec; *ip != 0; ip++) {
1419 m = &message[*ip-1];
1420 setdot(m);
1421 if ((m->m_flag & (MANSWER|MANSWERED)) == 0)
1422 m->m_flag |= MANSWER|MANSWERED;
1423 }
1424 return 0;
1425 }
1426
1427 int
cunanswered(void * v)1428 cunanswered(void *v)
1429 {
1430 struct message *m;
1431 int *msgvec = v;
1432 int *ip;
1433
1434 for (ip = msgvec; *ip != 0; ip++) {
1435 m = &message[*ip-1];
1436 setdot(m);
1437 if (m->m_flag & (MANSWER|MANSWERED)) {
1438 m->m_flag &= ~(MANSWER|MANSWERED);
1439 m->m_flag |= MUNANSWER;
1440 }
1441 }
1442 return 0;
1443 }
1444
1445 int
cdraft(void * v)1446 cdraft(void *v)
1447 {
1448 struct message *m;
1449 int *msgvec = v;
1450 int *ip;
1451
1452 for (ip = msgvec; *ip != 0; ip++) {
1453 m = &message[*ip-1];
1454 setdot(m);
1455 if ((m->m_flag & (MDRAFT|MDRAFTED)) == 0)
1456 m->m_flag |= MDRAFT|MDRAFTED;
1457 }
1458 return 0;
1459 }
1460
1461 int
cundraft(void * v)1462 cundraft(void *v)
1463 {
1464 struct message *m;
1465 int *msgvec = v;
1466 int *ip;
1467
1468 for (ip = msgvec; *ip != 0; ip++) {
1469 m = &message[*ip-1];
1470 setdot(m);
1471 if (m->m_flag & (MDRAFT|MDRAFTED)) {
1472 m->m_flag &= ~(MDRAFT|MDRAFTED);
1473 m->m_flag |= MUNDRAFT;
1474 }
1475 }
1476 return 0;
1477 }
1478
1479 static float
huge(void)1480 huge(void)
1481 {
1482 #if defined (_CRAY)
1483 /*
1484 * This is not perfect, but correct for machines with a 32-bit
1485 * IEEE float and a 32-bit unsigned long, and does at least not
1486 * produce SIGFPE on the Cray Y-MP.
1487 */
1488 union {
1489 float f;
1490 unsigned long l;
1491 } u;
1492
1493 u.l = 0xff800000; /* -inf */
1494 return u.f;
1495 #elif defined (INFINITY)
1496 return -INFINITY;
1497 #elif defined (HUGE_VALF)
1498 return -HUGE_VALF;
1499 #elif defined (FLT_MAX)
1500 return -FLT_MAX;
1501 #else
1502 return -1e10;
1503 #endif
1504 }
1505
1506 int
ckill(void * v)1507 ckill(void *v)
1508 {
1509 struct message *m;
1510 int *msgvec = v;
1511 int *ip;
1512
1513 for (ip = msgvec; *ip != 0; ip++) {
1514 m = &message[*ip-1];
1515 m->m_flag |= MKILL;
1516 m->m_score = huge();
1517 }
1518 return 0;
1519 }
1520
1521 int
cunkill(void * v)1522 cunkill(void *v)
1523 {
1524 struct message *m;
1525 int *msgvec = v;
1526 int *ip;
1527
1528 for (ip = msgvec; *ip != 0; ip++) {
1529 m = &message[*ip-1];
1530 m->m_flag &= ~MKILL;
1531 m->m_score = 0;
1532 }
1533 return 0;
1534 }
1535
1536 int
cscore(void * v)1537 cscore(void *v)
1538 {
1539 char *str = v;
1540 char *sscore, *xp;
1541 int f, *msgvec, *ip;
1542 double nscore;
1543 struct message *m;
1544
1545 msgvec = salloc((msgCount+2) * sizeof *msgvec);
1546 if ((sscore = laststring(str, &f, 0)) == NULL) {
1547 fprintf(stderr, "No score given.\n");
1548 return 1;
1549 }
1550 nscore = strtod(sscore, &xp);
1551 if (*xp) {
1552 fprintf(stderr, "Invalid score: \"%s\"\n", sscore);
1553 return 1;
1554 }
1555 if (nscore > FLT_MAX)
1556 nscore = FLT_MAX;
1557 else if (nscore < -FLT_MAX)
1558 nscore = -FLT_MAX;
1559 if (!f) {
1560 *msgvec = first(0, MMNORM);
1561 if (*msgvec == 0) {
1562 if (inhook)
1563 return 0;
1564 fprintf(stderr, "No messages to score.\n");
1565 return 1;
1566 }
1567 msgvec[1] = 0;
1568 } else if (getmsglist(str, msgvec, 0) < 0)
1569 return 1;
1570 if (*msgvec == 0) {
1571 if (inhook)
1572 return 0;
1573 fprintf(stderr, "No applicable messages.\n");
1574 return 1;
1575 }
1576 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
1577 m = &message[*ip-1];
1578 if (m->m_score != huge()) {
1579 m->m_score += nscore;
1580 if (m->m_score < 0)
1581 m->m_flag |= MKILL;
1582 else if (m->m_score > 0)
1583 m->m_flag &= ~MKILL;
1584 if (m->m_score >= 0)
1585 setdot(m);
1586 }
1587 }
1588 return 0;
1589 }
1590
1591 /*ARGSUSED*/
1592 int
cnoop(void * v)1593 cnoop(void *v)
1594 {
1595 switch (mb.mb_type) {
1596 case MB_IMAP:
1597 imap_noop();
1598 break;
1599 case MB_POP3:
1600 pop3_noop();
1601 break;
1602 default:
1603 break;
1604 }
1605 return 0;
1606 }
1607
1608 int
cremove(void * v)1609 cremove(void *v)
1610 {
1611 char vb[LINESIZE];
1612 char **args = v;
1613 char *name;
1614 int ec = 0;
1615
1616 if (*args == NULL) {
1617 fprintf(stderr, "Syntax is: remove mailbox ...\n");
1618 return 1;
1619 }
1620 do {
1621 if ((name = expand(*args)) == NULL)
1622 continue;
1623 if (strcmp(name, mailname) == 0) {
1624 fprintf(stderr,
1625 "Cannot remove current mailbox \"%s\".\n",
1626 name);
1627 ec |= 1;
1628 continue;
1629 }
1630 snprintf(vb, sizeof vb, "Remove \"%s\" (y/n) ? ", name);
1631 if (yorn(vb) == 0)
1632 continue;
1633 switch (which_protocol(name)) {
1634 case PROTO_FILE:
1635 if (unlink(name) < 0) { /* do not handle .gz .bz2 */
1636 perror(name);
1637 ec |= 1;
1638 }
1639 break;
1640 case PROTO_POP3:
1641 fprintf(stderr, "Cannot remove POP3 mailbox \"%s\".\n",
1642 name);
1643 ec |= 1;
1644 break;
1645 case PROTO_IMAP:
1646 if (imap_remove(name) != OKAY)
1647 ec |= 1;
1648 break;
1649 case PROTO_MAILDIR:
1650 if (maildir_remove(name) != OKAY)
1651 ec |= 1;
1652 break;
1653 case PROTO_UNKNOWN:
1654 fprintf(stderr,
1655 "Unknown protocol in \"%s\". Not removed.\n",
1656 name);
1657 ec |= 1;
1658 }
1659 } while (*++args);
1660 return ec;
1661 }
1662
1663 int
crename(void * v)1664 crename(void *v)
1665 {
1666 char **args = v, *old, *new;
1667 enum protocol oldp, newp;
1668 int ec = 0;
1669
1670 if (args[0] == NULL || args[1] == NULL || args[2] != NULL) {
1671 fprintf(stderr, "Syntax: rename old new\n");
1672 return 1;
1673 }
1674 old = expand(args[0]);
1675 oldp = which_protocol(old);
1676 new = expand(args[1]);
1677 newp = which_protocol(new);
1678 if (strcmp(old, mailname) == 0 || strcmp(new, mailname) == 0) {
1679 fprintf(stderr, "Cannot rename current mailbox \"%s\".\n", old);
1680 return 1;
1681 }
1682 if ((oldp == PROTO_IMAP || newp == PROTO_IMAP) && oldp != newp) {
1683 fprintf(stderr, "Can only rename folders of same type.\n");
1684 return 1;
1685 }
1686 if (newp == PROTO_POP3)
1687 goto nopop3;
1688 switch (oldp) {
1689 case PROTO_FILE:
1690 if (link(old, new) < 0) {
1691 switch (errno) {
1692 case EACCES:
1693 case EEXIST:
1694 case ENAMETOOLONG:
1695 case ENOENT:
1696 case ENOSPC:
1697 case EXDEV:
1698 perror(new);
1699 break;
1700 default:
1701 perror(old);
1702 }
1703 ec |= 1;
1704 } else if (unlink(old) < 0) {
1705 perror(old);
1706 ec |= 1;
1707 }
1708 break;
1709 case PROTO_MAILDIR:
1710 if (rename(old, new) < 0) {
1711 perror(old);
1712 ec |= 1;
1713 }
1714 break;
1715 case PROTO_POP3:
1716 nopop3: fprintf(stderr, "Cannot rename POP3 mailboxes.\n");
1717 ec |= 1;
1718 break;
1719 case PROTO_IMAP:
1720 if (imap_rename(old, new) != OKAY)
1721 ec |= 1;
1722 break;
1723 case PROTO_UNKNOWN:
1724 fprintf(stderr, "Unknown protocol in \"%s\" and \"%s\". "
1725 "Not renamed.\n", old, new);
1726 ec |= 1;
1727 }
1728 return ec;
1729 }
1730