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