1 #ifndef lint 2 static char sccsid[] = "@(#)mail.local.c 4.25 (Berkeley) 05/01/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 } 740