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