777 lines
17 KiB
C
777 lines
17 KiB
C
|
/* mail - send/receive mail Author: Peter S. Housel */
|
||
|
/* Version 0.2 of September 1990: added -e, -t, * options - cwr */
|
||
|
|
||
|
/* 2003-07-18: added -s option - ASW */
|
||
|
|
||
|
#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 */
|
||
|
char *subject = NULL;
|
||
|
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:tdvs:"))) 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;
|
||
|
|
||
|
case 's': subject = optarg; 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));
|
||
|
}
|
||
|
|
||
|
/* Add the To: header line */
|
||
|
fprintf(boxfp, "To: %s\n", vec[i]);
|
||
|
|
||
|
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");
|
||
|
}
|
||
|
|
||
|
/* Add the Subject: header line */
|
||
|
if (subject != NULL) fprintf(boxfp, "Subject: %s\n", subject);
|
||
|
|
||
|
fprintf(boxfp, "\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 ";
|
||
|
char *p, *q;
|
||
|
|
||
|
/* Here we copy until we reach the end of the letter (end of file or
|
||
|
* a line containing only a '.'). Postmarks (lines beginning with
|
||
|
* "From ") are copied with a ">" prepended. Here we also complicate
|
||
|
* things by not setting a line limit. */
|
||
|
state = '\n';
|
||
|
p = postmark;
|
||
|
while (EOF != (c = getc(fromfp))) {
|
||
|
switch (state) {
|
||
|
case '\n':
|
||
|
if ('.' == c) /* '.' at BOL */
|
||
|
state = '.';
|
||
|
else if (*p == c) { /* start of postmark */
|
||
|
++p;
|
||
|
state = 'P';
|
||
|
} else { /* anything else */
|
||
|
if ('\n' == c)
|
||
|
blankline = 1;
|
||
|
else {
|
||
|
state = '\0';
|
||
|
blankline = 0;
|
||
|
}
|
||
|
putc(c, tofp);
|
||
|
}
|
||
|
break;
|
||
|
case '.':
|
||
|
if ('\n' == c) goto done;
|
||
|
state = '\0';
|
||
|
putc('.', tofp);
|
||
|
putc(c, tofp);
|
||
|
break;
|
||
|
case 'P':
|
||
|
if (*p == c) {
|
||
|
if (*++p == '\0') { /* successfully reached end */
|
||
|
p = postmark;
|
||
|
putc('>', tofp);
|
||
|
fputs(postmark, tofp);
|
||
|
state = '\0';
|
||
|
break;
|
||
|
}
|
||
|
break; /* not there yet */
|
||
|
}
|
||
|
state = ('\n' == c) ? '\n' : '\0';
|
||
|
for (q = postmark; q < p; ++q) putc(*q, tofp);
|
||
|
putc(c, tofp);
|
||
|
blankline = 0;
|
||
|
p = postmark;
|
||
|
break;
|
||
|
default:
|
||
|
state = ('\n' == c) ? '\n' : '\0';
|
||
|
putc(c, tofp);
|
||
|
}
|
||
|
}
|
||
|
if ('\n' != state) putc('\n', tofp);
|
||
|
done:
|
||
|
if (!blankline) putc('\n', tofp);
|
||
|
if (ferror(tofp)) return -1;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void readbox()
|
||
|
{
|
||
|
char linebuf[512];
|
||
|
struct letter *let;
|
||
|
off_t current;
|
||
|
|
||
|
firstlet = lastlet = NULL;
|
||
|
|
||
|
if (access(mailbox, 4) < 0 || NULL == (boxfp = fopen(mailbox, "r"))) {
|
||
|
if (usedrop && ENOENT == errno) return;
|
||
|
fprintf(stderr, "can't access mailbox ");
|
||
|
perror(mailbox);
|
||
|
exit(1);
|
||
|
}
|
||
|
current = 0L;
|
||
|
while (1) {
|
||
|
if (NULL == fgets(linebuf, sizeof linebuf, boxfp)) break;
|
||
|
|
||
|
if (!strncmp(linebuf, "From ", (size_t)5)) {
|
||
|
if (NULL == (let = (struct letter *) malloc(sizeof(struct letter)))) {
|
||
|
fprintf(stderr, "Out of memory.\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
if (NULL == lastlet) {
|
||
|
firstlet = let;
|
||
|
let->prev = NULL;
|
||
|
} else {
|
||
|
let->prev = lastlet;
|
||
|
lastlet->next = let;
|
||
|
}
|
||
|
lastlet = let;
|
||
|
let->next = NULL;
|
||
|
|
||
|
let->status = UNREAD;
|
||
|
let->location = current;
|
||
|
D(printf("letter at %ld\n", current));
|
||
|
}
|
||
|
current += strlen(linebuf);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void printall()
|
||
|
{
|
||
|
struct letter *let;
|
||
|
|
||
|
let = reversemode ? firstlet : lastlet;
|
||
|
|
||
|
if (NULL == let) {
|
||
|
printf("No mail.\n");
|
||
|
return;
|
||
|
}
|
||
|
while (NULL != let) {
|
||
|
printlet(let, stdout);
|
||
|
let = reversemode ? let->next : let->prev;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void interact()
|
||
|
{
|
||
|
char linebuf[512]; /* user input line */
|
||
|
struct letter *let, *next; /* current and next letter */
|
||
|
int interrupted = 0; /* SIGINT hit during letter print */
|
||
|
int needprint = 1; /* need to print this letter */
|
||
|
char *savefile; /* filename to save into */
|
||
|
|
||
|
if (NULL == firstlet) {
|
||
|
printf("No mail.\n");
|
||
|
return;
|
||
|
}
|
||
|
let = reversemode ? firstlet : lastlet;
|
||
|
|
||
|
while (1) {
|
||
|
next = reversemode ? let->next : let->prev;
|
||
|
if (NULL == next) next = let;
|
||
|
|
||
|
if (!quitmode) {
|
||
|
interrupted = setjmp(printjump);
|
||
|
signal(SIGINT, onint);
|
||
|
}
|
||
|
if (!interrupted && needprint) {
|
||
|
if (DELETED != let->status) let->status = READ;
|
||
|
printlet(let, stdout);
|
||
|
}
|
||
|
if (interrupted) putchar('\n');
|
||
|
needprint = 0;
|
||
|
fputs(PROMPT, stdout);
|
||
|
fflush(stdout);
|
||
|
|
||
|
if (fgets(linebuf, sizeof linebuf, stdin) == NULL) break;
|
||
|
|
||
|
if (!quitmode) signal(SIGINT, SIG_IGN);
|
||
|
|
||
|
switch (linebuf[0]) {
|
||
|
case '\n':
|
||
|
let = next;
|
||
|
needprint = 1;
|
||
|
continue;
|
||
|
case 'd':
|
||
|
let->status = DELETED;
|
||
|
if (next != let)/* look into this */
|
||
|
needprint = 1;
|
||
|
needupdate = 1;
|
||
|
let = next;
|
||
|
continue;
|
||
|
case 'p':
|
||
|
needprint = 1;
|
||
|
continue;
|
||
|
case '-':
|
||
|
next = reversemode ? let->prev : let->next;
|
||
|
if (NULL == next) next = let;
|
||
|
let = next;
|
||
|
needprint = 1;
|
||
|
continue;
|
||
|
case 's':
|
||
|
for (savefile = strtok(linebuf + 1, " \t\n");
|
||
|
savefile != NULL;
|
||
|
savefile = strtok((char *) NULL, " \t\n")) {
|
||
|
savelet(let, savefile);
|
||
|
}
|
||
|
continue;
|
||
|
case '!':
|
||
|
doshell(linebuf + 1);
|
||
|
continue;
|
||
|
case '*':
|
||
|
dohelp();
|
||
|
continue;
|
||
|
case 'q':
|
||
|
return;
|
||
|
case 'x':
|
||
|
exit(0);
|
||
|
default:
|
||
|
fprintf(stderr, "Illegal command\n");
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void onint(dummy)
|
||
|
int dummy; /* to satisfy ANSI compilers */
|
||
|
{
|
||
|
longjmp(printjump, 1);
|
||
|
}
|
||
|
|
||
|
void savelet(let, savefile)
|
||
|
struct letter *let;
|
||
|
char *savefile;
|
||
|
{
|
||
|
int waitstat, pid;
|
||
|
FILE *savefp;
|
||
|
|
||
|
if ((pid = fork()) < 0) {
|
||
|
perror("mail: couldn't fork");
|
||
|
return;
|
||
|
} else if (pid != 0) { /* parent */
|
||
|
wait(&waitstat);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Child */
|
||
|
setgid(getgid());
|
||
|
setuid(getuid());
|
||
|
if ((savefp = fopen(savefile, "a")) == NULL) {
|
||
|
perror(savefile);
|
||
|
exit(0);
|
||
|
}
|
||
|
printlet(let, savefp);
|
||
|
if ((ferror(savefp) != 0) | (fclose(savefp) != 0)) {
|
||
|
fprintf(stderr, "savefile write error:");
|
||
|
perror(savefile);
|
||
|
}
|
||
|
exit(0);
|
||
|
}
|
||
|
|
||
|
void updatebox()
|
||
|
{
|
||
|
FILE *tempfp; /* fp for tempfile */
|
||
|
char lockname[PATHLEN]; /* maildrop lock */
|
||
|
int locktries = 0; /* tries when box is locked */
|
||
|
struct letter *let; /* current letter */
|
||
|
int c;
|
||
|
|
||
|
sprintf(lockname, LOCKNAME, whoami());
|
||
|
|
||
|
if (NULL == (tempfp = fopen(tempname, "w"))) {
|
||
|
perror("mail: can't create temporary file");
|
||
|
return;
|
||
|
}
|
||
|
for (let = firstlet; let != NULL; let = let->next) {
|
||
|
if (let->status != DELETED) {
|
||
|
printlet(let, tempfp);
|
||
|
D(printf("printed letter at %ld\n", let->location));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (ferror(tempfp) || NULL == (tempfp = freopen(tempname, "r", tempfp))) {
|
||
|
perror("mail: temporary file write error");
|
||
|
unlink(tempname);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Shut off signals during the update */
|
||
|
signal(SIGINT, SIG_IGN);
|
||
|
signal(SIGHUP, SIG_IGN);
|
||
|
signal(SIGQUIT, SIG_IGN);
|
||
|
|
||
|
if (usedrop) while (link(mailbox, lockname) != 0) {
|
||
|
if (++locktries >= LOCKTRIES) {
|
||
|
fprintf(stderr, "mail: couldn't lock maildrop for update\n");
|
||
|
return;
|
||
|
}
|
||
|
sleep(LOCKWAIT);
|
||
|
}
|
||
|
|
||
|
if (NULL == (boxfp = freopen(mailbox, "w", boxfp))) {
|
||
|
perror("mail: couldn't reopen maildrop");
|
||
|
fprintf(stderr, "mail may have been lost; look in %s\n", tempname);
|
||
|
if (usedrop) unlink(lockname);
|
||
|
return;
|
||
|
}
|
||
|
unlink(tempname);
|
||
|
|
||
|
while ((c = getc(tempfp)) != EOF) putc(c, boxfp);
|
||
|
|
||
|
fclose(boxfp);
|
||
|
|
||
|
if (usedrop) unlink(lockname);
|
||
|
}
|
||
|
|
||
|
void printlet(let, tofp)
|
||
|
struct letter *let;
|
||
|
FILE *tofp;
|
||
|
{
|
||
|
off_t current, limit;
|
||
|
int c;
|
||
|
|
||
|
fseek(boxfp, (current = let->location), 0);
|
||
|
limit = (NULL != let->next) ? let->next->location : -1;
|
||
|
|
||
|
while (current != limit && (c = getc(boxfp)) != EOF) {
|
||
|
putc(c, tofp);
|
||
|
++current;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void doshell(command)
|
||
|
char *command;
|
||
|
{
|
||
|
int waitstat, pid;
|
||
|
char *shell;
|
||
|
|
||
|
if (NULL == (shell = getenv("SHELL"))) shell = SHELL;
|
||
|
|
||
|
if ((pid = fork()) < 0) {
|
||
|
perror("mail: couldn't fork");
|
||
|
return;
|
||
|
} else if (pid != 0) { /* parent */
|
||
|
wait(&waitstat);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Child */
|
||
|
setgid(getgid());
|
||
|
setuid(getuid());
|
||
|
umask(oldmask);
|
||
|
|
||
|
execl(shell, shell, "-c", command, (char *) NULL);
|
||
|
fprintf(stderr, "can't exec shell\n");
|
||
|
exit(127);
|
||
|
}
|
||
|
|
||
|
void usage()
|
||
|
{
|
||
|
fprintf(stderr, "usage: mail [-epqr] [-f file]\n");
|
||
|
fprintf(stderr, " mail [-dtv] [-s subject] user [...]\n");
|
||
|
}
|
||
|
|
||
|
char *basename(name)
|
||
|
char *name;
|
||
|
{
|
||
|
char *p;
|
||
|
|
||
|
if (NULL == (p = rindex(name, '/')))
|
||
|
return name;
|
||
|
else
|
||
|
return p + 1;
|
||
|
}
|
||
|
|
||
|
char *whoami()
|
||
|
{
|
||
|
struct passwd *pw;
|
||
|
|
||
|
if (NULL != (pw = getpwuid(getuid())))
|
||
|
return pw->pw_name;
|
||
|
else
|
||
|
return "nobody";
|
||
|
}
|
||
|
|
||
|
void dohelp()
|
||
|
{
|
||
|
FILE *fp;
|
||
|
char buffer[80];
|
||
|
|
||
|
if ( (fp = fopen(HELPFILE, "r")) == NULL)
|
||
|
fprintf(stdout, "can't open helpfile %s\n", HELPFILE);
|
||
|
else
|
||
|
while (fgets(buffer, 80, fp))
|
||
|
fputs(buffer, stdout);
|
||
|
}
|
||
|
|
||
|
int filesize(name)
|
||
|
char *name ;
|
||
|
{
|
||
|
struct stat buf;
|
||
|
|
||
|
if (stat(name, &buf) == -1)
|
||
|
buf.st_size = 0L;
|
||
|
|
||
|
return (buf.st_size ? 1 : 0);
|
||
|
}
|