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