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