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