1: #if !defined(lint) && defined(DOSCCS) 2: static char sccsid[] = "@(#)mail.c 4.33.6 (2.11BSD) 1999/10/25"; 3: #endif 4: 5: #include <sys/param.h> 6: #include <sys/stat.h> 7: #include <sys/file.h> 8: 9: #include <ctype.h> 10: #include <stdio.h> 11: #include <pwd.h> 12: #include <utmp.h> 13: #include <signal.h> 14: #include <setjmp.h> 15: #include <string.h> 16: #include <sysexits.h> 17: #include <paths.h> 18: #include <stdlib.h> 19: #include <time.h> 20: #include <unistd.h> 21: 22: /* copylet flags */ 23: #define REMOTE 1 /* remote mail, add rmtmsg */ 24: #define ORDINARY 2 25: #define ZAP 3 /* zap header and trailing empty line */ 26: #define FORWARD 4 27: 28: #define LSIZE 256 29: #define MAXLET 300 /* maximum number of letters */ 30: #define MAILMODE 0600 /* mode of created mail */ 31: 32: char line[LSIZE]; 33: char resp[LSIZE]; 34: struct let { 35: long adr; 36: char change; 37: } let[MAXLET]; 38: int nlet = 0; 39: char lfil[50]; 40: long iop; 41: char lettmp[] = "/tmp/maXXXXX"; 42: char maildir[] = "/usr/spool/mail/"; 43: char mailfile[] = "/usr/spool/mail/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; 44: char dead[] = "dead.letter"; 45: char forwmsg[] = " forwarded\n"; 46: FILE *tmpf; 47: FILE *malf; 48: char my_name[60]; 49: int error; 50: int changed; 51: int forward; 52: char from[] = "From "; 53: int delex(); 54: int flgf; 55: int flgp; 56: int delflg = 1; 57: int hseqno; 58: jmp_buf sjbuf; 59: int rmail; 60: 61: main(argc, argv) 62: char **argv; 63: { 64: register int i; 65: char *name; 66: struct passwd *pwent; 67: 68: if (!(name = getlogin()) || !*name || !(pwent = getpwnam(name)) || 69: getuid() != pwent->pw_uid) 70: pwent = getpwuid(getuid()); 71: strncpy(my_name, pwent ? pwent->pw_name : "???", sizeof(my_name)-1); 72: if (setjmp(sjbuf)) 73: done(); 74: for (i=SIGHUP; i<=SIGTERM; i++) 75: setsig(i, delex); 76: i = mkstemp(lettmp); 77: tmpf = fdopen(i, "r+w"); 78: if (i < 0 || tmpf == NULL) 79: panic("mail: %s: cannot open for writing", lettmp); 80: /* 81: * This protects against others reading mail from temp file and 82: * if we exit, the file will be deleted already. 83: */ 84: unlink(lettmp); 85: if (argv[0][0] == 'r') 86: rmail++; 87: if (argv[0][0] != 'r' && /* no favors for rmail*/ 88: (argc == 1 || argv[1][0] == '-' && !any(argv[1][1], "rhd"))) 89: printmail(argc, argv); 90: else 91: bulkmail(argc, argv); 92: done(); 93: } 94: 95: setsig(i, f) 96: int i; 97: sig_t f; 98: { 99: if (signal(i, SIG_IGN) != SIG_IGN) 100: signal(i, f); 101: } 102: 103: any(c, str) 104: register int c; 105: register char *str; 106: { 107: 108: while (*str) 109: if (c == *str++) 110: return(1); 111: return(0); 112: } 113: 114: printmail(argc, argv) 115: char **argv; 116: { 117: int flg, i, j, print; 118: char *p, *getarg(); 119: struct stat statb; 120: 121: setuid(getuid()); 122: cat(mailfile, maildir, my_name); 123: for (; argc > 1; argv++, argc--) { 124: if (argv[1][0] != '-') 125: break; 126: switch (argv[1][1]) { 127: 128: case 'p': 129: flgp++; 130: /* fall thru... */ 131: case 'q': 132: delflg = 0; 133: break; 134: 135: case 'f': 136: if (argc >= 3) { 137: strcpy(mailfile, argv[2]); 138: argv++, argc--; 139: } 140: break; 141: 142: case 'b': 143: forward = 1; 144: break; 145: 146: default: 147: panic("unknown option %c", argv[1][1]); 148: /*NOTREACHED*/ 149: } 150: } 151: malf = fopen(mailfile, "r"); 152: if (malf == NULL) { 153: printf("No mail.\n"); 154: return; 155: } 156: flock(fileno(malf), LOCK_SH); 157: copymt(malf, tmpf); 158: fclose(malf); /* implicit unlock */ 159: fseek(tmpf, 0L, L_SET); 160: 161: changed = 0; 162: print = 1; 163: for (i = 0; i < nlet; ) { 164: j = forward ? i : nlet - i - 1; 165: if (setjmp(sjbuf)) { 166: print = 0; 167: } else { 168: if (print) 169: copylet(j, stdout, ORDINARY); 170: print = 1; 171: } 172: if (flgp) { 173: i++; 174: continue; 175: } 176: setjmp(sjbuf); 177: fputs("? ", stdout); 178: fflush(stdout); 179: if (fgets(resp, LSIZE, stdin) == NULL) 180: break; 181: switch (resp[0]) { 182: 183: default: 184: printf("usage\n"); 185: case '?': 186: print = 0; 187: printf("q\tquit\n"); 188: printf("x\texit without changing mail\n"); 189: printf("p\tprint\n"); 190: printf("s[file]\tsave (default mbox)\n"); 191: printf("w[file]\tsame without header\n"); 192: printf("-\tprint previous\n"); 193: printf("d\tdelete\n"); 194: printf("+\tnext (no delete)\n"); 195: printf("m user\tmail to user\n"); 196: printf("! cmd\texecute cmd\n"); 197: break; 198: 199: case '+': 200: case 'n': 201: case '\n': 202: i++; 203: break; 204: case 'x': 205: changed = 0; 206: case 'q': 207: goto donep; 208: case 'p': 209: break; 210: case '^': 211: case '-': 212: if (--i < 0) 213: i = 0; 214: break; 215: case 'y': 216: case 'w': 217: case 's': 218: flg = 0; 219: if (resp[1] != '\n' && resp[1] != ' ') { 220: printf("illegal\n"); 221: flg++; 222: print = 0; 223: continue; 224: } 225: if (resp[1] == '\n' || resp[1] == '\0') { 226: p = getenv("HOME"); 227: if (p != 0) 228: cat(resp+1, p, "/mbox"); 229: else 230: cat(resp+1, "", "mbox"); 231: } 232: for (p = resp+1; (p = getarg(lfil, p)) != NULL; ) { 233: malf = fopen(lfil, "a"); 234: if (malf == NULL) { 235: printf("mail: %s: cannot append\n", 236: lfil); 237: flg++; 238: continue; 239: } 240: copylet(j, malf, resp[0]=='w'? ZAP: ORDINARY); 241: fclose(malf); 242: } 243: if (flg) 244: print = 0; 245: else { 246: let[j].change = 'd'; 247: changed++; 248: i++; 249: } 250: break; 251: case 'm': 252: flg = 0; 253: if (resp[1] == '\n' || resp[1] == '\0') { 254: i++; 255: continue; 256: } 257: if (resp[1] != ' ') { 258: printf("invalid command\n"); 259: flg++; 260: print = 0; 261: continue; 262: } 263: for (p = resp+1; (p = getarg(lfil, p)) != NULL; ) 264: if (!sendmail(j, lfil, my_name)) 265: flg++; 266: if (flg) 267: print = 0; 268: else { 269: let[j].change = 'd'; 270: changed++; 271: i++; 272: } 273: break; 274: case '!': 275: system(resp+1); 276: printf("!\n"); 277: print = 0; 278: break; 279: case 'd': 280: let[j].change = 'd'; 281: changed++; 282: i++; 283: if (resp[1] == 'q') 284: goto donep; 285: break; 286: } 287: } 288: donep: 289: if (changed) 290: copyback(); 291: } 292: 293: /* copy temp or whatever back to /usr/spool/mail */ 294: copyback() 295: { 296: register int i, c; 297: sigset_t set; 298: int fd, new = 0; 299: struct stat stbuf; 300: 301: sigemptyset(&set); 302: sigaddset(&set, SIGINT); 303: sigaddset(&set, SIGHUP); 304: sigaddset(&set, SIGQUIT); 305: (void)sigprocmask(SIG_BLOCK, &set, NULL); 306: fd = open(mailfile, O_RDWR | O_CREAT, MAILMODE); 307: if (fd >= 0) { 308: flock(fd, LOCK_EX); 309: malf = fdopen(fd, "r+w"); 310: } 311: if (fd < 0 || malf == NULL) 312: panic("can't rewrite %s", lfil); 313: fstat(fd, &stbuf); 314: if (stbuf.st_size != let[nlet].adr) { /* new mail has arrived */ 315: fseek(malf, let[nlet].adr, L_SET); 316: fseek(tmpf, let[nlet].adr, L_SET); 317: while ((c = getc(malf)) != EOF) 318: putc(c, tmpf); 319: let[++nlet].adr = stbuf.st_size; 320: new = 1; 321: fseek(malf, 0L, L_SET); 322: } 323: ftruncate(fd, 0L); 324: for (i = 0; i < nlet; i++) 325: if (let[i].change != 'd') 326: copylet(i, malf, ORDINARY); 327: fclose(malf); /* implict unlock */ 328: if (new) 329: printf("New mail has arrived.\n"); 330: (void)sigprocmask(SIG_UNBLOCK, &set, NULL); 331: } 332: 333: /* copy mail (f1) to temp (f2) */ 334: copymt(f1, f2) 335: FILE *f1, *f2; 336: { 337: long nextadr; 338: 339: nlet = nextadr = 0; 340: let[0].adr = 0; 341: while (fgets(line, LSIZE, f1) != NULL) { 342: if (isfrom(line)) 343: let[nlet++].adr = nextadr; 344: nextadr += strlen(line); 345: fputs(line, f2); 346: } 347: let[nlet].adr = nextadr; /* last plus 1 */ 348: } 349: 350: copylet(n, f, type) 351: FILE *f; 352: { 353: int ch; 354: long k; 355: char hostname[MAXHOSTNAMELEN]; 356: 357: fseek(tmpf, let[n].adr, L_SET); 358: k = let[n+1].adr - let[n].adr; 359: while (k-- > 1 && (ch = getc(tmpf)) != '\n') 360: if (type != ZAP) 361: putc(ch, f); 362: switch (type) { 363: 364: case REMOTE: 365: gethostname(hostname, sizeof (hostname)); 366: fprintf(f, " remote from %s\n", hostname); 367: break; 368: 369: case FORWARD: 370: fprintf(f, forwmsg); 371: break; 372: 373: case ORDINARY: 374: putc(ch, f); 375: break; 376: 377: case ZAP: 378: break; 379: 380: default: 381: panic("Bad letter type %d to copylet.", type); 382: } 383: while (k-- > 1) { 384: ch = getc(tmpf); 385: putc(ch, f); 386: } 387: if (type != ZAP || ch != '\n') 388: putc(getc(tmpf), f); 389: } 390: 391: isfrom(lp) 392: register char *lp; 393: { 394: register char *p; 395: 396: for (p = from; *p; ) 397: if (*lp++ != *p++) 398: return(0); 399: return(1); 400: } 401: 402: bulkmail(argc, argv) 403: char **argv; 404: { 405: char *truename; 406: int first; 407: register char *cp; 408: char *newargv[1000]; 409: register char **ap; 410: register char **vp; 411: int dflag; 412: 413: dflag = 0; 414: delflg = 0; 415: if (argc < 1) { 416: fprintf(stderr, "puke\n"); 417: return; 418: } 419: for (vp = argv, ap = newargv + 1; (*ap = *vp++) != 0; ap++) 420: if (ap[0][0] == '-' && ap[0][1] == 'd') 421: dflag++; 422: if (!dflag) { 423: /* give it to sendmail, rah rah! */ 424: unlink(lettmp); 425: ap = newargv+1; 426: if (rmail) 427: *ap-- = "-s"; 428: *ap = "-sendmail"; 429: setuid(getuid()); 430: execv(_PATH_SENDMAIL, ap); 431: perror(_PATH_SENDMAIL); 432: exit(EX_UNAVAILABLE); 433: } 434: 435: truename = 0; 436: line[0] = '\0'; 437: 438: /* 439: * When we fall out of this, argv[1] should be first name, 440: * argc should be number of names + 1. 441: */ 442: 443: while (argc > 1 && *argv[1] == '-') { 444: cp = *++argv; 445: argc--; 446: switch (cp[1]) { 447: case 'r': 448: if (argc <= 1) 449: usage(); 450: truename = argv[1]; 451: fgets(line, LSIZE, stdin); 452: if (strncmp("From", line, 4) == 0) 453: line[0] = '\0'; 454: argv++; 455: argc--; 456: break; 457: 458: case 'h': 459: if (argc <= 1) 460: usage(); 461: hseqno = atoi(argv[1]); 462: argv++; 463: argc--; 464: break; 465: 466: case 'd': 467: break; 468: 469: default: 470: usage(); 471: } 472: } 473: if (argc <= 1) 474: usage(); 475: if (truename == 0) 476: truename = my_name; 477: time(&iop); 478: fprintf(tmpf, "%s%s %s", from, truename, ctime(&iop)); 479: iop = ftell(tmpf); 480: flgf = first = 1; 481: for (;;) { 482: if (first) { 483: first = 0; 484: if (*line == '\0' && fgets(line, LSIZE, stdin) == NULL) 485: break; 486: } else { 487: if (fgets(line, LSIZE, stdin) == NULL) 488: break; 489: } 490: if (*line == '.' && line[1] == '\n' && isatty(fileno(stdin))) 491: break; 492: if (isfrom(line)) 493: putc('>', tmpf); 494: fputs(line, tmpf); 495: flgf = 0; 496: } 497: putc('\n', tmpf); 498: nlet = 1; 499: let[0].adr = 0; 500: let[1].adr = ftell(tmpf); 501: if (flgf) 502: return; 503: while (--argc > 0) 504: if (!sendmail(0, *++argv, truename)) 505: error++; 506: if (error && safefile(dead)) { 507: setuid(getuid()); 508: malf = fopen(dead, "w"); 509: if (malf == NULL) { 510: printf("mail: cannot open %s\n", dead); 511: fclose(tmpf); 512: return; 513: } 514: copylet(0, malf, ZAP); 515: fclose(malf); 516: printf("Mail saved in %s\n", dead); 517: } 518: fclose(tmpf); 519: } 520: 521: sendrmt(n, name) 522: char *name; 523: { 524: FILE *rmf, *popen(); 525: register char *p; 526: char rsys[64], cmd[64]; 527: register pid; 528: int sts; 529: 530: for (p=rsys; *name!='!'; *p++ = *name++) 531: if (*name=='\0') 532: return(0); /* local address, no '!' */ 533: *p = '\0'; 534: if (name[1]=='\0') { 535: printf("null name\n"); 536: return(0); 537: } 538: skip: 539: if ((pid = fork()) == -1) { 540: fprintf(stderr, "mail: can't create proc for remote\n"); 541: return(0); 542: } 543: if (pid) { 544: while (wait(&sts) != pid) { 545: if (wait(&sts)==-1) 546: return(0); 547: } 548: return(!sts); 549: } 550: setuid(getuid()); 551: if (any('!', name+1)) 552: (void)sprintf(cmd, "uux - %s!rmail \\(%s\\)", rsys, name+1); 553: else 554: (void)sprintf(cmd, "uux - %s!rmail %s", rsys, name+1); 555: if ((rmf=popen(cmd, "w")) == NULL) 556: exit(1); 557: copylet(n, rmf, REMOTE); 558: exit(pclose(rmf) != 0); 559: } 560: 561: usage() 562: { 563: 564: fprintf(stderr, "Usage: mail [ -f ] people . . .\n"); 565: error = EX_USAGE; 566: done(); 567: } 568: 569: #include <sys/socket.h> 570: #include <netinet/in.h> 571: #include <netdb.h> 572: 573: notifybiff(msg) 574: char *msg; 575: { 576: static struct sockaddr_in addr; 577: static int f = -1; 578: 579: if (addr.sin_family == 0) { 580: struct hostent *hp = gethostbyname("localhost"); 581: struct servent *sp = getservbyname("biff", "udp"); 582: 583: if (hp && sp) { 584: addr.sin_family = hp->h_addrtype; 585: bcopy(hp->h_addr, &addr.sin_addr, hp->h_length); 586: addr.sin_port = sp->s_port; 587: } 588: } 589: if (addr.sin_family) { 590: if (f < 0) 591: f = socket(AF_INET, SOCK_DGRAM, 0); 592: if (f >= 0) 593: sendto(f, msg, strlen(msg)+1, 0, &addr, sizeof (addr)); 594: } 595: } 596: 597: sendmail(n, name, fromaddr) 598: int n; 599: char *name, *fromaddr; 600: { 601: char file[256]; 602: int fd; 603: struct passwd *pw; 604: char buf[128]; 605: off_t oldsize; 606: 607: if (*name=='!') 608: name++; 609: if (any('!', name)) 610: return (sendrmt(n, name)); 611: if ((pw = getpwnam(name)) == NULL) { 612: printf("mail: can't send to %s\n", name); 613: return(0); 614: } 615: cat(file, maildir, name); 616: if (!safefile(file)) 617: return(0); 618: fd = open(file, O_WRONLY | O_CREAT | O_EXLOCK, MAILMODE); 619: if (fd >= 0) 620: malf = fdopen(fd, "a"); 621: if (fd < 0 || malf == NULL) { 622: close(fd); 623: printf("mail: %s: cannot append\n", file); 624: return(0); 625: } 626: fchown(fd, pw->pw_uid, pw->pw_gid); 627: oldsize = ftell(malf); 628: (void)sprintf(buf, "%s@%ld\n", name, oldsize); 629: 630: copylet(n, malf, ORDINARY); /* Try to deliver the message */ 631: 632: /* If there is any error during the delivery of the message, 633: * the mail file may be corrupted (incomplete last line) and 634: * any subsequent mail will be apparently lost, since the 635: * <NL> before the 'From ' won't be there. So, restore the 636: * file to the pre-delivery size and report an error. 637: * 638: * fflush does "_flag |= _IOERR" so we don't need to check both the 639: # return from fflush and the ferror status. 640: */ 641: (void)fflush(malf); 642: if (ferror(malf)) { 643: printf("mail: %s: cannot append\n", file); 644: ftruncate(fd, oldsize); 645: fclose(malf); 646: return(0); 647: } 648: fclose(malf); 649: notifybiff(buf); 650: return(1); 651: } 652: 653: delex(i) 654: { 655: sigset_t sigt; 656: 657: if (i != SIGINT) { 658: setsig(i, SIG_DFL); 659: sigemptyset(&sigt); 660: sigaddset(&sigt, i); 661: sigprocmask(SIG_UNBLOCK, &sigt, NULL); 662: } 663: putc('\n', stderr); 664: if (delflg) 665: longjmp(sjbuf, 1); 666: if (error == 0) 667: error = i; 668: done(); 669: } 670: 671: done() 672: { 673: 674: unlink(lettmp); 675: exit(error); 676: } 677: 678: cat(to, from1, from2) 679: char *to, *from1, *from2; 680: { 681: register char *cp, *dp; 682: 683: cp = to; 684: for (dp = from1; *cp = *dp++; cp++) 685: ; 686: for (dp = from2; *cp++ = *dp++; ) 687: ; 688: } 689: 690: /* copy p... into s, update p */ 691: char * 692: getarg(s, p) 693: register char *s, *p; 694: { 695: while (*p == ' ' || *p == '\t') 696: p++; 697: if (*p == '\n' || *p == '\0') 698: return(NULL); 699: while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0') 700: *s++ = *p++; 701: *s = '\0'; 702: return(p); 703: } 704: 705: safefile(f) 706: char *f; 707: { 708: struct stat statb; 709: 710: if (lstat(f, &statb) < 0) 711: return (1); 712: if (statb.st_nlink != 1 || (statb.st_mode & S_IFMT) == S_IFLNK) { 713: fprintf(stderr, 714: "mail: %s has more than one link or is a symbolic link\n", 715: f); 716: return (0); 717: } 718: return (1); 719: } 720: 721: panic(msg, a1, a2, a3) 722: char *msg; 723: { 724: 725: fprintf(stderr, "mail: "); 726: fprintf(stderr, msg, a1, a2, a3); 727: fprintf(stderr, "\n"); 728: done(); 729: }