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