1 /* 2 * Copyright (c) 1980, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * @(#)cmd3.c 8.2 (Berkeley) 4/20/95 34 * $FreeBSD: src/usr.bin/mail/cmd3.c,v 1.4.6.4 2003/01/06 05:46:03 mikeh Exp $ 35 */ 36 37 #include "rcv.h" 38 #include "extern.h" 39 40 /* 41 * Mail -- a mail program 42 * 43 * Still more user commands. 44 */ 45 46 /* 47 * Process a shell escape by saving signals, ignoring signals, 48 * and forking a sh -c 49 */ 50 int 51 shell(char *str) 52 { 53 sig_t sigint = signal(SIGINT, SIG_IGN); 54 char *sh; 55 char cmd[BUFSIZ]; 56 57 if (strlcpy(cmd, str, sizeof(cmd)) >= sizeof(cmd)) 58 return (1); 59 if (bangexp(cmd, sizeof(cmd)) < 0) 60 return (1); 61 if ((sh = value("SHELL")) == NULL) 62 sh = _PATH_CSHELL; 63 run_command(sh, 0, -1, -1, "-c", cmd, NULL); 64 signal(SIGINT, sigint); 65 printf("!\n"); 66 return (0); 67 } 68 69 /* 70 * Fork an interactive shell. 71 */ 72 /*ARGSUSED*/ 73 int 74 dosh(char *str) 75 { 76 sig_t sigint = signal(SIGINT, SIG_IGN); 77 char *sh; 78 79 if ((sh = value("SHELL")) == NULL) 80 sh = _PATH_CSHELL; 81 run_command(sh, 0, -1, -1, NULL, NULL, NULL); 82 signal(SIGINT, sigint); 83 printf("\n"); 84 return (0); 85 } 86 87 /* 88 * Expand the shell escape by expanding unescaped !'s into the 89 * last issued command where possible. 90 */ 91 int 92 bangexp(char *str, size_t strsize) 93 { 94 char bangbuf[BUFSIZ]; 95 static char lastbang[BUFSIZ]; 96 char *cp, *cp2; 97 int n, changed = 0; 98 99 cp = str; 100 cp2 = bangbuf; 101 n = sizeof(bangbuf); 102 while (*cp != '\0') { 103 if (*cp == '!') { 104 if (n < strlen(lastbang)) { 105 overf: 106 printf("Command buffer overflow\n"); 107 return (-1); 108 } 109 changed++; 110 if (strlcpy(cp2, lastbang, sizeof(bangbuf) - (cp2 - bangbuf)) 111 >= sizeof(bangbuf) - (cp2 - bangbuf)) 112 goto overf; 113 cp2 += strlen(lastbang); 114 n -= strlen(lastbang); 115 cp++; 116 continue; 117 } 118 if (*cp == '\\' && cp[1] == '!') { 119 if (--n <= 1) 120 goto overf; 121 *cp2++ = '!'; 122 cp += 2; 123 changed++; 124 } 125 if (--n <= 1) 126 goto overf; 127 *cp2++ = *cp++; 128 } 129 *cp2 = 0; 130 if (changed) { 131 printf("!%s\n", bangbuf); 132 fflush(stdout); 133 } 134 if (strlcpy(str, bangbuf, strsize) >= strsize) 135 goto overf; 136 if (strlcpy(lastbang, bangbuf, sizeof(lastbang)) >= sizeof(lastbang)) 137 goto overf; 138 return (0); 139 } 140 141 /* 142 * Print out a nice help message from some file or another. 143 */ 144 145 int 146 help(void) 147 { 148 int c; 149 FILE *f; 150 151 if ((f = Fopen(_PATH_HELP, "r")) == NULL) { 152 warn("%s", _PATH_HELP); 153 return (1); 154 } 155 while ((c = getc(f)) != EOF) 156 putchar(c); 157 Fclose(f); 158 return (0); 159 } 160 161 /* 162 * Change user's working directory. 163 */ 164 int 165 schdir(char **arglist) 166 { 167 char *cp; 168 169 if (*arglist == NULL) { 170 if (homedir == NULL) 171 return (1); 172 cp = homedir; 173 } else 174 if ((cp = expand(*arglist)) == NULL) 175 return (1); 176 if (chdir(cp) < 0) { 177 warn("%s", cp); 178 return (1); 179 } 180 return (0); 181 } 182 183 int 184 respond(int *msgvec) 185 { 186 if (value("Replyall") == NULL && value("flipr") == NULL) 187 return (dorespond(msgvec)); 188 else 189 return (doRespond(msgvec)); 190 } 191 192 /* 193 * Reply to a list of messages. Extract each name from the 194 * message header and send them off to mail1() 195 */ 196 int 197 dorespond(int *msgvec) 198 { 199 struct message *mp; 200 char *cp, *rcv, *replyto; 201 char **ap; 202 struct name *np; 203 struct header head; 204 205 if (msgvec[1] != 0) { 206 printf("Sorry, can't reply to multiple messages at once\n"); 207 return (1); 208 } 209 mp = &message[msgvec[0] - 1]; 210 touch(mp); 211 dot = mp; 212 if ((rcv = skin(hfield("from", mp))) == NULL) 213 rcv = skin(nameof(mp, 1)); 214 if ((replyto = skin(hfield("reply-to", mp))) != NULL) 215 np = extract(replyto, GTO); 216 else if ((cp = skin(hfield("to", mp))) != NULL) 217 np = extract(cp, GTO); 218 else 219 np = NULL; 220 np = elide(np); 221 /* 222 * Delete my name from the reply list, 223 * and with it, all my alternate names. 224 */ 225 np = delname(np, myname); 226 if (altnames) 227 for (ap = altnames; *ap != NULL; ap++) 228 np = delname(np, *ap); 229 if (np != NULL && replyto == NULL) 230 np = cat(np, extract(rcv, GTO)); 231 else if (np == NULL) { 232 if (replyto != NULL) 233 printf("Empty reply-to field -- replying to author\n"); 234 np = extract(rcv, GTO); 235 } 236 head.h_to = np; 237 if ((head.h_subject = hfield("subject", mp)) == NULL) 238 head.h_subject = hfield("subj", mp); 239 head.h_subject = reedit(head.h_subject); 240 if (replyto == NULL && (cp = skin(hfield("cc", mp))) != NULL) { 241 np = elide(extract(cp, GCC)); 242 np = delname(np, myname); 243 if (altnames != 0) 244 for (ap = altnames; *ap != NULL; ap++) 245 np = delname(np, *ap); 246 head.h_cc = np; 247 } else 248 head.h_cc = NULL; 249 head.h_bcc = NULL; 250 head.h_smopts = NULL; 251 head.h_replyto = value("REPLYTO"); 252 head.h_inreplyto = skin(hfield("message-id", mp)); 253 mail1(&head, 1); 254 return (0); 255 } 256 257 /* 258 * Modify the subject we are replying to to begin with Re: if 259 * it does not already. 260 */ 261 char * 262 reedit(char *subj) 263 { 264 char *newsubj; 265 266 if (subj == NULL) 267 return (NULL); 268 if ((subj[0] == 'r' || subj[0] == 'R') && 269 (subj[1] == 'e' || subj[1] == 'E') && 270 subj[2] == ':') 271 return (subj); 272 newsubj = salloc(strlen(subj) + 5); 273 sprintf(newsubj, "Re: %s", subj); 274 return (newsubj); 275 } 276 277 /* 278 * Preserve the named messages, so that they will be sent 279 * back to the system mailbox. 280 */ 281 int 282 preserve(int *msgvec) 283 { 284 int *ip, mesg; 285 struct message *mp; 286 287 if (edit) { 288 printf("Cannot \"preserve\" in edit mode\n"); 289 return (1); 290 } 291 for (ip = msgvec; *ip != 0; ip++) { 292 mesg = *ip; 293 mp = &message[mesg-1]; 294 mp->m_flag |= MPRESERVE; 295 mp->m_flag &= ~MBOX; 296 dot = mp; 297 } 298 return (0); 299 } 300 301 /* 302 * Mark all given messages as unread. 303 */ 304 int 305 unread(int *msgvec) 306 { 307 int *ip; 308 309 for (ip = msgvec; *ip != 0; ip++) { 310 dot = &message[*ip-1]; 311 dot->m_flag &= ~(MREAD|MTOUCH); 312 dot->m_flag |= MSTATUS; 313 } 314 return (0); 315 } 316 317 /* 318 * Print the size of each message. 319 */ 320 int 321 messize(int *msgvec) 322 { 323 struct message *mp; 324 int *ip, mesg; 325 326 for (ip = msgvec; *ip != 0; ip++) { 327 mesg = *ip; 328 mp = &message[mesg-1]; 329 printf("%d: %ld/%ld\n", mesg, mp->m_lines, mp->m_size); 330 } 331 return (0); 332 } 333 334 /* 335 * Quit quickly. If we are sourcing, just pop the input level 336 * by returning an error. 337 */ 338 int 339 rexit(int e) 340 { 341 if (sourcing) 342 return (1); 343 exit(0); 344 /*NOTREACHED*/ 345 } 346 347 /* 348 * Set or display a variable value. Syntax is similar to that 349 * of csh. 350 */ 351 int 352 set(char **arglist) 353 { 354 struct var *vp; 355 char *cp, *cp2; 356 char varbuf[BUFSIZ], **ap, **p; 357 int errs, h, s; 358 359 if (*arglist == NULL) { 360 for (h = 0, s = 1; h < HSHSIZE; h++) 361 for (vp = variables[h]; vp != NULL; vp = vp->v_link) 362 s++; 363 ap = (char **)salloc(s * sizeof(*ap)); 364 for (h = 0, p = ap; h < HSHSIZE; h++) 365 for (vp = variables[h]; vp != NULL; vp = vp->v_link) 366 *p++ = vp->v_name; 367 *p = NULL; 368 sort(ap); 369 for (p = ap; *p != NULL; p++) 370 printf("%s\t%s\n", *p, value(*p)); 371 return (0); 372 } 373 errs = 0; 374 for (ap = arglist; *ap != NULL; ap++) { 375 cp = *ap; 376 cp2 = varbuf; 377 while (cp2 < varbuf + sizeof(varbuf) - 1 && *cp != '=' && *cp != '\0') 378 *cp2++ = *cp++; 379 *cp2 = '\0'; 380 if (*cp == '\0') 381 cp = ""; 382 else 383 cp++; 384 if (equal(varbuf, "")) { 385 printf("Non-null variable name required\n"); 386 errs++; 387 continue; 388 } 389 assign(varbuf, cp); 390 } 391 return (errs); 392 } 393 394 /* 395 * Unset a bunch of variable values. 396 */ 397 int 398 unset(char **arglist) 399 { 400 struct var *vp, *vp2; 401 int errs, h; 402 char **ap; 403 404 errs = 0; 405 for (ap = arglist; *ap != NULL; ap++) { 406 if ((vp2 = lookup(*ap)) == NULL) { 407 if (getenv(*ap)) 408 unsetenv(*ap); 409 else if (!sourcing) { 410 printf("\"%s\": undefined variable\n", *ap); 411 errs++; 412 } 413 continue; 414 } 415 h = hash(*ap); 416 if (vp2 == variables[h]) { 417 variables[h] = variables[h]->v_link; 418 vfree(vp2->v_name); 419 vfree(vp2->v_value); 420 free(vp2); 421 continue; 422 } 423 for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link) 424 ; 425 vp->v_link = vp2->v_link; 426 vfree(vp2->v_name); 427 vfree(vp2->v_value); 428 free(vp2); 429 } 430 return (errs); 431 } 432 433 /* 434 * Put add users to a group. 435 */ 436 int 437 group(char **argv) 438 { 439 struct grouphead *gh; 440 struct group *gp; 441 char **ap, *gname, **p; 442 int h, s; 443 444 if (*argv == NULL) { 445 for (h = 0, s = 1; h < HSHSIZE; h++) 446 for (gh = groups[h]; gh != NULL; gh = gh->g_link) 447 s++; 448 ap = (char **)salloc(s * sizeof(*ap)); 449 for (h = 0, p = ap; h < HSHSIZE; h++) 450 for (gh = groups[h]; gh != NULL; gh = gh->g_link) 451 *p++ = gh->g_name; 452 *p = NULL; 453 sort(ap); 454 for (p = ap; *p != NULL; p++) 455 printgroup(*p); 456 return (0); 457 } 458 if (argv[1] == NULL) { 459 printgroup(*argv); 460 return (0); 461 } 462 gname = *argv; 463 h = hash(gname); 464 if ((gh = findgroup(gname)) == NULL) { 465 gh = calloc(sizeof(*gh), 1); 466 gh->g_name = vcopy(gname); 467 gh->g_list = NULL; 468 gh->g_link = groups[h]; 469 groups[h] = gh; 470 } 471 472 /* 473 * Insert names from the command list into the group. 474 * Who cares if there are duplicates? They get tossed 475 * later anyway. 476 */ 477 478 for (ap = argv+1; *ap != NULL; ap++) { 479 gp = calloc(sizeof(*gp), 1); 480 gp->ge_name = vcopy(*ap); 481 gp->ge_link = gh->g_list; 482 gh->g_list = gp; 483 } 484 return (0); 485 } 486 487 /* 488 * Sort the passed string vecotor into ascending dictionary 489 * order. 490 */ 491 void 492 sort(char **list) 493 { 494 char **ap; 495 496 for (ap = list; *ap != NULL; ap++) 497 ; 498 if (ap-list < 2) 499 return; 500 qsort(list, ap-list, sizeof(*list), diction); 501 } 502 503 /* 504 * Do a dictionary order comparison of the arguments from 505 * qsort. 506 */ 507 int 508 diction(const void *a, const void *b) 509 { 510 return (strcmp(*(const char **)a, *(const char **)b)); 511 } 512 513 /* 514 * The do nothing command for comments. 515 */ 516 517 /*ARGSUSED*/ 518 int 519 null(int e) 520 { 521 return (0); 522 } 523 524 /* 525 * Change to another file. With no argument, print information about 526 * the current file. 527 */ 528 int 529 file(char **argv) 530 { 531 if (argv[0] == NULL) { 532 newfileinfo(0); 533 return (0); 534 } 535 if (setfile(*argv) < 0) 536 return (1); 537 announce(); 538 return (0); 539 } 540 541 /* 542 * Expand file names like echo 543 */ 544 int 545 echo(char **argv) 546 { 547 char **ap, *cp; 548 549 for (ap = argv; *ap != NULL; ap++) { 550 cp = *ap; 551 if ((cp = expand(cp)) != NULL) { 552 if (ap != argv) 553 printf(" "); 554 printf("%s", cp); 555 } 556 } 557 printf("\n"); 558 return (0); 559 } 560 561 int 562 Respond(int *msgvec) 563 { 564 if (value("Replyall") == NULL && value("flipr") == NULL) 565 return (doRespond(msgvec)); 566 else 567 return (dorespond(msgvec)); 568 } 569 570 /* 571 * Reply to a series of messages by simply mailing to the senders 572 * and not messing around with the To: and Cc: lists as in normal 573 * reply. 574 */ 575 int 576 doRespond(int *msgvec) 577 { 578 struct header head; 579 struct message *mp; 580 int *ap; 581 char *cp, *mid; 582 583 head.h_to = NULL; 584 for (ap = msgvec; *ap != 0; ap++) { 585 mp = &message[*ap - 1]; 586 touch(mp); 587 dot = mp; 588 if ((cp = skin(hfield("from", mp))) == NULL) 589 cp = skin(nameof(mp, 2)); 590 head.h_to = cat(head.h_to, extract(cp, GTO)); 591 mid = skin(hfield("message-id", mp)); 592 } 593 if (head.h_to == NULL) 594 return (0); 595 mp = &message[msgvec[0] - 1]; 596 if ((head.h_subject = hfield("subject", mp)) == NULL) 597 head.h_subject = hfield("subj", mp); 598 head.h_subject = reedit(head.h_subject); 599 head.h_cc = NULL; 600 head.h_bcc = NULL; 601 head.h_smopts = NULL; 602 head.h_replyto = value("REPLYTO"); 603 head.h_inreplyto = mid; 604 mail1(&head, 1); 605 return (0); 606 } 607 608 /* 609 * Conditional commands. These allow one to parameterize one's 610 * .mailrc and do some things if sending, others if receiving. 611 */ 612 int 613 ifcmd(char **argv) 614 { 615 char *cp; 616 617 if (cond != CANY) { 618 printf("Illegal nested \"if\"\n"); 619 return (1); 620 } 621 cond = CANY; 622 cp = argv[0]; 623 switch (*cp) { 624 case 'r': case 'R': 625 cond = CRCV; 626 break; 627 628 case 's': case 'S': 629 cond = CSEND; 630 break; 631 632 default: 633 printf("Unrecognized if-keyword: \"%s\"\n", cp); 634 return (1); 635 } 636 return (0); 637 } 638 639 /* 640 * Implement 'else'. This is pretty simple -- we just 641 * flip over the conditional flag. 642 */ 643 int 644 elsecmd(void) 645 { 646 switch (cond) { 647 case CANY: 648 printf("\"Else\" without matching \"if\"\n"); 649 return (1); 650 651 case CSEND: 652 cond = CRCV; 653 break; 654 655 case CRCV: 656 cond = CSEND; 657 break; 658 659 default: 660 printf("Mail's idea of conditions is screwed up\n"); 661 cond = CANY; 662 break; 663 } 664 return (0); 665 } 666 667 /* 668 * End of if statement. Just set cond back to anything. 669 */ 670 int 671 endifcmd(void) 672 { 673 if (cond == CANY) { 674 printf("\"Endif\" without matching \"if\"\n"); 675 return (1); 676 } 677 cond = CANY; 678 return (0); 679 } 680 681 /* 682 * Set the list of alternate names. 683 */ 684 int 685 alternates(char **namelist) 686 { 687 int c; 688 char **ap, **ap2, *cp; 689 690 c = argcount(namelist) + 1; 691 if (c == 1) { 692 if (altnames == 0) 693 return (0); 694 for (ap = altnames; *ap != NULL; ap++) 695 printf("%s ", *ap); 696 printf("\n"); 697 return (0); 698 } 699 if (altnames != 0) 700 free(altnames); 701 altnames = calloc((unsigned)c, sizeof(char *)); 702 for (ap = namelist, ap2 = altnames; *ap != NULL; ap++, ap2++) { 703 cp = calloc((unsigned)strlen(*ap) + 1, sizeof(char)); 704 strcpy(cp, *ap); 705 *ap2 = cp; 706 } 707 *ap2 = NULL; 708 return (0); 709 } 710