1 #include <ctype.h> 2 #include <stdio.h> 3 #include <pwd.h> 4 #include <utmp.h> 5 #include <signal.h> 6 #include <sys/types.h> 7 #include <sys/stat.h> 8 #include <setjmp.h> 9 #include <whoami.h> 10 #include <sysexits.h> 11 12 static char SccsId[] = "@(#)mail.local.c 4.1 10/01/80"; 13 14 #define DELIVERMAIL "/etc/delivermail" 15 16 17 /*copylet flags */ 18 /*remote mail, add rmtmsg */ 19 #define REMOTE 1 20 /* zap header and trailing empty line */ 21 #define ZAP 3 22 #define ORDINARY 2 23 #define FORWARD 4 24 #define LSIZE 256 25 #define MAXLET 300 /* maximum number of letters */ 26 #define MAILMODE (~0644) /* mode of created mail */ 27 # ifndef DELIVERMAIL 28 #define RMAIL "/usr/net/bin/sendberkmail" 29 #define LOCNAM1 "csvax" 30 #define LOCNAM2 "ucbvax" 31 #define LOCNAM3 "vax" 32 #define LOCNAM4 "v" 33 # endif 34 35 char line[LSIZE]; 36 char resp[LSIZE]; 37 struct let { 38 long adr; 39 char change; 40 } let[MAXLET]; 41 int nlet = 0; 42 char lfil[50]; 43 long iop, time(); 44 char *getenv(); 45 char *index(); 46 char lettmp[] = "/tmp/maXXXXX"; 47 char maildir[] = "/usr/spool/mail/"; 48 char mailfile[] = "/usr/spool/mail/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; 49 char dead[] = "dead.letter"; 50 char *thissys = sysname; 51 char *netname = "vax"; 52 char forwmsg[] = " forwarded\n"; 53 FILE *tmpf; 54 FILE *malf; 55 char *my_name; 56 char *getlogin(); 57 struct passwd *getpwuid(); 58 int error; 59 int changed; 60 int forward; 61 char from[] = "From "; 62 long ftell(); 63 int delete(); 64 char *ctime(); 65 int flgf; 66 int flgp; 67 int delflg = 1; 68 int hseqno; 69 jmp_buf sjbuf; 70 int rmail; 71 72 main(argc, argv) 73 char **argv; 74 { 75 register i; 76 char sobuf[BUFSIZ]; 77 78 setbuf(stdout, sobuf); 79 mktemp(lettmp); 80 unlink(lettmp); 81 my_name = getlogin(); 82 if (my_name == NULL || strlen(my_name) == 0) { 83 struct passwd *pwent; 84 pwent = getpwuid(getuid()); 85 if (pwent==NULL) 86 my_name = "???"; 87 else 88 my_name = pwent->pw_name; 89 } 90 if(setjmp(sjbuf)) done(); 91 for (i=0; i<20; i++) 92 setsig(i, delete); 93 tmpf = fopen(lettmp, "w"); 94 if (tmpf == NULL) { 95 fprintf(stderr, "mail: cannot open %s for writing\n", lettmp); 96 done(); 97 } 98 if (argv[0][0] == 'r') 99 rmail++; 100 if (argv[0][0] != 'r' && /* no favors for rmail*/ 101 (argc == 1 || argv[1][0] == '-' && !any(argv[1][1], "rhd"))) 102 printmail(argc, argv); 103 else 104 sendmail(argc, argv); 105 done(); 106 } 107 108 setsig(i, f) 109 int i; 110 int (*f)(); 111 { 112 if(signal(i, SIG_IGN)!=SIG_IGN) 113 signal(i, f); 114 } 115 116 any(c, str) 117 register int c; 118 register char *str; 119 { 120 121 while (*str) 122 if (c == *str++) 123 return(1); 124 return(0); 125 } 126 127 printmail(argc, argv) 128 char **argv; 129 { 130 int flg, i, j, print; 131 char *p, *getarg(); 132 struct stat statb; 133 134 setuid(getuid()); 135 cat(mailfile, maildir, my_name); 136 if (stat(mailfile, &statb) >= 0 137 && (statb.st_mode & S_IFMT) == S_IFDIR) { 138 strcat(mailfile, "/"); 139 strcat(mailfile, my_name); 140 } 141 for (; argc>1; argv++, argc--) { 142 if (argv[1][0]=='-') { 143 if (argv[1][1]=='q') 144 delflg = 0; 145 else if (argv[1][1]=='p') { 146 flgp++; 147 delflg = 0; 148 } else if (argv[1][1]=='f') { 149 if (argc>=3) { 150 strcpy(mailfile, argv[2]); 151 argv++; 152 argc--; 153 } 154 } else if (argv[1][1]=='r') { 155 forward = 1; 156 } else if (argv[1][1]=='h') { 157 forward = 1; 158 } else { 159 fprintf(stderr, "mail: unknown option %c\n", argv[1][1]); 160 done(); 161 } 162 } else 163 break; 164 } 165 malf = fopen(mailfile, "r"); 166 if (malf == NULL) { 167 fprintf(stdout, "No mail.\n"); 168 return; 169 } 170 lock(mailfile); 171 copymt(malf, tmpf); 172 fclose(malf); 173 fclose(tmpf); 174 unlock(); 175 tmpf = fopen(lettmp, "r"); 176 177 changed = 0; 178 print = 1; 179 for (i = 0; i < nlet; ) { 180 j = forward ? i : nlet - i - 1; 181 if(setjmp(sjbuf)) { 182 print=0; 183 } else { 184 if (print) 185 copylet(j, stdout, ORDINARY); 186 print = 1; 187 } 188 if (flgp) { 189 i++; 190 continue; 191 } 192 setjmp(sjbuf); 193 fprintf(stdout, "? "); 194 fflush(stdout); 195 if (fgets(resp, LSIZE, stdin) == NULL) 196 break; 197 switch (resp[0]) { 198 199 default: 200 fprintf(stderr, "usage\n"); 201 case '?': 202 print = 0; 203 fprintf(stderr, "q\tquit\n"); 204 fprintf(stderr, "x\texit without changing mail\n"); 205 fprintf(stderr, "p\tprint\n"); 206 fprintf(stderr, "s[file]\tsave (default mbox)\n"); 207 fprintf(stderr, "w[file]\tsame without header\n"); 208 fprintf(stderr, "-\tprint previous\n"); 209 fprintf(stderr, "d\tdelete\n"); 210 fprintf(stderr, "+\tnext (no delete)\n"); 211 fprintf(stderr, "m user\tmail to user\n"); 212 fprintf(stderr, "! cmd\texecute cmd\n"); 213 break; 214 215 case '+': 216 case 'n': 217 case '\n': 218 i++; 219 break; 220 case 'x': 221 changed = 0; 222 case 'q': 223 goto donep; 224 case 'p': 225 break; 226 case '^': 227 case '-': 228 if (--i < 0) 229 i = 0; 230 break; 231 case 'y': 232 case 'w': 233 case 's': 234 flg = 0; 235 if (resp[1] != '\n' && resp[1] != ' ') { 236 printf("illegal\n"); 237 flg++; 238 print = 0; 239 continue; 240 } 241 if (resp[1] == '\n' || resp[1] == '\0') { 242 p = getenv("HOME"); 243 if(p != 0) 244 cat(resp+1, p, "/mbox"); 245 else 246 cat(resp+1, "", "mbox"); 247 } 248 for (p = resp+1; (p = getarg(lfil, p)) != NULL; ) { 249 malf = fopen(lfil, "a"); 250 if (malf == NULL) { 251 fprintf(stdout, "mail: cannot append to %s\n", lfil); 252 flg++; 253 continue; 254 } 255 copylet(j, malf, resp[0]=='w'? ZAP: ORDINARY); 256 fclose(malf); 257 } 258 if (flg) 259 print = 0; 260 else { 261 let[j].change = 'd'; 262 changed++; 263 i++; 264 } 265 break; 266 case 'm': 267 flg = 0; 268 if (resp[1] == '\n' || resp[1] == '\0') { 269 i++; 270 continue; 271 } 272 if (resp[1] != ' ') { 273 printf("invalid command\n"); 274 flg++; 275 print = 0; 276 continue; 277 } 278 for (p = resp+1; (p = getarg(lfil, p)) != NULL; ) 279 if (!sendrmt(j, lfil, "/bin/mail")) /* couldn't send it */ 280 flg++; 281 if (flg) 282 print = 0; 283 else { 284 let[j].change = 'd'; 285 changed++; 286 i++; 287 } 288 break; 289 case '!': 290 system(resp+1); 291 printf("!\n"); 292 print = 0; 293 break; 294 case 'd': 295 let[j].change = 'd'; 296 changed++; 297 i++; 298 if (resp[1] == 'q') 299 goto donep; 300 break; 301 } 302 } 303 donep: 304 if (changed) 305 copyback(); 306 } 307 308 copyback() /* copy temp or whatever back to /usr/spool/mail */ 309 { 310 register i, n, c; 311 int new = 0; 312 struct stat stbuf; 313 314 signal(SIGINT, SIG_IGN); 315 signal(SIGHUP, SIG_IGN); 316 signal(SIGQUIT, SIG_IGN); 317 lock(mailfile); 318 stat(mailfile, &stbuf); 319 if (stbuf.st_size != let[nlet].adr) { /* new mail has arrived */ 320 malf = fopen(mailfile, "r"); 321 if (malf == NULL) { 322 fprintf(stdout, "mail: can't re-read %s\n", mailfile); 323 done(); 324 } 325 fseek(malf, let[nlet].adr, 0); 326 fclose(tmpf); 327 tmpf = fopen(lettmp, "a"); 328 fseek(tmpf, let[nlet].adr, 0); 329 while ((c = fgetc(malf)) != EOF) 330 fputc(c, tmpf); 331 fclose(malf); 332 fclose(tmpf); 333 tmpf = fopen(lettmp, "r"); 334 let[++nlet].adr = stbuf.st_size; 335 new = 1; 336 } 337 malf = fopen(mailfile, "w"); 338 if (malf == NULL) { 339 fprintf(stderr, "mail: can't rewrite %s\n", lfil); 340 done(); 341 } 342 n = 0; 343 for (i = 0; i < nlet; i++) 344 if (let[i].change != 'd') { 345 copylet(i, malf, ORDINARY); 346 n++; 347 } 348 fclose(malf); 349 if (new) 350 fprintf(stdout, "new mail arrived\n"); 351 unlock(); 352 } 353 354 copymt(f1, f2) /* copy mail (f1) to temp (f2) */ 355 FILE *f1, *f2; 356 { 357 long nextadr; 358 359 nlet = nextadr = 0; 360 let[0].adr = 0; 361 while (fgets(line, LSIZE, f1) != NULL) { 362 if (isfrom(line)) 363 let[nlet++].adr = nextadr; 364 nextadr += strlen(line); 365 fputs(line, f2); 366 } 367 let[nlet].adr = nextadr; /* last plus 1 */ 368 } 369 370 copylet(n, f, type) FILE *f; 371 { int ch, k; 372 fseek(tmpf, let[n].adr, 0); 373 k = let[n+1].adr - let[n].adr; 374 while(k-- > 1 && (ch=fgetc(tmpf))!='\n') 375 if(type!=ZAP) fputc(ch,f); 376 if(type==REMOTE) 377 fprintf(f, " remote from %s\n", thissys); 378 else if (type==FORWARD) 379 fprintf(f, forwmsg); 380 else if(type==ORDINARY) 381 fputc(ch,f); 382 while(k-->1) 383 fputc(ch=fgetc(tmpf), f); 384 if(type!=ZAP || ch!= '\n') 385 fputc(fgetc(tmpf), f); 386 } 387 388 isfrom(lp) 389 register char *lp; 390 { 391 register char *p; 392 393 for (p = from; *p; ) 394 if (*lp++ != *p++) 395 return(0); 396 return(1); 397 } 398 399 sendmail(argc, argv) 400 char **argv; 401 { 402 char truename[100]; 403 int first; 404 register char *cp; 405 int gaver = 0; 406 # ifdef DELIVERMAIL 407 char *newargv[1000]; 408 register char **ap; 409 register char **vp; 410 int dflag; 411 412 dflag = 0; 413 if (argc < 1) 414 fprintf(stderr, "puke\n"); 415 for (vp = argv, ap = newargv + 1; (*ap = *vp++) != 0; ap++) 416 { 417 if (ap[0][0] == '-' && ap[0][1] == 'd') 418 dflag++; 419 } 420 if (!dflag) 421 { 422 /* give it to delivermail, rah rah! */ 423 unlink(lettmp); 424 ap = newargv+1; 425 if (rmail) 426 *ap-- = "-s"; 427 *ap = "-delivermail"; 428 execv(DELIVERMAIL, ap); 429 perror(DELIVERMAIL); 430 exit(EX_UNAVAILABLE); 431 } 432 # endif DELIVERMAIL 433 434 truename[0] = 0; 435 line[0] = '\0'; 436 437 /* 438 * When we fall out of this, argv[1] should be first name, 439 * argc should be number of names + 1. 440 */ 441 442 while (argc > 1 && *argv[1] == '-') { 443 cp = *++argv; 444 argc--; 445 switch (cp[1]) { 446 case 'r': 447 if (argc <= 0) { 448 usage(); 449 done(); 450 } 451 gaver++; 452 strcpy(truename, argv[1]); 453 fgets(line, LSIZE, stdin); 454 if (strcmpn("From", line, 4) == 0) 455 line[0] = '\0'; 456 argv++; 457 argc--; 458 break; 459 460 case 'h': 461 if (argc <= 0) { 462 usage(); 463 done(); 464 } 465 hseqno = atoi(argv[1]); 466 argv++; 467 argc--; 468 break; 469 470 # ifdef DELIVERMAIL 471 case 'd': 472 break; 473 # endif DELIVERMAIL 474 475 default: 476 usage(); 477 done(); 478 } 479 } 480 if (argc <= 1) { 481 usage(); 482 done(); 483 } 484 if (gaver == 0) 485 strcpy(truename, my_name); 486 /* 487 if (argc > 4 && strcmp(argv[1], "-r") == 0) { 488 strcpy(truename, argv[2]); 489 argc -= 2; 490 argv += 2; 491 fgets(line, LSIZE, stdin); 492 if (strcmpn("From", line, 4) == 0) 493 line[0] = '\0'; 494 } else 495 strcpy(truename, my_name); 496 */ 497 time(&iop); 498 fprintf(tmpf, "%s%s %s", from, truename, ctime(&iop)); 499 iop = ftell(tmpf); 500 flgf = 1; 501 for (first = 1;; first = 0) { 502 if (first && line[0] == '\0' && fgets(line, LSIZE, stdin) == NULL) 503 break; 504 if (!first && fgets(line, LSIZE, stdin) == NULL) 505 break; 506 if (line[0] == '.' && line[1] == '\n' && isatty(fileno(stdin))) 507 break; 508 if (isfrom(line)) 509 fputs(">", tmpf); 510 fputs(line, tmpf); 511 flgf = 0; 512 } 513 fputs("\n", tmpf); 514 nlet = 1; 515 let[0].adr = 0; 516 let[1].adr = ftell(tmpf); 517 fclose(tmpf); 518 if (flgf) 519 return; 520 tmpf = fopen(lettmp, "r"); 521 if (tmpf == NULL) { 522 fprintf(stderr, "mail: cannot reopen %s for reading\n", lettmp); 523 return; 524 } 525 while (--argc > 0) 526 if (!send(0, *++argv, truename)) 527 error++; 528 if (error) { 529 setuid(getuid()); 530 malf = fopen(dead, "w"); 531 if (malf == NULL) { 532 fprintf(stdout, "mail: cannot open %s\n", dead); 533 fclose(tmpf); 534 return; 535 } 536 copylet(0, malf, ZAP); 537 fclose(malf); 538 fprintf(stdout, "Mail saved in %s\n", dead); 539 } 540 fclose(tmpf); 541 } 542 543 sendrmt(n, name, rcmd) 544 char *name; 545 char *rcmd; 546 { 547 FILE *rmf, *popen(); 548 register char *p; 549 char rsys[64], cmd[64]; 550 register local, pid; 551 int sts; 552 553 local = 0; 554 if (index(name, '^')) { 555 while (p = index(name, '^')) 556 *p = '!'; 557 if (strncmp(name, "researc", 7)) { 558 strcpy(rsys, "research"); 559 if (*name != '!') 560 --name; 561 goto skip; 562 } 563 } 564 if (*name=='!') 565 name++; 566 for(p=rsys; *name!='!'; *p++ = *name++) 567 if (*name=='\0') { 568 local++; 569 break; 570 } 571 *p = '\0'; 572 if ((!local && *name=='\0') || (local && *rsys=='\0')) { 573 fprintf(stdout, "null name\n"); 574 return(0); 575 } 576 skip: 577 if ((pid = fork()) == -1) { 578 fprintf(stderr, "mail: can't create proc for remote\n"); 579 return(0); 580 } 581 if (pid) { 582 while (wait(&sts) != pid) { 583 if (wait(&sts)==-1) 584 return(0); 585 } 586 return(!sts); 587 } 588 setuid(getuid()); 589 if (local) 590 sprintf(cmd, "%s %s", rcmd, rsys); 591 else { 592 if (index(name+1, '!')) 593 sprintf(cmd, "uux - %s!rmail \\(%s\\)", rsys, name+1); 594 else 595 sprintf(cmd, "uux - %s!rmail %s", rsys, name+1); 596 } 597 if ((rmf=popen(cmd, "w")) == NULL) 598 exit(1); 599 copylet(n, rmf, local ? !strcmp(rcmd, "/bin/mail") ? FORWARD : ORDINARY : REMOTE); 600 pclose(rmf); 601 exit(0); 602 } 603 604 # ifndef DELIVERMAIL 605 /* 606 * Send mail on the Berkeley network. 607 * Sorry Bill, sendrmt() is so awful we just gave up. 608 */ 609 610 sendberkmail(n, name, fromaddr) 611 char name[]; 612 char fromaddr[]; 613 { 614 char cmd[200]; 615 register FILE *cmdf; 616 617 sprintf(cmd, "%s -h %d -f %s -t %s", RMAIL, hseqno, fromaddr, name); 618 if ((cmdf = popen(cmd, "w")) == NULL) { 619 perror(RMAIL); 620 return(0); 621 } 622 copylet(n, cmdf, ORDINARY); 623 pclose(cmdf); 624 return(9); 625 } 626 # endif 627 628 usage() 629 { 630 631 fprintf(stderr, "Usage: mail [ -f ] people . . .\n"); 632 } 633 634 send(n, name, fromaddr) 635 int n; 636 char *name; 637 char *fromaddr; 638 { 639 char file[100]; 640 register char *p; 641 register mask; 642 struct passwd *pw, *getpwnam(); 643 struct stat statb; 644 645 # ifndef DELIVERMAIL 646 stripfx(LOCNAM1, &name); 647 stripfx(LOCNAM2, &name); 648 stripfx(LOCNAM3, &name); 649 stripfx(LOCNAM4, &name); 650 if(*name == ':')name++; /* skip colon in to-name */ 651 for(p=name; *p!=':' && *p!='!' && *p!='^' &&*p!='\0'; p++); 652 /* if(*p == ':') return(sendrmt(n, name, RMAIL)); */ 653 if (*p == ':') 654 return(sendberkmail(n, name, fromaddr)); 655 else if (*p=='\0' && strcmp(name, "msgs") == 0) 656 return(sendrmt(n, "-s", "/usr/ucb/msgs")); 657 # endif 658 for(p=name; *p!='!'&&*p!='^' &&*p!='\0'; p++) 659 ; 660 if (*p == '!'|| *p=='^') 661 return(sendrmt(n, name, 0)); 662 if ((pw = getpwnam(name)) == NULL) { 663 fprintf(stdout, "mail: can't send to %s\n", name); 664 return(0); 665 } 666 cat(file, maildir, name); 667 if (stat(file, &statb) >= 0 && (statb.st_mode & S_IFMT) == S_IFDIR) { 668 strcat(file, "/"); 669 strcat(file, name); 670 } 671 mask = umask(MAILMODE); 672 malf = fopen(file, "a"); 673 umask(mask); 674 if (malf == NULL) { 675 fprintf(stdout, "mail: cannot append to %s\n", file); 676 return(0); 677 } 678 lock(file); 679 chown(file, pw->pw_uid, pw->pw_gid); 680 copylet(n, malf, ORDINARY); 681 fclose(malf); 682 unlock(); 683 return(1); 684 } 685 686 delete(i) 687 { 688 setsig(i, delete); 689 fprintf(stderr, "\n"); 690 if(delflg) 691 longjmp(sjbuf, 1); 692 done(); 693 } 694 695 /* 696 * Lock the specified mail file by setting the file mailfile.lock. 697 * We must, of course, be careful to unlink the lock file by a call 698 * to unlock before we stop. The algorithm used here is to see if 699 * the lock exists, and if it does, to check its modify time. If it 700 * is older than 30 seconds, we assume error and set our own file. 701 * Otherwise, we wait for 5 seconds and try again. 702 */ 703 704 char *maillock = ".lock"; /* Lock suffix for mailname */ 705 char *lockname = "/usr/spool/mail/tmXXXXXX"; 706 char locktmp[30]; /* Usable lock temporary */ 707 char curlock[50]; /* Last used name of lock */ 708 int locked; /* To note that we locked it */ 709 710 lock(file) 711 char *file; 712 { 713 register int f; 714 struct stat sbuf; 715 long curtime; 716 int statfailed; 717 718 if (locked || flgf) 719 return(0); 720 strcpy(curlock, file); 721 strcat(curlock, maillock); 722 strcpy(locktmp, lockname); 723 mktemp(locktmp); 724 unlink(locktmp); 725 statfailed = 0; 726 for (;;) { 727 f = lock1(locktmp, curlock); 728 if (f == 0) { 729 locked = 1; 730 return(0); 731 } 732 if (stat(curlock, &sbuf) < 0) { 733 if (statfailed++ > 5) 734 return(-1); 735 sleep(5); 736 continue; 737 } 738 statfailed = 0; 739 time(&curtime); 740 if (curtime < sbuf.st_ctime + 30) { 741 sleep(5); 742 continue; 743 } 744 unlink(curlock); 745 } 746 } 747 748 /* 749 * Remove the mail lock, and note that we no longer 750 * have it locked. 751 */ 752 753 unlock() 754 { 755 756 unlink(curlock); 757 locked = 0; 758 } 759 760 /* 761 * Attempt to set the lock by creating the temporary file, 762 * then doing a link/unlink. If it fails, return -1 else 0 763 */ 764 765 lock1(tempfile, name) 766 char tempfile[], name[]; 767 { 768 register int fd; 769 770 fd = creat(tempfile, 0); 771 if (fd < 0) 772 return(-1); 773 close(fd); 774 if (link(tempfile, name) < 0) { 775 unlink(tempfile); 776 return(-1); 777 } 778 unlink(tempfile); 779 return(0); 780 } 781 782 done() 783 { 784 if(locked) 785 unlock(); 786 unlink(lettmp); 787 unlink(locktmp); 788 exit(error); 789 } 790 791 cat(to, from1, from2) 792 char *to, *from1, *from2; 793 { 794 int i, j; 795 796 j = 0; 797 for (i=0; from1[i]; i++) 798 to[j++] = from1[i]; 799 for (i=0; from2[i]; i++) 800 to[j++] = from2[i]; 801 to[j] = 0; 802 } 803 804 char *getarg(s, p) /* copy p... into s, update p */ 805 register char *s, *p; 806 { 807 while (*p == ' ' || *p == '\t') 808 p++; 809 if (*p == '\n' || *p == '\0') 810 return(NULL); 811 while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0') 812 *s++ = *p++; 813 *s = '\0'; 814 return(p); 815 } 816 # ifndef DELIVERMAIL 817 /* 818 stripfx(prefix string, pointer to string) 819 820 takes a ptr to string and compares it to prefix string. 821 may be called multiple times 822 */ 823 stripfx(pfx, name) 824 char *pfx; 825 char **name; 826 { 827 register char *cp = *name; 828 829 while (*pfx && (*cp == *pfx || *cp == toupper(*pfx))) 830 cp++, pfx++; 831 if (*cp != ':' || *pfx != 0) 832 return; 833 *name = cp; 834 } 835 # endif 836