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.9 11/14/82"; 12 13 #define DELIVERMAIL "/etc/delivermail" 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 (~0644) /* 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) FILE *f; 362 { int ch, k; 363 fseek(tmpf, let[n].adr, 0); 364 k = let[n+1].adr - let[n].adr; 365 while(k-- > 1 && (ch=fgetc(tmpf))!='\n') 366 if(type!=ZAP) fputc(ch,f); 367 if(type==REMOTE) { 368 char hostname[32]; 369 gethostname(hostname, sizeof (hostname)); 370 fprintf(f, " remote from %s\n", hostname); 371 } else if (type==FORWARD) 372 fprintf(f, forwmsg); 373 else if(type==ORDINARY) 374 fputc(ch,f); 375 while(k-->1) 376 fputc(ch=fgetc(tmpf), f); 377 if(type!=ZAP || ch!= '\n') 378 fputc(fgetc(tmpf), f); 379 } 380 381 isfrom(lp) 382 register char *lp; 383 { 384 register char *p; 385 386 for (p = from; *p; ) 387 if (*lp++ != *p++) 388 return(0); 389 return(1); 390 } 391 392 bulkmail(argc, argv) 393 char **argv; 394 { 395 char truename[100]; 396 int first; 397 register char *cp; 398 int gaver = 0; 399 char *newargv[1000]; 400 register char **ap; 401 register char **vp; 402 int dflag; 403 404 dflag = 0; 405 if (argc < 1) 406 fprintf(stderr, "puke\n"); 407 for (vp = argv, ap = newargv + 1; (*ap = *vp++) != 0; ap++) 408 { 409 if (ap[0][0] == '-' && ap[0][1] == 'd') 410 dflag++; 411 } 412 if (!dflag) 413 { 414 /* give it to delivermail, rah rah! */ 415 unlink(lettmp); 416 ap = newargv+1; 417 if (rmail) 418 *ap-- = "-s"; 419 *ap = "-delivermail"; 420 setuid(getuid()); 421 execv(DELIVERMAIL, ap); 422 perror(DELIVERMAIL); 423 exit(EX_UNAVAILABLE); 424 } 425 426 truename[0] = 0; 427 line[0] = '\0'; 428 429 /* 430 * When we fall out of this, argv[1] should be first name, 431 * argc should be number of names + 1. 432 */ 433 434 while (argc > 1 && *argv[1] == '-') { 435 cp = *++argv; 436 argc--; 437 switch (cp[1]) { 438 case 'r': 439 if (argc <= 0) { 440 usage(); 441 done(); 442 } 443 gaver++; 444 strcpy(truename, argv[1]); 445 fgets(line, LSIZE, stdin); 446 if (strcmpn("From", line, 4) == 0) 447 line[0] = '\0'; 448 argv++; 449 argc--; 450 break; 451 452 case 'h': 453 if (argc <= 0) { 454 usage(); 455 done(); 456 } 457 hseqno = atoi(argv[1]); 458 argv++; 459 argc--; 460 break; 461 462 case 'd': 463 break; 464 465 default: 466 usage(); 467 done(); 468 } 469 } 470 if (argc <= 1) { 471 usage(); 472 done(); 473 } 474 if (gaver == 0) 475 strcpy(truename, my_name); 476 /* 477 if (argc > 4 && strcmp(argv[1], "-r") == 0) { 478 strcpy(truename, argv[2]); 479 argc -= 2; 480 argv += 2; 481 fgets(line, LSIZE, stdin); 482 if (strcmpn("From", line, 4) == 0) 483 line[0] = '\0'; 484 } else 485 strcpy(truename, my_name); 486 */ 487 time(&iop); 488 fprintf(tmpf, "%s%s %s", from, truename, ctime(&iop)); 489 iop = ftell(tmpf); 490 flgf = 1; 491 for (first = 1;; first = 0) { 492 if (first && line[0] == '\0' && fgets(line, LSIZE, stdin) == NULL) 493 break; 494 if (!first && fgets(line, LSIZE, stdin) == NULL) 495 break; 496 if (line[0] == '.' && line[1] == '\n' && isatty(fileno(stdin))) 497 break; 498 if (isfrom(line)) 499 fputs(">", tmpf); 500 fputs(line, tmpf); 501 flgf = 0; 502 } 503 fputs("\n", tmpf); 504 nlet = 1; 505 let[0].adr = 0; 506 let[1].adr = ftell(tmpf); 507 fclose(tmpf); 508 if (flgf) 509 return; 510 tmpf = fopen(lettmp, "r"); 511 if (tmpf == NULL) { 512 fprintf(stderr, "mail: cannot reopen %s for reading\n", lettmp); 513 return; 514 } 515 while (--argc > 0) { 516 if (!sendmail(0, *++argv, truename)) 517 error++; 518 } 519 if (error) { 520 setuid(getuid()); 521 malf = fopen(dead, "w"); 522 if (malf == NULL) { 523 fprintf(stdout, "mail: cannot open %s\n", dead); 524 fclose(tmpf); 525 return; 526 } 527 copylet(0, malf, ZAP); 528 fclose(malf); 529 fprintf(stdout, "Mail saved in %s\n", dead); 530 } 531 fclose(tmpf); 532 } 533 534 sendrmt(n, name, rcmd) 535 char *name; 536 char *rcmd; 537 { 538 FILE *rmf, *popen(); 539 register char *p; 540 char rsys[64], cmd[64]; 541 register local, pid; 542 int sts; 543 544 local = 0; 545 if (index(name, '^')) { 546 while (p = index(name, '^')) 547 *p = '!'; 548 if (strncmp(name, "researc", 7)) { 549 strcpy(rsys, "research"); 550 if (*name != '!') 551 --name; 552 goto skip; 553 } 554 } 555 if (*name=='!') 556 name++; 557 for(p=rsys; *name!='!'; *p++ = *name++) 558 if (*name=='\0') { 559 local++; 560 break; 561 } 562 *p = '\0'; 563 if ((!local && *name=='\0') || (local && *rsys=='\0')) { 564 fprintf(stdout, "null name\n"); 565 return(0); 566 } 567 skip: 568 if ((pid = fork()) == -1) { 569 fprintf(stderr, "mail: can't create proc for remote\n"); 570 return(0); 571 } 572 if (pid) { 573 while (wait(&sts) != pid) { 574 if (wait(&sts)==-1) 575 return(0); 576 } 577 return(!sts); 578 } 579 setuid(getuid()); 580 if (local) 581 sprintf(cmd, "%s %s", rcmd, rsys); 582 else { 583 if (index(name+1, '!')) 584 sprintf(cmd, "uux - %s!rmail \\(%s\\)", rsys, name+1); 585 else 586 sprintf(cmd, "uux - %s!rmail %s", rsys, name+1); 587 } 588 if ((rmf=popen(cmd, "w")) == NULL) 589 exit(1); 590 copylet(n, rmf, local ? !strcmp(rcmd, "/bin/mail") ? FORWARD : ORDINARY : REMOTE); 591 pclose(rmf); 592 exit(0); 593 } 594 595 usage() 596 { 597 598 fprintf(stderr, "Usage: mail [ -f ] people . . .\n"); 599 } 600 601 #include <sys/socket.h> 602 #include <netinet/in.h> 603 struct sockaddr_in biffaddr = { AF_INET, IPPORT_BIFFUDP }; 604 char *localhost = "localhost"; 605 606 sendmail(n, name, fromaddr) 607 int n; 608 char *name; 609 char *fromaddr; 610 { 611 char file[100]; 612 register char *p; 613 register mask; 614 struct passwd *pw, *getpwnam(); 615 struct stat statb; 616 char buf[128]; 617 int f; 618 619 for(p=name; *p!='!'&&*p!='^' &&*p!='\0'; p++) 620 ; 621 if (*p == '!'|| *p=='^') 622 return(sendrmt(n, name, 0)); 623 if ((pw = getpwnam(name)) == NULL) { 624 fprintf(stdout, "mail: can't send to %s\n", name); 625 return(0); 626 } 627 cat(file, maildir, name); 628 if (stat(file, &statb) >= 0 && (statb.st_mode & S_IFMT) == S_IFDIR) { 629 strcat(file, "/"); 630 strcat(file, name); 631 } 632 mask = umask(MAILMODE); 633 if (stat(file, &statb) >= 0 && statb.st_nlink != 1) { 634 fprintf(stdout, "mail: %s's mail file has more than one link\n", name); 635 return(0); 636 } 637 malf = fopen(file, "a"); 638 umask(mask); 639 if (malf == NULL) { 640 fprintf(stdout, "mail: cannot append to %s\n", file); 641 return(0); 642 } 643 lock(file); 644 chown(file, pw->pw_uid, pw->pw_gid); 645 { 646 f = socket(0, SOCK_DGRAM, 0, 0); 647 sprintf(buf, "%s@%d\n", name, ftell(malf)); 648 } 649 copylet(n, malf, ORDINARY); 650 fclose(malf); 651 if (f >= 0) { 652 biffaddr.sin_addr.s_addr = rhost(&localhost); 653 #if vax 654 biffaddr.sin_port = 655 ((biffaddr.sin_port<<8)&0xff00)|((biffaddr.sin_port>>8)&0xff); 656 #endif 657 sendto(f, buf, strlen(buf)+1, 0, &biffaddr, sizeof (biffaddr)); 658 close(f); 659 } 660 unlock(); 661 return(1); 662 } 663 664 delete(i) 665 { 666 setsig(i, delete); 667 fprintf(stderr, "\n"); 668 if(delflg) 669 longjmp(sjbuf, 1); 670 done(); 671 } 672 673 /* 674 * Lock the specified mail file by setting the file mailfile.lock. 675 * We must, of course, be careful to unlink the lock file by a call 676 * to unlock before we stop. The algorithm used here is to see if 677 * the lock exists, and if it does, to check its modify time. If it 678 * is older than 30 seconds, we assume error and set our own file. 679 * Otherwise, we wait for 5 seconds and try again. 680 */ 681 682 char *maillock = ".lock"; /* Lock suffix for mailname */ 683 char *lockname = "/usr/spool/mail/tmXXXXXX"; 684 char locktmp[30]; /* Usable lock temporary */ 685 char curlock[50]; /* Last used name of lock */ 686 int locked; /* To note that we locked it */ 687 688 lock(file) 689 char *file; 690 { 691 register int f; 692 struct stat sbuf; 693 long curtime; 694 int statfailed; 695 696 if (locked || flgf) 697 return(0); 698 strcpy(curlock, file); 699 strcat(curlock, maillock); 700 strcpy(locktmp, lockname); 701 mktemp(locktmp); 702 unlink(locktmp); 703 statfailed = 0; 704 for (;;) { 705 f = lock1(locktmp, curlock); 706 if (f == 0) { 707 locked = 1; 708 return(0); 709 } 710 if (stat(curlock, &sbuf) < 0) { 711 if (statfailed++ > 5) 712 return(-1); 713 sleep(5); 714 continue; 715 } 716 statfailed = 0; 717 time(&curtime); 718 if (curtime < sbuf.st_ctime + 30) { 719 sleep(5); 720 continue; 721 } 722 unlink(curlock); 723 } 724 } 725 726 /* 727 * Remove the mail lock, and note that we no longer 728 * have it locked. 729 */ 730 731 unlock() 732 { 733 734 unlink(curlock); 735 locked = 0; 736 } 737 738 /* 739 * Attempt to set the lock by creating the temporary file, 740 * then doing a link/unlink. If it fails, return -1 else 0 741 */ 742 743 lock1(tempfile, name) 744 char tempfile[], name[]; 745 { 746 register int fd; 747 748 fd = creat(tempfile, 0); 749 if (fd < 0) 750 return(-1); 751 close(fd); 752 if (link(tempfile, name) < 0) { 753 unlink(tempfile); 754 return(-1); 755 } 756 unlink(tempfile); 757 return(0); 758 } 759 760 done() 761 { 762 if(locked) 763 unlock(); 764 unlink(lettmp); 765 unlink(locktmp); 766 exit(error); 767 } 768 769 cat(to, from1, from2) 770 char *to, *from1, *from2; 771 { 772 int i, j; 773 774 j = 0; 775 for (i=0; from1[i]; i++) 776 to[j++] = from1[i]; 777 for (i=0; from2[i]; i++) 778 to[j++] = from2[i]; 779 to[j] = 0; 780 } 781 782 char *getarg(s, p) /* copy p... into s, update p */ 783 register char *s, *p; 784 { 785 while (*p == ' ' || *p == '\t') 786 p++; 787 if (*p == '\n' || *p == '\0') 788 return(NULL); 789 while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0') 790 *s++ = *p++; 791 *s = '\0'; 792 return(p); 793 } 794