📄 mail.c
字号:
/* mail - send/receive mail Author: Peter S. Housel *//* Version 0.2 of September 1990: added -e, -t, * options - cwr */#include <sys/types.h>#include <sys/stat.h>#include <errno.h>#undef EOF /* temporary hack */#include <signal.h>#include <pwd.h>#include <time.h>#include <setjmp.h>#include <string.h>#include <stdlib.h>#include <fcntl.h>#include <unistd.h>#include <sys/wait.h>#include <stdio.h>#ifdef DEBUG#define D(Q) (Q)#else#define D(Q)#endif#define SHELL "/bin/sh"#define DROPNAME "/usr/spool/mail/%s"#define LOCKNAME "/usr/spool/mail/%s.lock"#define LOCKWAIT 5 /* seconds to wait after collision */#define LOCKTRIES 4 /* maximum number of collisions */#define MBOX "mbox"#define HELPFILE "/usr/lib/mail.help"#define PROMPT "? "#define PATHLEN 80#define MAXRCPT 100 /* maximum number of recipients */#define LINELEN 512/* #define MAILER "/usr/bin/smail" */ /* smart mailer */#define MAILERARGS /* (unused) */#define UNREAD 1 /* 'not read yet' status */#define DELETED 2 /* 'deleted' status */#define READ 3 /* 'has been read' status */struct letter { struct letter *prev, *next; /* linked letter list */ int status; /* letter status */ off_t location; /* location within mailbox file */};struct letter *firstlet, *lastlet;int usemailer = 1; /* use MAILER to deliver (if any) */int printmode = 0; /* print-and-exit mode */int quitmode = 0; /* take interrupts */int reversemode = 0; /* print mailbox in reverse order */int usedrop = 1; /* read the maildrop (no -f given) */int verbose = 0; /* pass "-v" flag on to mailer */int needupdate = 0; /* need to update mailbox */int msgstatus = 0; /* return the mail status */int distlist = 0; /* include distribution list */char mailbox[PATHLEN]; /* user's mailbox/maildrop */char tempname[PATHLEN] = "/tmp/mailXXXXXX"; /* temporary file */FILE *boxfp = NULL; /* mailbox file */jmp_buf printjump; /* for quitting out of letters */unsigned oldmask; /* saved umask() */extern int optind;extern char *optarg;_PROTOTYPE(int main, (int argc, char **argv));_PROTOTYPE(int deliver, (int count, char *vec []));_PROTOTYPE(FILE *makerewindable, (void));_PROTOTYPE(int copy, (FILE *fromfp, FILE *tofp));_PROTOTYPE(void readbox, (void));_PROTOTYPE(void printall, (void));_PROTOTYPE(void interact, (void));_PROTOTYPE(void onint, (int dummy));_PROTOTYPE(void savelet, (struct letter *let, char *savefile));_PROTOTYPE(void updatebox, (void));_PROTOTYPE(void printlet, (struct letter *let, FILE *tofp));_PROTOTYPE(void doshell, (char *command));_PROTOTYPE(void usage, (void));_PROTOTYPE(char *basename, (char *name));_PROTOTYPE(char *whoami, (void));_PROTOTYPE(void dohelp, (void));_PROTOTYPE(int filesize, (char *name));int main(argc, argv)int argc;char *argv[];{ int c; if ('l' == (basename(argv[0]))[0]) /* 'lmail' link? */ usemailer = 0; /* yes, let's deliver it */ (void) mktemp(tempname); /* name the temp file */ oldmask = umask(022); /* change umask for security */ while (EOF != (c = getopt(argc, argv, "epqrf:tdv"))) switch (c) { case 'e': ++msgstatus; break; case 't': ++distlist; break; case 'p': ++printmode; break; case 'q': ++quitmode; break; case 'r': ++reversemode; break; case 'f': setuid(getuid()); /* won't need to lock */ usedrop = 0; strncpy(mailbox, optarg, (size_t)(PATHLEN - 1)); break; case 'd': usemailer = 0; break; case 'v': ++verbose; break; default: usage(); exit(1); } if (optind < argc) { if (deliver(argc - optind, argv + optind) < 0) exit(1); else exit(0); } if (usedrop) sprintf(mailbox, DROPNAME, whoami()); D(printf("mailbox=%s\n", mailbox)); if (msgstatus) { if (filesize(mailbox)) exit(0); else exit(1); } readbox(); if (printmode) printall(); else interact(); if (needupdate) updatebox(); return(0);}int deliver(count, vec)int count;char *vec[];{ int i, j; int errs = 0; /* count of errors */ int dropfd; /* file descriptor for user's drop */ int created = 0; /* true if we created the maildrop */ FILE *mailfp; /* fp for mail */ struct stat stb; /* for checking drop modes, owners */#ifdef __STDC__ void (*sigint)(int), (*sighup)(int), (*sigquit)(int);/* saving signal state */#else void (*sigint) (), (*sighup) (), (*sigquit) (); /* saving signal state */#endif time_t now; /* for datestamping the postmark */ char sender[32]; /* sender's login name */ char lockname[PATHLEN]; /* maildrop lock */ int locktries; /* tries when box is locked */ struct passwd *pw; /* sender and recipent */ int to_console; /* deliver to console if everything fails */ if (count > MAXRCPT) { fprintf(stderr, "mail: too many recipients\n"); return -1; }#ifdef MAILER if (usemailer) { char *argvec[MAXRCPT + 3]; char **argp; setuid(getuid()); argp = argvec; *argp++ = "send-mail"; if (verbose) *argp++ = "-v"; for (i = 0; i < count; ++i) *argp++ = vec[i]; *argp = NULL; execv(MAILER, argvec); fprintf(stderr, "mail: couldn't exec %s\n", MAILER); return -1; }#endif /* MAILER */ if (NULL == (pw = getpwuid(getuid()))) { fprintf(stderr, "mail: unknown sender\n"); return -1; } strcpy(sender, pw->pw_name); /* If we need to rewind stdin and it isn't rewindable, make a copy */ if (isatty(0) || (count > 1 && lseek(0, 0L, 0) == (off_t) -1)) { mailfp = makerewindable(); } else mailfp = stdin; /* Shut off signals during the delivery */ sigint = signal(SIGINT, SIG_IGN); sighup = signal(SIGHUP, SIG_IGN); sigquit = signal(SIGQUIT, SIG_IGN); for (i = 0; i < count; ++i) { if (count > 1) rewind(mailfp); D(printf("deliver to %s\n", vec[i])); if (NULL == (pw = getpwnam(vec[i]))) { fprintf(stderr, "mail: user %s not known\n", vec[i]); ++errs; continue; } sprintf(mailbox, DROPNAME, pw->pw_name); sprintf(lockname, LOCKNAME, pw->pw_name); D(printf("maildrop='%s', lock='%s'\n", mailbox, lockname)); /* Lock the maildrop while we're messing with it. Races are * possible (though not very likely) when we have to create * the maildrop, but not otherwise. If the box is already * locked, wait awhile and try again. */ locktries = created = to_console = 0;trylock: if (link(mailbox, lockname) != 0) { if (ENOENT == errno) { /* user doesn't have a drop yet */ dropfd = creat(mailbox, 0600); if (dropfd < 0 && errno == ENOENT) { /* Probably missing spool dir; to console. */ boxfp = fopen("/dev/console", "w"); if (boxfp != NULL) { to_console = 1; goto nobox; } } if (dropfd < 0) { fprintf(stderr, "mail: couln't create a maildrop for user %s\n", vec[i]); ++errs; continue; } ++created; goto trylock; } else { /* somebody else has it locked, it seems - * wait */ if (++locktries >= LOCKTRIES) { fprintf(stderr, "mail: couldn't lock maildrop for user %s\n", vec[i]); ++errs; continue; } sleep(LOCKWAIT); goto trylock; } } if (created) { (void) chown(mailbox, pw->pw_uid, pw->pw_gid); boxfp = fdopen(dropfd, "a"); } else boxfp = fopen(mailbox, "a"); if (NULL == boxfp || stat(mailbox, &stb) < 0) { fprintf(stderr, "mail: serious maildrop problems for %s\n", vec[i]); unlink(lockname); ++errs; continue; } if (stb.st_uid != pw->pw_uid || (stb.st_mode & S_IFMT) != S_IFREG) { fprintf(stderr, "mail: mailbox for user %s is illegal\n", vec[i]); unlink(lockname); ++errs; continue; }nobox: if (to_console) { fprintf(boxfp, "-------------\n| Mail from %s to %s\n-------------\n", sender, vec[i]); } else { (void) time(&now); fprintf(boxfp, "From %s %24.24s\n", sender, ctime(&now)); } if (distlist) { fprintf(boxfp, "Dist: "); for (j = 0; j < count; ++j) if (getpwnam(vec[j]) != NULL && j != i) fprintf(boxfp, "%s ", vec[j]) ; fprintf(boxfp, "\n\n"); } if ((copy(mailfp, boxfp) < 0) || (fclose(boxfp) != 0)) { fprintf(stderr, "mail: error delivering to user %s", vec[i]); perror(" "); ++errs; } unlink(lockname); } fclose(mailfp); /* Put signals back the way they were */ signal(SIGINT, sigint); signal(SIGHUP, sighup); signal(SIGQUIT, sigquit); return(0 == errs) ? 0 : -1;}/* 'stdin' isn't rewindable. Make a temp file that is. * Note that if one wanted to catch SIGINT and write a '~/dead.letter' * for interactive mails, this might be the place to do it (though the * case where a MAILER is being used would also need to be handled). */FILE *makerewindable(){ FILE *tempfp; /* temp file used for copy */ int c; /* character being copied */ int state; /* ".\n" detection state */ if (NULL == (tempfp = fopen(tempname, "w"))) { fprintf(stderr, "mail: can't create temporary file\n"); return NULL; } /* Here we copy until we reach the end of the letter (end of file or * a line containing only a '.'), painstakingly avoiding setting a * line length limit. */ state = '\n'; while (EOF != (c = getc(stdin))) switch (state) { case '\n': if ('.' == c) state = '.'; else { if ('\n' != c) state = '\0'; putc(c, tempfp); } break; case '.': if ('\n' == c) goto done; state = '\0'; putc('.', tempfp); putc(c, tempfp); break; default: state = ('\n' == c) ? '\n' : '\0'; putc(c, tempfp); }done: if (ferror(tempfp) || fclose(tempfp)) { fprintf(stderr, "mail: couldn't copy letter to temporary file\n"); return NULL; } tempfp = freopen(tempname, "r", stdin); unlink(tempname); /* unlink name; file lingers on in limbo */ return tempfp;}int copy(fromfp, tofp)FILE *fromfp, *tofp;{ int c; /* character being copied */ int state; /* ".\n" and postmark detection state */ int blankline = 0; /* was most recent line completely blank? */ static char postmark[] = "From ";
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -