1: #include <stdio.h> 2: #include <pwd.h> 3: #include <utmp.h> 4: #include <signal.h> 5: #include <sys/types.h> 6: #include <sys/stat.h> 7: #include <setjmp.h> 8: #include <whoami.h> 9: 10: #define MSGS /* pipe mail to "msgs" to /usr/ucb/msgs */ 11: /*copylet flags */ 12: /*remote mail, add rmtmsg */ 13: #define REMOTE 1 14: /* zap header and trailing empty line */ 15: #define ZAP 3 16: #define ORDINARY 2 17: #define FORWARD 4 18: #define LSIZE 256 19: #define MAXLET 300 /* maximum number of letters */ 20: #define MAILMODE (~0600) /* mode of created mail */ 21: 22: char line[LSIZE]; 23: char resp[LSIZE]; 24: struct let { 25: long adr; 26: char change; 27: } let[MAXLET]; 28: int nlet = 0; 29: char lfil[50]; 30: long iop, time(); 31: char lettmp[] = "/tmp/maXXXXX"; 32: char maildir[] = "/usr/spool/mail/"; 33: char mailfile[] = "/usr/spool/mail/xxxxxxxxxxxxxxxxxxxxxxx"; 34: char dead[] = "dead.letter"; 35: char forwmsg[] = " forwarded\n"; 36: char *curlock; 37: int lockerror; 38: FILE *tmpf; 39: FILE *malf; 40: char *my_name; 41: struct passwd *getpwuid(); 42: int error; 43: int locked; 44: int changed; 45: int forward; 46: char from[] = "From "; 47: long ftell(); 48: int delete(); 49: char *ctime(); 50: int flgf; 51: int flgp; 52: int delflg = 1; 53: jmp_buf sjbuf; 54: char hostname[32]; 55: 56: main(argc, argv) 57: char **argv; 58: { 59: register i; 60: char sobuf[BUFSIZ]; 61: 62: setbuf(stdout, sobuf); 63: mktemp(lettmp); 64: unlink(lettmp); 65: if (my_name == NULL) { 66: struct passwd *pwent; 67: pwent = getpwuid(getuid()); 68: if (pwent==NULL) 69: my_name = "???"; 70: else 71: my_name = pwent->pw_name; 72: } 73: if(setjmp(sjbuf)) done(); 74: for (i = SIGHUP; i <= SIGTERM; i++) 75: setsig(i, delete); 76: tmpf = fopen(lettmp, "w"); 77: if (tmpf == NULL) { 78: fprintf(stderr, "mail: cannot open %s for writing\n", lettmp); 79: done(); 80: } 81: if (argv[0][0] != 'r' && /* no favors for rmail*/ 82: (argc == 1 || argv[1][0] == '-')) 83: printmail(argc, argv); 84: else 85: sendmail(argc, argv); 86: done(); 87: } 88: 89: setsig(i, f) 90: int i; 91: int (*f)(); 92: { 93: if(signal(i, SIG_IGN) != SIG_IGN) 94: signal(i, f); 95: } 96: 97: printmail(argc, argv) 98: char **argv; 99: { 100: int flg, i, j, print; 101: char *p, *getarg(); 102: 103: setuid(getuid()); 104: cat(mailfile, maildir, my_name); 105: for (; argc>1; argv++, argc--) { 106: if (argv[1][0]=='-') { 107: if (argv[1][1]=='q') 108: delflg = 0; 109: else if (argv[1][1]=='p') { 110: flgp++; 111: delflg = 0; 112: } else if (argv[1][1]=='f') { 113: if (argc>=3) { 114: strcpy(mailfile, argv[2]); 115: argv++; 116: argc--; 117: } 118: } else if (argv[1][1]=='r') { 119: forward = 1; 120: } else { 121: fprintf(stderr, "mail: unknown option %c\n", argv[1][1]); 122: done(); 123: } 124: } else 125: break; 126: } 127: malf = fopen(mailfile, "r"); 128: if (malf == NULL) { 129: fprintf(stdout, "No mail.\n"); 130: return; 131: } 132: lock(mailfile); 133: copymt(malf, tmpf); 134: fclose(malf); 135: fclose(tmpf); 136: unlock(); 137: tmpf = fopen(lettmp, "r"); 138: 139: changed = 0; 140: print = 1; 141: for (i = 0; i < nlet; ) { 142: j = forward ? i : nlet - i - 1; 143: if(setjmp(sjbuf)) { 144: print=0; 145: } else { 146: if (print) 147: copylet(j, stdout, ORDINARY); 148: print = 1; 149: } 150: if (flgp) { 151: i++; 152: continue; 153: } 154: setjmp(sjbuf); 155: fprintf(stdout, "? "); 156: fflush(stdout); 157: if (fgets(resp, LSIZE, stdin) == NULL) 158: break; 159: switch (resp[0]) { 160: 161: default: 162: fprintf(stderr, "usage\n"); 163: case '?': 164: print = 0; 165: fprintf(stderr, "q\tquit\n"); 166: fprintf(stderr, "x\texit without changing mail\n"); 167: fprintf(stderr, "p\tprint\n"); 168: fprintf(stderr, "s[file]\tsave (default mbox)\n"); 169: fprintf(stderr, "w[file]\tsame without header\n"); 170: fprintf(stderr, "-\tprint previous\n"); 171: fprintf(stderr, "d\tdelete\n"); 172: fprintf(stderr, "+\tnext (no delete)\n"); 173: fprintf(stderr, "m user\tmail to user\n"); 174: fprintf(stderr, "! cmd\texecute cmd\n"); 175: break; 176: 177: case '+': 178: case 'n': 179: case '\n': 180: i++; 181: break; 182: case 'x': 183: changed = 0; 184: case 'q': 185: goto donep; 186: case 'p': 187: break; 188: case '^': 189: case '-': 190: if (--i < 0) 191: i = 0; 192: break; 193: case 'y': 194: case 'w': 195: case 's': 196: flg = 0; 197: if (resp[1] != '\n' && resp[1] != ' ') { 198: printf("illegal\n"); 199: flg++; 200: print = 0; 201: continue; 202: } 203: if (resp[1] == '\n' || resp[1] == '\0') 204: cat(resp+1, "mbox", ""); 205: for (p = resp+1; (p = getarg(lfil, p)) != NULL; ) { 206: malf = fopen(lfil, "a"); 207: if (malf == NULL) { 208: fprintf(stdout, "mail: cannot append to %s\n", lfil); 209: flg++; 210: continue; 211: } 212: copylet(j, malf, resp[0]=='w'? ZAP: ORDINARY); 213: fclose(malf); 214: } 215: if (flg) 216: print = 0; 217: else { 218: let[j].change = 'd'; 219: changed++; 220: i++; 221: } 222: break; 223: case 'm': 224: flg = 0; 225: if (resp[1] == '\n' || resp[1] == '\0') { 226: i++; 227: continue; 228: } 229: if (resp[1] != ' ') { 230: printf("invalid command\n"); 231: flg++; 232: print = 0; 233: continue; 234: } 235: for (p = resp+1; (p = getarg(lfil, p)) != NULL; ) 236: if (!sendrmt(j, lfil)) /* couldn't send it */ 237: flg++; 238: if (flg) 239: print = 0; 240: else { 241: let[j].change = 'd'; 242: changed++; 243: i++; 244: } 245: break; 246: case '!': 247: system(resp+1); 248: printf("!\n"); 249: print = 0; 250: break; 251: case 'd': 252: let[j].change = 'd'; 253: changed++; 254: i++; 255: if (resp[1] == 'q') 256: goto donep; 257: break; 258: } 259: } 260: donep: 261: if (changed) 262: copyback(); 263: } 264: 265: copyback() /* copy temp or whatever back to /usr/spool/mail */ 266: { 267: register i, n, c; 268: int new = 0; 269: struct stat stbuf; 270: 271: signal(SIGINT, SIG_IGN); 272: signal(SIGHUP, SIG_IGN); 273: signal(SIGQUIT, SIG_IGN); 274: lock(mailfile); 275: stat(mailfile, &stbuf); 276: if (stbuf.st_size != let[nlet].adr) { /* new mail has arrived */ 277: malf = fopen(mailfile, "r"); 278: if (malf == NULL) { 279: fprintf(stdout, "mail: can't re-read %s\n", mailfile); 280: done(); 281: } 282: fseek(malf, let[nlet].adr, 0); 283: fclose(tmpf); 284: tmpf = fopen(lettmp, "a"); 285: fseek(tmpf, let[nlet].adr, 0); 286: while ((c = fgetc(malf)) != EOF) 287: fputc(c, tmpf); 288: fclose(malf); 289: fclose(tmpf); 290: tmpf = fopen(lettmp, "r"); 291: let[++nlet].adr = stbuf.st_size; 292: new = 1; 293: } 294: malf = fopen(mailfile, "w"); 295: if (malf == NULL) { 296: fprintf(stderr, "mail: can't rewrite %s\n", lfil); 297: done(); 298: } 299: n = 0; 300: for (i = 0; i < nlet; i++) 301: if (let[i].change != 'd') { 302: copylet(i, malf, ORDINARY); 303: n++; 304: } 305: fclose(malf); 306: if (new) 307: fprintf(stdout, "new mail arrived\n"); 308: unlock(); 309: } 310: 311: copymt(f1, f2) /* copy mail (f1) to temp (f2) */ 312: FILE *f1, *f2; 313: { 314: long nextadr; 315: 316: nlet = nextadr = 0; 317: let[0].adr = 0; 318: while (fgets(line, LSIZE, f1) != NULL) { 319: if (isfrom(line)) 320: let[nlet++].adr = nextadr; 321: nextadr += strlen(line); 322: fputs(line, f2); 323: } 324: let[nlet].adr = nextadr; /* last plus 1 */ 325: } 326: 327: copylet(n, f, type) FILE *f; 328: { int ch; 329: long k; 330: fseek(tmpf, let[n].adr, 0); 331: k = let[n+1].adr - let[n].adr; 332: while(k-- > 1L && (ch=fgetc(tmpf))!='\n') 333: if(type!=ZAP) fputc(ch,f); 334: if(type==REMOTE) { 335: gethostname(hostname, sizeof (hostname)); 336: fprintf(f, " remote from %s\n", hostname); 337: } 338: else if (type==FORWARD) 339: fprintf(f, forwmsg); 340: else if(type==ORDINARY) 341: fputc(ch,f); 342: while(k-->1L) 343: fputc(ch=fgetc(tmpf), f); 344: if(type!=ZAP || ch!= '\n') 345: fputc(fgetc(tmpf), f); 346: } 347: 348: isfrom(lp) 349: register char *lp; 350: { 351: register char *p; 352: 353: for (p = from; *p; ) 354: if (*lp++ != *p++) 355: return(0); 356: return(1); 357: } 358: 359: sendmail(argc, argv) 360: char **argv; 361: { 362: 363: time(&iop); 364: fprintf(tmpf, "%s%s %s", from, my_name, ctime(&iop)); 365: iop = ftell(tmpf); 366: flgf = 1; 367: while (fgets(line, LSIZE, stdin) != NULL) { 368: if (line[0] == '.' && line[1] == '\n') 369: break; 370: if (isfrom(line)) 371: fputs(">", tmpf); 372: fputs(line, tmpf); 373: flgf = 0; 374: } 375: fputs("\n", tmpf); 376: nlet = 1; 377: let[0].adr = 0; 378: let[1].adr = ftell(tmpf); 379: fclose(tmpf); 380: if (flgf) 381: return; 382: tmpf = fopen(lettmp, "r"); 383: if (tmpf == NULL) { 384: fprintf(stderr, "mail: cannot reopen %s for reading\n", lettmp); 385: return; 386: } 387: while (--argc > 0) 388: if (!send(0, *++argv)) /* couldn't send to him */ 389: error++; 390: if (error && safefile(dead)) { 391: setuid(getuid()); 392: malf = fopen(dead, "w"); 393: if (malf == NULL) { 394: fprintf(stdout, "mail: cannot open %s\n", dead); 395: fclose(tmpf); 396: return; 397: } 398: copylet(0, malf, ZAP); 399: fclose(malf); 400: fprintf(stdout, "Mail saved in %s\n", dead); 401: } 402: fclose(tmpf); 403: } 404: 405: sendrmt(n, name) 406: char *name; 407: { 408: FILE *rmf, *popen(); 409: register char *p; 410: char rsys[64], cmd[64]; 411: register local, pid; 412: int ret, sts; 413: 414: local = 0; 415: if (*name=='!') 416: name++; 417: for(p=rsys; *name!='!'; *p++ = *name++) 418: if (*name=='\0') { 419: local++; 420: break; 421: } 422: *p = '\0'; 423: if ((!local && *name=='\0') || (local && *rsys=='\0')) { 424: fprintf(stdout, "null name\n"); 425: return(0); 426: } 427: if ((pid = fork()) == -1) { 428: fprintf(stderr, "mail: can't create proc for remote\n"); 429: return(0); 430: } 431: if (pid) { 432: while ((ret = wait(&sts)) != pid) { 433: if (ret == -1) 434: return(0); 435: } 436: return(!sts); 437: } 438: setuid(getuid()); 439: if (local) 440: sprintf(cmd, "mail %s", rsys); 441: else { 442: if (index(name+1, '!')) 443: sprintf(cmd, "uux - %s!rmail \\(%s\\)", rsys, name+1); 444: else 445: sprintf(cmd, "uux - %s!rmail %s", rsys, name+1); 446: } 447: if ((rmf=popen(cmd, "w")) == NULL) 448: exit(1); 449: copylet(n, rmf, local? FORWARD : REMOTE); 450: exit(pclose(rmf) != 0); 451: } 452: 453: send(n, name) /* send letter n to name */ 454: int n; 455: char *name; 456: { 457: char file[50]; 458: register char *p; 459: register mask; 460: struct passwd *pw, *getpwnam(); 461: struct stat statb; 462: #ifdef MSGS 463: FILE *pip; 464: char line[256]; 465: int count; 466: #endif 467: 468: for (p = name; *p != '!' && *p != '^' && *p != '\0'; p++) 469: ; 470: if (*p == '!' || *p == '^') 471: return(sendrmt(n, name)); 472: #ifdef MSGS 473: /* 474: * modification to call Berkeley 'msgs' program when the 'recipient' 475: * is "msgs". Assumption is that 'n' is 0, no REMOTE, FORWARD, etc. 476: * pipe the letter to "msgs -s" -- pag 11/10/79 477: */ 478: if(!strcmp(name,"msgs")) /* this letter is really a mesg */ 479: { 480: pip = popen("/usr/ucb/msgs -s", "w"); 481: if (pip == NULL) 482: return(0); 483: fseek(tmpf,0L,0); 484: count = 0; 485: while(fgets(line,256,tmpf) != NULL) 486: { 487: count++; 488: if(count == 2) 489: { 490: fprintf(pip,"To: msgs\n"); 491: /* 492: * check 2nd line for a Berkeley 'Mail'-added "To:" line.... 493: * we don't want to add 2 "To:"'s 494: */ 495: if(!strncmp(line,"To: msgs",8)) 496: continue; 497: } 498: fputs(line,pip); 499: } 500: pclose(pip); 501: return(1); 502: } 503: #endif 504: if ((pw = getpwnam(name)) == NULL) { 505: fprintf(stdout, "mail: can't send to %s\n", name); 506: return(0); 507: } 508: cat(file, maildir, name); 509: if (stat(file, &statb) >= 0 && (statb.st_mode & S_IFMT) == S_IFDIR) { 510: strcat(file, "/"); 511: strcat(file, name); 512: } 513: mask = umask(MAILMODE); 514: if (!safefile(file)) 515: return(0); 516: lock(file); 517: malf = fopen(file, "a"); 518: umask(mask); 519: if (malf == NULL) { 520: unlock(); 521: fprintf(stdout, "mail: cannot append to %s\n", file); 522: return(0); 523: } 524: chown(file, pw->pw_uid, pw->pw_gid); 525: copylet(n, malf, ORDINARY); 526: fclose(malf); 527: unlock(); 528: return(1); 529: } 530: 531: delete(i) 532: { 533: setsig(i, delete); 534: fprintf(stderr, "\n"); 535: if(delflg) 536: longjmp(sjbuf, 1); 537: done(); 538: } 539: 540: done() 541: { 542: if(!lockerror) 543: unlock(); 544: unlink(lettmp); 545: exit(error+lockerror); 546: } 547: 548: lock(file) 549: char *file; 550: { 551: struct stat stbuf; 552: 553: if (locked || flgf) 554: return; 555: if (stat(file, &stbuf)<0) 556: return; 557: if (stbuf.st_mode&01) { /* user x bit is the lock */ 558: if (stbuf.st_ctime+60 >= time((long *)0)) { 559: fprintf(stderr, "%s busy; try again in a minute\n", file); 560: lockerror++; 561: done(); 562: } 563: } 564: locked = stbuf.st_mode & ~01; 565: curlock = file; 566: chmod(file, stbuf.st_mode|01); 567: } 568: 569: unlock() 570: { 571: if (locked) 572: chmod(curlock, locked); 573: locked = 0; 574: } 575: 576: cat(to, from1, from2) 577: char *to, *from1, *from2; 578: { 579: int i, j; 580: 581: j = 0; 582: for (i=0; from1[i]; i++) 583: to[j++] = from1[i]; 584: for (i=0; from2[i]; i++) 585: to[j++] = from2[i]; 586: to[j] = 0; 587: } 588: 589: char *getarg(s, p) /* copy p... into s, update p */ 590: register char *s, *p; 591: { 592: while (*p == ' ' || *p == '\t') 593: p++; 594: if (*p == '\n' || *p == '\0') 595: return(NULL); 596: while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0') 597: *s++ = *p++; 598: *s = '\0'; 599: return(p); 600: } 601: 602: safefile(f) 603: char *f; 604: { 605: struct stat statb; 606: 607: #ifdef UCB_SYMLINKS 608: if (lstat(f, &statb) < 0) 609: #else 610: if (stat(f, &statb) < 0) 611: #endif 612: return(1); 613: if (statb.st_nlink != 1 || (statb.st_mode & S_IFMT) == S_IFLNK) { 614: fprintf(stderr, "mail: %s has more than one link or is a symbolic link\n", f); 615: return(0); 616: } 617: return(1); 618: }