00001
00002
00003
00004
00005
00006 #include <sys/types.h>
00007 #include <sys/stat.h>
00008 #include <errno.h>
00009 #undef EOF
00010 #include <signal.h>
00011 #include <pwd.h>
00012 #include <time.h>
00013 #include <setjmp.h>
00014 #include <string.h>
00015 #include <stdlib.h>
00016 #include <fcntl.h>
00017 #include <unistd.h>
00018 #include <sys/wait.h>
00019 #include <stdio.h>
00020
00021 #ifdef DEBUG
00022 #define D(Q) (Q)
00023 #else
00024 #define D(Q)
00025 #endif
00026
00027 #define SHELL "/bin/sh"
00028
00029 #define DROPNAME "/usr/spool/mail/%s"
00030 #define LOCKNAME "/usr/spool/mail/%s.lock"
00031 #define LOCKWAIT 5
00032 #define LOCKTRIES 4
00033
00034 #define MBOX "mbox"
00035
00036 #define HELPFILE "/usr/lib/mail.help"
00037 #define PROMPT "? "
00038 #define PATHLEN 80
00039 #define MAXRCPT 100
00040 #define LINELEN 512
00041
00042
00043 #define MAILERARGS
00044
00045 #define UNREAD 1
00046 #define DELETED 2
00047 #define READ 3
00048
00049 struct letter {
00050 struct letter *prev, *next;
00051 int status;
00052 off_t location;
00053 };
00054
00055 struct letter *firstlet, *lastlet;
00056
00057 int usemailer = 1;
00058 int printmode = 0;
00059 int quitmode = 0;
00060 int reversemode = 0;
00061 int usedrop = 1;
00062 int verbose = 0;
00063 int needupdate = 0;
00064 int msgstatus = 0;
00065 int distlist = 0;
00066 char mailbox[PATHLEN];
00067 char tempname[PATHLEN] = "/tmp/mailXXXXXX";
00068 char *subject = NULL;
00069 FILE *boxfp = NULL;
00070 jmp_buf printjump;
00071 unsigned oldmask;
00072
00073 extern int optind;
00074 extern char *optarg;
00075
00076 _PROTOTYPE(int main, (int argc, char **argv));
00077 _PROTOTYPE(int deliver, (int count, char *vec []));
00078 _PROTOTYPE(FILE *makerewindable, (void));
00079 _PROTOTYPE(int copy, (FILE *fromfp, FILE *tofp));
00080 _PROTOTYPE(void readbox, (void));
00081 _PROTOTYPE(void printall, (void));
00082 _PROTOTYPE(void interact, (void));
00083 _PROTOTYPE(void onint, (int dummy));
00084 _PROTOTYPE(void savelet, (struct letter *let, char *savefile));
00085 _PROTOTYPE(void updatebox, (void));
00086 _PROTOTYPE(void printlet, (struct letter *let, FILE *tofp));
00087 _PROTOTYPE(void doshell, (char *command));
00088 _PROTOTYPE(void usage, (void));
00089 _PROTOTYPE(char *basename, (char *name));
00090 _PROTOTYPE(char *whoami, (void));
00091 _PROTOTYPE(void dohelp, (void));
00092 _PROTOTYPE(int filesize, (char *name));
00093
00094 int main(argc, argv)
00095 int argc;
00096 char *argv[];
00097 {
00098 int c;
00099
00100 if ('l' == (basename(argv[0]))[0])
00101 usemailer = 0;
00102
00103 (void) mktemp(tempname);
00104
00105 oldmask = umask(022);
00106
00107 while (EOF != (c = getopt(argc, argv, "epqrf:tdvs:"))) switch (c) {
00108 case 'e': ++msgstatus; break;
00109
00110 case 't': ++distlist; break;
00111
00112 case 'p': ++printmode; break;
00113
00114 case 'q': ++quitmode; break;
00115
00116 case 'r': ++reversemode; break;
00117
00118 case 'f':
00119 setuid(getuid());
00120 usedrop = 0;
00121 strncpy(mailbox, optarg, (size_t)(PATHLEN - 1));
00122 break;
00123
00124 case 'd': usemailer = 0; break;
00125
00126 case 'v': ++verbose; break;
00127
00128 case 's': subject = optarg; break;
00129
00130 default:
00131 usage();
00132 exit(1);
00133 }
00134
00135 if (optind < argc) {
00136 if (deliver(argc - optind, argv + optind) < 0)
00137 exit(1);
00138 else
00139 exit(0);
00140 }
00141 if (usedrop) sprintf(mailbox, DROPNAME, whoami());
00142
00143 D(printf("mailbox=%s\n", mailbox));
00144
00145 if (msgstatus) {
00146 if (filesize(mailbox))
00147 exit(0);
00148 else
00149 exit(1);
00150 }
00151
00152 readbox();
00153
00154 if (printmode)
00155 printall();
00156 else
00157 interact();
00158
00159 if (needupdate) updatebox();
00160
00161 return(0);
00162 }
00163
00164 int deliver(count, vec)
00165 int count;
00166 char *vec[];
00167 {
00168 int i, j;
00169 int errs = 0;
00170 int dropfd;
00171 int created = 0;
00172 FILE *mailfp;
00173 struct stat stb;
00174 #ifdef __STDC__
00175 void (*sigint)(int), (*sighup)(int), (*sigquit)(int);
00176 #else
00177 void (*sigint) (), (*sighup) (), (*sigquit) ();
00178 #endif
00179 time_t now;
00180 char sender[32];
00181 char lockname[PATHLEN];
00182 int locktries;
00183 struct passwd *pw;
00184 int to_console;
00185
00186 if (count > MAXRCPT) {
00187 fprintf(stderr, "mail: too many recipients\n");
00188 return -1;
00189 }
00190 #ifdef MAILER
00191 if (usemailer) {
00192 char *argvec[MAXRCPT + 3];
00193 char **argp;
00194
00195 setuid(getuid());
00196
00197 argp = argvec;
00198 *argp++ = "send-mail";
00199 if (verbose) *argp++ = "-v";
00200
00201 for (i = 0; i < count; ++i) *argp++ = vec[i];
00202
00203 *argp = NULL;
00204 execv(MAILER, argvec);
00205 fprintf(stderr, "mail: couldn't exec %s\n", MAILER);
00206 return -1;
00207 }
00208 #endif
00209
00210 if (NULL == (pw = getpwuid(getuid()))) {
00211 fprintf(stderr, "mail: unknown sender\n");
00212 return -1;
00213 }
00214 strcpy(sender, pw->pw_name);
00215
00216
00217 if (isatty(0) || (count > 1 && lseek(0, 0L, 0) == (off_t) -1)) {
00218 mailfp = makerewindable();
00219 } else
00220 mailfp = stdin;
00221
00222
00223 sigint = signal(SIGINT, SIG_IGN);
00224 sighup = signal(SIGHUP, SIG_IGN);
00225 sigquit = signal(SIGQUIT, SIG_IGN);
00226
00227 for (i = 0; i < count; ++i) {
00228 if (count > 1) rewind(mailfp);
00229
00230 D(printf("deliver to %s\n", vec[i]));
00231
00232 if (NULL == (pw = getpwnam(vec[i]))) {
00233 fprintf(stderr, "mail: user %s not known\n", vec[i]);
00234 ++errs;
00235 continue;
00236 }
00237 sprintf(mailbox, DROPNAME, pw->pw_name);
00238 sprintf(lockname, LOCKNAME, pw->pw_name);
00239
00240 D(printf("maildrop='%s', lock='%s'\n", mailbox, lockname));
00241
00242
00243
00244
00245
00246 locktries = created = to_console = 0;
00247 trylock:
00248 if (link(mailbox, lockname) != 0) {
00249 if (ENOENT == errno) {
00250 dropfd = creat(mailbox, 0600);
00251 if (dropfd < 0 && errno == ENOENT) {
00252
00253 boxfp = fopen("/dev/console", "w");
00254 if (boxfp != NULL) {
00255 to_console = 1;
00256 goto nobox;
00257 }
00258 }
00259 if (dropfd < 0) {
00260 fprintf(stderr, "mail: couln't create a maildrop for user %s\n",
00261 vec[i]);
00262 ++errs;
00263 continue;
00264 }
00265 ++created;
00266 goto trylock;
00267 } else {
00268
00269 if (++locktries >= LOCKTRIES) {
00270 fprintf(stderr, "mail: couldn't lock maildrop for user %s\n",
00271 vec[i]);
00272 ++errs;
00273 continue;
00274 }
00275 sleep(LOCKWAIT);
00276 goto trylock;
00277 }
00278 }
00279 if (created) {
00280 (void) chown(mailbox, pw->pw_uid, pw->pw_gid);
00281 boxfp = fdopen(dropfd, "a");
00282 } else
00283 boxfp = fopen(mailbox, "a");
00284
00285 if (NULL == boxfp || stat(mailbox, &stb) < 0) {
00286 fprintf(stderr, "mail: serious maildrop problems for %s\n", vec[i]);
00287 unlink(lockname);
00288 ++errs;
00289 continue;
00290 }
00291 if (stb.st_uid != pw->pw_uid || (stb.st_mode & S_IFMT) != S_IFREG) {
00292 fprintf(stderr, "mail: mailbox for user %s is illegal\n", vec[i]);
00293 unlink(lockname);
00294 ++errs;
00295 continue;
00296 }
00297 nobox:
00298 if (to_console) {
00299 fprintf(boxfp,
00300 "-------------\n| Mail from %s to %s\n-------------\n",
00301 sender, vec[i]);
00302 } else {
00303 (void) time(&now);
00304 fprintf(boxfp, "From %s %24.24s\n", sender, ctime(&now));
00305 }
00306
00307
00308 fprintf(boxfp, "To: %s\n", vec[i]);
00309
00310 if (distlist) {
00311 fprintf(boxfp, "Dist: ");
00312 for (j = 0; j < count; ++j)
00313 if (getpwnam(vec[j]) != NULL && j != i)
00314 fprintf(boxfp, "%s ", vec[j]) ;
00315 fprintf(boxfp, "\n");
00316 }
00317
00318
00319 if (subject != NULL) fprintf(boxfp, "Subject: %s\n", subject);
00320
00321 fprintf(boxfp, "\n");
00322
00323 if ((copy(mailfp, boxfp) < 0) || (fclose(boxfp) != 0)) {
00324 fprintf(stderr, "mail: error delivering to user %s", vec[i]);
00325 perror(" ");
00326 ++errs;
00327 }
00328 unlink(lockname);
00329 }
00330
00331 fclose(mailfp);
00332
00333
00334 signal(SIGINT, sigint);
00335 signal(SIGHUP, sighup);
00336 signal(SIGQUIT, sigquit);
00337
00338 return(0 == errs) ? 0 : -1;
00339 }
00340
00341
00342
00343
00344
00345
00346 FILE *makerewindable()
00347 {
00348 FILE *tempfp;
00349 int c;
00350 int state;
00351
00352 if (NULL == (tempfp = fopen(tempname, "w"))) {
00353 fprintf(stderr, "mail: can't create temporary file\n");
00354 return NULL;
00355 }
00356
00357
00358
00359
00360 state = '\n';
00361 while (EOF != (c = getc(stdin))) switch (state) {
00362 case '\n':
00363 if ('.' == c)
00364 state = '.';
00365 else {
00366 if ('\n' != c) state = '\0';
00367 putc(c, tempfp);
00368 }
00369 break;
00370 case '.':
00371 if ('\n' == c) goto done;
00372 state = '\0';
00373 putc('.', tempfp);
00374 putc(c, tempfp);
00375 break;
00376 default:
00377 state = ('\n' == c) ? '\n' : '\0';
00378 putc(c, tempfp);
00379 }
00380 done:
00381 if (ferror(tempfp) || fclose(tempfp)) {
00382 fprintf(stderr, "mail: couldn't copy letter to temporary file\n");
00383 return NULL;
00384 }
00385 tempfp = freopen(tempname, "r", stdin);
00386 unlink(tempname);
00387 return tempfp;
00388 }
00389
00390 int copy(fromfp, tofp)
00391 FILE *fromfp, *tofp;
00392 {
00393 int c;
00394 int state;
00395 int blankline = 0;
00396 static char postmark[] = "From ";
00397 char *p, *q;
00398
00399
00400
00401
00402
00403 state = '\n';
00404 p = postmark;
00405 while (EOF != (c = getc(fromfp))) {
00406 switch (state) {
00407 case '\n':
00408 if ('.' == c)
00409 state = '.';
00410 else if (*p == c) {
00411 ++p;
00412 state = 'P';
00413 } else {
00414 if ('\n' == c)
00415 blankline = 1;
00416 else {
00417 state = '\0';
00418 blankline = 0;
00419 }
00420 putc(c, tofp);
00421 }
00422 break;
00423 case '.':
00424 if ('\n' == c) goto done;
00425 state = '\0';
00426 putc('.', tofp);
00427 putc(c, tofp);
00428 break;
00429 case 'P':
00430 if (*p == c) {
00431 if (*++p == '\0') {
00432 p = postmark;
00433 putc('>', tofp);
00434 fputs(postmark, tofp);
00435 state = '\0';
00436 break;
00437 }
00438 break;
00439 }
00440 state = ('\n' == c) ? '\n' : '\0';
00441 for (q = postmark; q < p; ++q) putc(*q, tofp);
00442 putc(c, tofp);
00443 blankline = 0;
00444 p = postmark;
00445 break;
00446 default:
00447 state = ('\n' == c) ? '\n' : '\0';
00448 putc(c, tofp);
00449 }
00450 }
00451 if ('\n' != state) putc('\n', tofp);
00452 done:
00453 if (!blankline) putc('\n', tofp);
00454 if (ferror(tofp)) return -1;
00455 return 0;
00456 }
00457
00458 void readbox()
00459 {
00460 char linebuf[512];
00461 struct letter *let;
00462 off_t current;
00463
00464 firstlet = lastlet = NULL;
00465
00466 if (access(mailbox, 4) < 0 || NULL == (boxfp = fopen(mailbox, "r"))) {
00467 if (usedrop && ENOENT == errno) return;
00468 fprintf(stderr, "can't access mailbox ");
00469 perror(mailbox);
00470 exit(1);
00471 }
00472 current = 0L;
00473 while (1) {
00474 if (NULL == fgets(linebuf, sizeof linebuf, boxfp)) break;
00475
00476 if (!strncmp(linebuf, "From ", (size_t)5)) {
00477 if (NULL == (let = (struct letter *) malloc(sizeof(struct letter)))) {
00478 fprintf(stderr, "Out of memory.\n");
00479 exit(1);
00480 }
00481 if (NULL == lastlet) {
00482 firstlet = let;
00483 let->prev = NULL;
00484 } else {
00485 let->prev = lastlet;
00486 lastlet->next = let;
00487 }
00488 lastlet = let;
00489 let->next = NULL;
00490
00491 let->status = UNREAD;
00492 let->location = current;
00493 D(printf("letter at %ld\n", current));
00494 }
00495 current += strlen(linebuf);
00496 }
00497 }
00498
00499 void printall()
00500 {
00501 struct letter *let;
00502
00503 let = reversemode ? firstlet : lastlet;
00504
00505 if (NULL == let) {
00506 printf("No mail.\n");
00507 return;
00508 }
00509 while (NULL != let) {
00510 printlet(let, stdout);
00511 let = reversemode ? let->next : let->prev;
00512 }
00513 }
00514
00515 void interact()
00516 {
00517 char linebuf[512];
00518 struct letter *let, *next;
00519 int interrupted = 0;
00520 int needprint = 1;
00521 char *savefile;
00522
00523 if (NULL == firstlet) {
00524 printf("No mail.\n");
00525 return;
00526 }
00527 let = reversemode ? firstlet : lastlet;
00528
00529 while (1) {
00530 next = reversemode ? let->next : let->prev;
00531 if (NULL == next) next = let;
00532
00533 if (!quitmode) {
00534 interrupted = setjmp(printjump);
00535 signal(SIGINT, onint);
00536 }
00537 if (!interrupted && needprint) {
00538 if (DELETED != let->status) let->status = READ;
00539 printlet(let, stdout);
00540 }
00541 if (interrupted) putchar('\n');
00542 needprint = 0;
00543 fputs(PROMPT, stdout);
00544 fflush(stdout);
00545
00546 if (fgets(linebuf, sizeof linebuf, stdin) == NULL) break;
00547
00548 if (!quitmode) signal(SIGINT, SIG_IGN);
00549
00550 switch (linebuf[0]) {
00551 case '\n':
00552 let = next;
00553 needprint = 1;
00554 continue;
00555 case 'd':
00556 let->status = DELETED;
00557 if (next != let)
00558 needprint = 1;
00559 needupdate = 1;
00560 let = next;
00561 continue;
00562 case 'p':
00563 needprint = 1;
00564 continue;
00565 case '-':
00566 next = reversemode ? let->prev : let->next;
00567 if (NULL == next) next = let;
00568 let = next;
00569 needprint = 1;
00570 continue;
00571 case 's':
00572 for (savefile = strtok(linebuf + 1, " \t\n");
00573 savefile != NULL;
00574 savefile = strtok((char *) NULL, " \t\n")) {
00575 savelet(let, savefile);
00576 }
00577 continue;
00578 case '!':
00579 doshell(linebuf + 1);
00580 continue;
00581 case '*':
00582 dohelp();
00583 continue;
00584 case 'q':
00585 return;
00586 case 'x':
00587 exit(0);
00588 default:
00589 fprintf(stderr, "Illegal command\n");
00590 continue;
00591 }
00592 }
00593 }
00594
00595 void onint(dummy)
00596 int dummy;
00597 {
00598 longjmp(printjump, 1);
00599 }
00600
00601 void savelet(let, savefile)
00602 struct letter *let;
00603 char *savefile;
00604 {
00605 int waitstat, pid;
00606 FILE *savefp;
00607
00608 if ((pid = fork()) < 0) {
00609 perror("mail: couldn't fork");
00610 return;
00611 } else if (pid != 0) {
00612 wait(&waitstat);
00613 return;
00614 }
00615
00616
00617 setgid(getgid());
00618 setuid(getuid());
00619 if ((savefp = fopen(savefile, "a")) == NULL) {
00620 perror(savefile);
00621 exit(0);
00622 }
00623 printlet(let, savefp);
00624 if ((ferror(savefp) != 0) | (fclose(savefp) != 0)) {
00625 fprintf(stderr, "savefile write error:");
00626 perror(savefile);
00627 }
00628 exit(0);
00629 }
00630
00631 void updatebox()
00632 {
00633 FILE *tempfp;
00634 char lockname[PATHLEN];
00635 int locktries = 0;
00636 struct letter *let;
00637 int c;
00638
00639 sprintf(lockname, LOCKNAME, whoami());
00640
00641 if (NULL == (tempfp = fopen(tempname, "w"))) {
00642 perror("mail: can't create temporary file");
00643 return;
00644 }
00645 for (let = firstlet; let != NULL; let = let->next) {
00646 if (let->status != DELETED) {
00647 printlet(let, tempfp);
00648 D(printf("printed letter at %ld\n", let->location));
00649 }
00650 }
00651
00652 if (ferror(tempfp) || NULL == (tempfp = freopen(tempname, "r", tempfp))) {
00653 perror("mail: temporary file write error");
00654 unlink(tempname);
00655 return;
00656 }
00657
00658
00659 signal(SIGINT, SIG_IGN);
00660 signal(SIGHUP, SIG_IGN);
00661 signal(SIGQUIT, SIG_IGN);
00662
00663 if (usedrop) while (link(mailbox, lockname) != 0) {
00664 if (++locktries >= LOCKTRIES) {
00665 fprintf(stderr, "mail: couldn't lock maildrop for update\n");
00666 return;
00667 }
00668 sleep(LOCKWAIT);
00669 }
00670
00671 if (NULL == (boxfp = freopen(mailbox, "w", boxfp))) {
00672 perror("mail: couldn't reopen maildrop");
00673 fprintf(stderr, "mail may have been lost; look in %s\n", tempname);
00674 if (usedrop) unlink(lockname);
00675 return;
00676 }
00677 unlink(tempname);
00678
00679 while ((c = getc(tempfp)) != EOF) putc(c, boxfp);
00680
00681 fclose(boxfp);
00682
00683 if (usedrop) unlink(lockname);
00684 }
00685
00686 void printlet(let, tofp)
00687 struct letter *let;
00688 FILE *tofp;
00689 {
00690 off_t current, limit;
00691 int c;
00692
00693 fseek(boxfp, (current = let->location), 0);
00694 limit = (NULL != let->next) ? let->next->location : -1;
00695
00696 while (current != limit && (c = getc(boxfp)) != EOF) {
00697 putc(c, tofp);
00698 ++current;
00699 }
00700 }
00701
00702 void doshell(command)
00703 char *command;
00704 {
00705 int waitstat, pid;
00706 char *shell;
00707
00708 if (NULL == (shell = getenv("SHELL"))) shell = SHELL;
00709
00710 if ((pid = fork()) < 0) {
00711 perror("mail: couldn't fork");
00712 return;
00713 } else if (pid != 0) {
00714 wait(&waitstat);
00715 return;
00716 }
00717
00718
00719 setgid(getgid());
00720 setuid(getuid());
00721 umask(oldmask);
00722
00723 execl(shell, shell, "-c", command, (char *) NULL);
00724 fprintf(stderr, "can't exec shell\n");
00725 exit(127);
00726 }
00727
00728 void usage()
00729 {
00730 fprintf(stderr, "usage: mail [-epqr] [-f file]\n");
00731 fprintf(stderr, " mail [-dtv] [-s subject] user [...]\n");
00732 }
00733
00734 char *basename(name)
00735 char *name;
00736 {
00737 char *p;
00738
00739 if (NULL == (p = rindex(name, '/')))
00740 return name;
00741 else
00742 return p + 1;
00743 }
00744
00745 char *whoami()
00746 {
00747 struct passwd *pw;
00748
00749 if (NULL != (pw = getpwuid(getuid())))
00750 return pw->pw_name;
00751 else
00752 return "nobody";
00753 }
00754
00755 void dohelp()
00756 {
00757 FILE *fp;
00758 char buffer[80];
00759
00760 if ( (fp = fopen(HELPFILE, "r")) == NULL)
00761 fprintf(stdout, "can't open helpfile %s\n", HELPFILE);
00762 else
00763 while (fgets(buffer, 80, fp))
00764 fputs(buffer, stdout);
00765 }
00766
00767 int filesize(name)
00768 char *name ;
00769 {
00770 struct stat buf;
00771
00772 if (stat(name, &buf) == -1)
00773 buf.st_size = 0L;
00774
00775 return (buf.st_size ? 1 : 0);
00776 }