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