1*433d6423SLionel Sambuc /* mail - send/receive mail Author: Peter S. Housel */ 2*433d6423SLionel Sambuc /* Version 0.2 of September 1990: added -e, -t, * options - cwr */ 3*433d6423SLionel Sambuc 4*433d6423SLionel Sambuc /* 2003-07-18: added -s option - ASW */ 5*433d6423SLionel Sambuc 6*433d6423SLionel Sambuc #include <sys/types.h> 7*433d6423SLionel Sambuc #include <sys/stat.h> 8*433d6423SLionel Sambuc #include <errno.h> 9*433d6423SLionel Sambuc #undef EOF /* temporary hack */ 10*433d6423SLionel Sambuc #include <signal.h> 11*433d6423SLionel Sambuc #include <pwd.h> 12*433d6423SLionel Sambuc #include <time.h> 13*433d6423SLionel Sambuc #include <setjmp.h> 14*433d6423SLionel Sambuc #include <string.h> 15*433d6423SLionel Sambuc #include <stdlib.h> 16*433d6423SLionel Sambuc #include <fcntl.h> 17*433d6423SLionel Sambuc #include <unistd.h> 18*433d6423SLionel Sambuc #include <sys/wait.h> 19*433d6423SLionel Sambuc #include <stdio.h> 20*433d6423SLionel Sambuc 21*433d6423SLionel Sambuc #ifdef DEBUG 22*433d6423SLionel Sambuc #define D(Q) (Q) 23*433d6423SLionel Sambuc #else 24*433d6423SLionel Sambuc #define D(Q) 25*433d6423SLionel Sambuc #endif 26*433d6423SLionel Sambuc 27*433d6423SLionel Sambuc #define SHELL "/bin/sh" 28*433d6423SLionel Sambuc 29*433d6423SLionel Sambuc #define DROPNAME "/var/mail/%s" 30*433d6423SLionel Sambuc #define LOCKNAME "/var/mail/%s.lock" 31*433d6423SLionel Sambuc #define LOCKWAIT 5 /* seconds to wait after collision */ 32*433d6423SLionel Sambuc #define LOCKTRIES 4 /* maximum number of collisions */ 33*433d6423SLionel Sambuc 34*433d6423SLionel Sambuc #define MBOX "mbox" 35*433d6423SLionel Sambuc 36*433d6423SLionel Sambuc #define HELPFILE "/usr/lib/mail.help" 37*433d6423SLionel Sambuc #define PROMPT "? " 38*433d6423SLionel Sambuc #define PATHLEN 80 39*433d6423SLionel Sambuc #define MAXRCPT 100 /* maximum number of recipients */ 40*433d6423SLionel Sambuc #define LINELEN 512 41*433d6423SLionel Sambuc 42*433d6423SLionel Sambuc /* #define MAILER "/usr/bin/smail" */ /* smart mailer */ 43*433d6423SLionel Sambuc #define MAILERARGS /* (unused) */ 44*433d6423SLionel Sambuc 45*433d6423SLionel Sambuc #define UNREAD 1 /* 'not read yet' status */ 46*433d6423SLionel Sambuc #define DELETED 2 /* 'deleted' status */ 47*433d6423SLionel Sambuc #define READ 3 /* 'has been read' status */ 48*433d6423SLionel Sambuc 49*433d6423SLionel Sambuc struct letter { 50*433d6423SLionel Sambuc struct letter *prev, *next; /* linked letter list */ 51*433d6423SLionel Sambuc int status; /* letter status */ 52*433d6423SLionel Sambuc off_t location; /* location within mailbox file */ 53*433d6423SLionel Sambuc }; 54*433d6423SLionel Sambuc 55*433d6423SLionel Sambuc struct letter *firstlet, *lastlet; 56*433d6423SLionel Sambuc 57*433d6423SLionel Sambuc int usemailer = 1; /* use MAILER to deliver (if any) */ 58*433d6423SLionel Sambuc int printmode = 0; /* print-and-exit mode */ 59*433d6423SLionel Sambuc int quitmode = 0; /* take interrupts */ 60*433d6423SLionel Sambuc int reversemode = 0; /* print mailbox in reverse order */ 61*433d6423SLionel Sambuc int usedrop = 1; /* read the maildrop (no -f given) */ 62*433d6423SLionel Sambuc int verbose = 0; /* pass "-v" flag on to mailer */ 63*433d6423SLionel Sambuc int needupdate = 0; /* need to update mailbox */ 64*433d6423SLionel Sambuc int msgstatus = 0; /* return the mail status */ 65*433d6423SLionel Sambuc int distlist = 0; /* include distribution list */ 66*433d6423SLionel Sambuc char mailbox[PATHLEN]; /* user's mailbox/maildrop */ 67*433d6423SLionel Sambuc char tempname[PATHLEN] = "/tmp/mailXXXXXX"; /* temporary file */ 68*433d6423SLionel Sambuc char *subject = NULL; 69*433d6423SLionel Sambuc FILE *boxfp = NULL; /* mailbox file */ 70*433d6423SLionel Sambuc jmp_buf printjump; /* for quitting out of letters */ 71*433d6423SLionel Sambuc unsigned oldmask; /* saved umask() */ 72*433d6423SLionel Sambuc 73*433d6423SLionel Sambuc extern int optind; 74*433d6423SLionel Sambuc extern char *optarg; 75*433d6423SLionel Sambuc 76*433d6423SLionel Sambuc int main(int argc, char **argv); 77*433d6423SLionel Sambuc int deliver(int count, char *vec []); 78*433d6423SLionel Sambuc FILE *makerewindable(void); 79*433d6423SLionel Sambuc int copy(FILE *fromfp, FILE *tofp); 80*433d6423SLionel Sambuc void readbox(void); 81*433d6423SLionel Sambuc void printall(void); 82*433d6423SLionel Sambuc void interact(void); 83*433d6423SLionel Sambuc void onint(int dummy); 84*433d6423SLionel Sambuc void savelet(struct letter *let, char *savefile); 85*433d6423SLionel Sambuc void updatebox(void); 86*433d6423SLionel Sambuc void printlet(struct letter *let, FILE *tofp); 87*433d6423SLionel Sambuc void doshell(char *command); 88*433d6423SLionel Sambuc void usage(void); 89*433d6423SLionel Sambuc char *basename(char *name); 90*433d6423SLionel Sambuc char *whoami(void); 91*433d6423SLionel Sambuc void dohelp(void); 92*433d6423SLionel Sambuc int filesize(char *name); 93*433d6423SLionel Sambuc 94*433d6423SLionel Sambuc int main(argc, argv) 95*433d6423SLionel Sambuc int argc; 96*433d6423SLionel Sambuc char *argv[]; 97*433d6423SLionel Sambuc { 98*433d6423SLionel Sambuc int c; 99*433d6423SLionel Sambuc 100*433d6423SLionel Sambuc if ('l' == (basename(argv[0]))[0]) /* 'lmail' link? */ 101*433d6423SLionel Sambuc usemailer = 0; /* yes, let's deliver it */ 102*433d6423SLionel Sambuc 103*433d6423SLionel Sambuc (void) mktemp(tempname); /* name the temp file */ 104*433d6423SLionel Sambuc 105*433d6423SLionel Sambuc oldmask = umask(022); /* change umask for security */ 106*433d6423SLionel Sambuc 107*433d6423SLionel Sambuc while (EOF != (c = getopt(argc, argv, "epqrf:tdvs:"))) switch (c) { 108*433d6423SLionel Sambuc case 'e': ++msgstatus; break; 109*433d6423SLionel Sambuc 110*433d6423SLionel Sambuc case 't': ++distlist; break; 111*433d6423SLionel Sambuc 112*433d6423SLionel Sambuc case 'p': ++printmode; break; 113*433d6423SLionel Sambuc 114*433d6423SLionel Sambuc case 'q': ++quitmode; break; 115*433d6423SLionel Sambuc 116*433d6423SLionel Sambuc case 'r': ++reversemode; break; 117*433d6423SLionel Sambuc 118*433d6423SLionel Sambuc case 'f': 119*433d6423SLionel Sambuc setuid(getuid()); /* won't need to lock */ 120*433d6423SLionel Sambuc usedrop = 0; 121*433d6423SLionel Sambuc strncpy(mailbox, optarg, (size_t)(PATHLEN - 1)); 122*433d6423SLionel Sambuc break; 123*433d6423SLionel Sambuc 124*433d6423SLionel Sambuc case 'd': usemailer = 0; break; 125*433d6423SLionel Sambuc 126*433d6423SLionel Sambuc case 'v': ++verbose; break; 127*433d6423SLionel Sambuc 128*433d6423SLionel Sambuc case 's': subject = optarg; break; 129*433d6423SLionel Sambuc 130*433d6423SLionel Sambuc default: 131*433d6423SLionel Sambuc usage(); 132*433d6423SLionel Sambuc exit(1); 133*433d6423SLionel Sambuc } 134*433d6423SLionel Sambuc 135*433d6423SLionel Sambuc if (optind < argc) { 136*433d6423SLionel Sambuc if (deliver(argc - optind, argv + optind) < 0) 137*433d6423SLionel Sambuc exit(1); 138*433d6423SLionel Sambuc else 139*433d6423SLionel Sambuc exit(0); 140*433d6423SLionel Sambuc } 141*433d6423SLionel Sambuc if (usedrop) sprintf(mailbox, DROPNAME, whoami()); 142*433d6423SLionel Sambuc 143*433d6423SLionel Sambuc D(printf("mailbox=%s\n", mailbox)); 144*433d6423SLionel Sambuc 145*433d6423SLionel Sambuc if (msgstatus) { 146*433d6423SLionel Sambuc if (filesize(mailbox)) 147*433d6423SLionel Sambuc exit(0); 148*433d6423SLionel Sambuc else 149*433d6423SLionel Sambuc exit(1); 150*433d6423SLionel Sambuc } 151*433d6423SLionel Sambuc 152*433d6423SLionel Sambuc readbox(); 153*433d6423SLionel Sambuc 154*433d6423SLionel Sambuc if (printmode) 155*433d6423SLionel Sambuc printall(); 156*433d6423SLionel Sambuc else 157*433d6423SLionel Sambuc interact(); 158*433d6423SLionel Sambuc 159*433d6423SLionel Sambuc if (needupdate) updatebox(); 160*433d6423SLionel Sambuc 161*433d6423SLionel Sambuc return(0); 162*433d6423SLionel Sambuc } 163*433d6423SLionel Sambuc 164*433d6423SLionel Sambuc int deliver(count, vec) 165*433d6423SLionel Sambuc int count; 166*433d6423SLionel Sambuc char *vec[]; 167*433d6423SLionel Sambuc { 168*433d6423SLionel Sambuc int i, j; 169*433d6423SLionel Sambuc int errs = 0; /* count of errors */ 170*433d6423SLionel Sambuc int dropfd; /* file descriptor for user's drop */ 171*433d6423SLionel Sambuc int created = 0; /* true if we created the maildrop */ 172*433d6423SLionel Sambuc FILE *mailfp; /* fp for mail */ 173*433d6423SLionel Sambuc struct stat stb; /* for checking drop modes, owners */ 174*433d6423SLionel Sambuc #ifdef __STDC__ 175*433d6423SLionel Sambuc void (*sigint)(int), (*sighup)(int), (*sigquit)(int);/* saving signal state */ 176*433d6423SLionel Sambuc #else 177*433d6423SLionel Sambuc void (*sigint) (), (*sighup) (), (*sigquit) (); /* saving signal state */ 178*433d6423SLionel Sambuc #endif 179*433d6423SLionel Sambuc time_t now; /* for datestamping the postmark */ 180*433d6423SLionel Sambuc char sender[32]; /* sender's login name */ 181*433d6423SLionel Sambuc char lockname[PATHLEN]; /* maildrop lock */ 182*433d6423SLionel Sambuc int locktries; /* tries when box is locked */ 183*433d6423SLionel Sambuc struct passwd *pw; /* sender and recipent */ 184*433d6423SLionel Sambuc int to_console; /* deliver to console if everything fails */ 185*433d6423SLionel Sambuc 186*433d6423SLionel Sambuc if (count > MAXRCPT) { 187*433d6423SLionel Sambuc fprintf(stderr, "mail: too many recipients\n"); 188*433d6423SLionel Sambuc return -1; 189*433d6423SLionel Sambuc } 190*433d6423SLionel Sambuc #ifdef MAILER 191*433d6423SLionel Sambuc if (usemailer) { 192*433d6423SLionel Sambuc char *argvec[MAXRCPT + 3]; 193*433d6423SLionel Sambuc char **argp; 194*433d6423SLionel Sambuc 195*433d6423SLionel Sambuc setuid(getuid()); 196*433d6423SLionel Sambuc 197*433d6423SLionel Sambuc argp = argvec; 198*433d6423SLionel Sambuc *argp++ = "send-mail"; 199*433d6423SLionel Sambuc if (verbose) *argp++ = "-v"; 200*433d6423SLionel Sambuc 201*433d6423SLionel Sambuc for (i = 0; i < count; ++i) *argp++ = vec[i]; 202*433d6423SLionel Sambuc 203*433d6423SLionel Sambuc *argp = NULL; 204*433d6423SLionel Sambuc execv(MAILER, argvec); 205*433d6423SLionel Sambuc fprintf(stderr, "mail: couldn't exec %s\n", MAILER); 206*433d6423SLionel Sambuc return -1; 207*433d6423SLionel Sambuc } 208*433d6423SLionel Sambuc #endif /* MAILER */ 209*433d6423SLionel Sambuc 210*433d6423SLionel Sambuc if (NULL == (pw = getpwuid(getuid()))) { 211*433d6423SLionel Sambuc fprintf(stderr, "mail: unknown sender\n"); 212*433d6423SLionel Sambuc return -1; 213*433d6423SLionel Sambuc } 214*433d6423SLionel Sambuc strcpy(sender, pw->pw_name); 215*433d6423SLionel Sambuc 216*433d6423SLionel Sambuc /* If we need to rewind stdin and it isn't rewindable, make a copy */ 217*433d6423SLionel Sambuc if (isatty(0) || (count > 1 && lseek(0, 0L, 0) == (off_t) -1)) { 218*433d6423SLionel Sambuc mailfp = makerewindable(); 219*433d6423SLionel Sambuc } else 220*433d6423SLionel Sambuc mailfp = stdin; 221*433d6423SLionel Sambuc 222*433d6423SLionel Sambuc /* Shut off signals during the delivery */ 223*433d6423SLionel Sambuc sigint = signal(SIGINT, SIG_IGN); 224*433d6423SLionel Sambuc sighup = signal(SIGHUP, SIG_IGN); 225*433d6423SLionel Sambuc sigquit = signal(SIGQUIT, SIG_IGN); 226*433d6423SLionel Sambuc 227*433d6423SLionel Sambuc for (i = 0; i < count; ++i) { 228*433d6423SLionel Sambuc if (count > 1) rewind(mailfp); 229*433d6423SLionel Sambuc 230*433d6423SLionel Sambuc D(printf("deliver to %s\n", vec[i])); 231*433d6423SLionel Sambuc 232*433d6423SLionel Sambuc if (NULL == (pw = getpwnam(vec[i]))) { 233*433d6423SLionel Sambuc fprintf(stderr, "mail: user %s not known\n", vec[i]); 234*433d6423SLionel Sambuc ++errs; 235*433d6423SLionel Sambuc continue; 236*433d6423SLionel Sambuc } 237*433d6423SLionel Sambuc sprintf(mailbox, DROPNAME, pw->pw_name); 238*433d6423SLionel Sambuc sprintf(lockname, LOCKNAME, pw->pw_name); 239*433d6423SLionel Sambuc 240*433d6423SLionel Sambuc D(printf("maildrop='%s', lock='%s'\n", mailbox, lockname)); 241*433d6423SLionel Sambuc 242*433d6423SLionel Sambuc /* Lock the maildrop while we're messing with it. Races are 243*433d6423SLionel Sambuc * possible (though not very likely) when we have to create 244*433d6423SLionel Sambuc * the maildrop, but not otherwise. If the box is already 245*433d6423SLionel Sambuc * locked, wait awhile and try again. */ 246*433d6423SLionel Sambuc locktries = created = to_console = 0; 247*433d6423SLionel Sambuc trylock: 248*433d6423SLionel Sambuc if (link(mailbox, lockname) != 0) { 249*433d6423SLionel Sambuc if (ENOENT == errno) { /* user doesn't have a drop yet */ 250*433d6423SLionel Sambuc dropfd = creat(mailbox, 0600); 251*433d6423SLionel Sambuc if (dropfd < 0 && errno == ENOENT) { 252*433d6423SLionel Sambuc /* Probably missing spool dir; to console. */ 253*433d6423SLionel Sambuc boxfp = fopen("/dev/console", "w"); 254*433d6423SLionel Sambuc if (boxfp != NULL) { 255*433d6423SLionel Sambuc to_console = 1; 256*433d6423SLionel Sambuc goto nobox; 257*433d6423SLionel Sambuc } 258*433d6423SLionel Sambuc } 259*433d6423SLionel Sambuc if (dropfd < 0) { 260*433d6423SLionel Sambuc fprintf(stderr, "mail: couln't create a maildrop for user %s\n", 261*433d6423SLionel Sambuc vec[i]); 262*433d6423SLionel Sambuc ++errs; 263*433d6423SLionel Sambuc continue; 264*433d6423SLionel Sambuc } 265*433d6423SLionel Sambuc ++created; 266*433d6423SLionel Sambuc goto trylock; 267*433d6423SLionel Sambuc } else { /* somebody else has it locked, it seems - 268*433d6423SLionel Sambuc * wait */ 269*433d6423SLionel Sambuc if (++locktries >= LOCKTRIES) { 270*433d6423SLionel Sambuc fprintf(stderr, "mail: couldn't lock maildrop for user %s\n", 271*433d6423SLionel Sambuc vec[i]); 272*433d6423SLionel Sambuc ++errs; 273*433d6423SLionel Sambuc continue; 274*433d6423SLionel Sambuc } 275*433d6423SLionel Sambuc sleep(LOCKWAIT); 276*433d6423SLionel Sambuc goto trylock; 277*433d6423SLionel Sambuc } 278*433d6423SLionel Sambuc } 279*433d6423SLionel Sambuc if (created) { 280*433d6423SLionel Sambuc (void) chown(mailbox, pw->pw_uid, pw->pw_gid); 281*433d6423SLionel Sambuc boxfp = fdopen(dropfd, "a"); 282*433d6423SLionel Sambuc } else 283*433d6423SLionel Sambuc boxfp = fopen(mailbox, "a"); 284*433d6423SLionel Sambuc 285*433d6423SLionel Sambuc if (NULL == boxfp || stat(mailbox, &stb) < 0) { 286*433d6423SLionel Sambuc fprintf(stderr, "mail: serious maildrop problems for %s\n", vec[i]); 287*433d6423SLionel Sambuc unlink(lockname); 288*433d6423SLionel Sambuc ++errs; 289*433d6423SLionel Sambuc continue; 290*433d6423SLionel Sambuc } 291*433d6423SLionel Sambuc if (stb.st_uid != pw->pw_uid || (stb.st_mode & S_IFMT) != S_IFREG) { 292*433d6423SLionel Sambuc fprintf(stderr, "mail: mailbox for user %s is illegal\n", vec[i]); 293*433d6423SLionel Sambuc unlink(lockname); 294*433d6423SLionel Sambuc ++errs; 295*433d6423SLionel Sambuc continue; 296*433d6423SLionel Sambuc } 297*433d6423SLionel Sambuc nobox: 298*433d6423SLionel Sambuc if (to_console) { 299*433d6423SLionel Sambuc fprintf(boxfp, 300*433d6423SLionel Sambuc "-------------\n| Mail from %s to %s\n-------------\n", 301*433d6423SLionel Sambuc sender, vec[i]); 302*433d6423SLionel Sambuc } else { 303*433d6423SLionel Sambuc (void) time(&now); 304*433d6423SLionel Sambuc fprintf(boxfp, "From %s %24.24s\n", sender, ctime(&now)); 305*433d6423SLionel Sambuc } 306*433d6423SLionel Sambuc 307*433d6423SLionel Sambuc /* Add the To: header line */ 308*433d6423SLionel Sambuc fprintf(boxfp, "To: %s\n", vec[i]); 309*433d6423SLionel Sambuc 310*433d6423SLionel Sambuc if (distlist) { 311*433d6423SLionel Sambuc fprintf(boxfp, "Dist: "); 312*433d6423SLionel Sambuc for (j = 0; j < count; ++j) 313*433d6423SLionel Sambuc if (getpwnam(vec[j]) != NULL && j != i) 314*433d6423SLionel Sambuc fprintf(boxfp, "%s ", vec[j]) ; 315*433d6423SLionel Sambuc fprintf(boxfp, "\n"); 316*433d6423SLionel Sambuc } 317*433d6423SLionel Sambuc 318*433d6423SLionel Sambuc /* Add the Subject: header line */ 319*433d6423SLionel Sambuc if (subject != NULL) fprintf(boxfp, "Subject: %s\n", subject); 320*433d6423SLionel Sambuc 321*433d6423SLionel Sambuc fprintf(boxfp, "\n"); 322*433d6423SLionel Sambuc 323*433d6423SLionel Sambuc if ((copy(mailfp, boxfp) < 0) || (fclose(boxfp) != 0)) { 324*433d6423SLionel Sambuc fprintf(stderr, "mail: error delivering to user %s", vec[i]); 325*433d6423SLionel Sambuc perror(" "); 326*433d6423SLionel Sambuc ++errs; 327*433d6423SLionel Sambuc } 328*433d6423SLionel Sambuc unlink(lockname); 329*433d6423SLionel Sambuc } 330*433d6423SLionel Sambuc 331*433d6423SLionel Sambuc fclose(mailfp); 332*433d6423SLionel Sambuc 333*433d6423SLionel Sambuc /* Put signals back the way they were */ 334*433d6423SLionel Sambuc signal(SIGINT, sigint); 335*433d6423SLionel Sambuc signal(SIGHUP, sighup); 336*433d6423SLionel Sambuc signal(SIGQUIT, sigquit); 337*433d6423SLionel Sambuc 338*433d6423SLionel Sambuc return(0 == errs) ? 0 : -1; 339*433d6423SLionel Sambuc } 340*433d6423SLionel Sambuc 341*433d6423SLionel Sambuc /* 'stdin' isn't rewindable. Make a temp file that is. 342*433d6423SLionel Sambuc * Note that if one wanted to catch SIGINT and write a '~/dead.letter' 343*433d6423SLionel Sambuc * for interactive mails, this might be the place to do it (though the 344*433d6423SLionel Sambuc * case where a MAILER is being used would also need to be handled). 345*433d6423SLionel Sambuc */ 346*433d6423SLionel Sambuc FILE *makerewindable() 347*433d6423SLionel Sambuc { 348*433d6423SLionel Sambuc FILE *tempfp; /* temp file used for copy */ 349*433d6423SLionel Sambuc int c; /* character being copied */ 350*433d6423SLionel Sambuc int state; /* ".\n" detection state */ 351*433d6423SLionel Sambuc 352*433d6423SLionel Sambuc if (NULL == (tempfp = fopen(tempname, "w"))) { 353*433d6423SLionel Sambuc fprintf(stderr, "mail: can't create temporary file\n"); 354*433d6423SLionel Sambuc return NULL; 355*433d6423SLionel Sambuc } 356*433d6423SLionel Sambuc 357*433d6423SLionel Sambuc /* Here we copy until we reach the end of the letter (end of file or 358*433d6423SLionel Sambuc * a line containing only a '.'), painstakingly avoiding setting a 359*433d6423SLionel Sambuc * line length limit. */ 360*433d6423SLionel Sambuc state = '\n'; 361*433d6423SLionel Sambuc while (EOF != (c = getc(stdin))) switch (state) { 362*433d6423SLionel Sambuc case '\n': 363*433d6423SLionel Sambuc if ('.' == c) 364*433d6423SLionel Sambuc state = '.'; 365*433d6423SLionel Sambuc else { 366*433d6423SLionel Sambuc if ('\n' != c) state = '\0'; 367*433d6423SLionel Sambuc putc(c, tempfp); 368*433d6423SLionel Sambuc } 369*433d6423SLionel Sambuc break; 370*433d6423SLionel Sambuc case '.': 371*433d6423SLionel Sambuc if ('\n' == c) goto done; 372*433d6423SLionel Sambuc state = '\0'; 373*433d6423SLionel Sambuc putc('.', tempfp); 374*433d6423SLionel Sambuc putc(c, tempfp); 375*433d6423SLionel Sambuc break; 376*433d6423SLionel Sambuc default: 377*433d6423SLionel Sambuc state = ('\n' == c) ? '\n' : '\0'; 378*433d6423SLionel Sambuc putc(c, tempfp); 379*433d6423SLionel Sambuc } 380*433d6423SLionel Sambuc done: 381*433d6423SLionel Sambuc if (ferror(tempfp) || fclose(tempfp)) { 382*433d6423SLionel Sambuc fprintf(stderr, "mail: couldn't copy letter to temporary file\n"); 383*433d6423SLionel Sambuc return NULL; 384*433d6423SLionel Sambuc } 385*433d6423SLionel Sambuc tempfp = freopen(tempname, "r", stdin); 386*433d6423SLionel Sambuc unlink(tempname); /* unlink name; file lingers on in limbo */ 387*433d6423SLionel Sambuc return tempfp; 388*433d6423SLionel Sambuc } 389*433d6423SLionel Sambuc 390*433d6423SLionel Sambuc int copy(fromfp, tofp) 391*433d6423SLionel Sambuc FILE *fromfp, *tofp; 392*433d6423SLionel Sambuc { 393*433d6423SLionel Sambuc int c; /* character being copied */ 394*433d6423SLionel Sambuc int state; /* ".\n" and postmark detection state */ 395*433d6423SLionel Sambuc int blankline = 0; /* was most recent line completely blank? */ 396*433d6423SLionel Sambuc static char postmark[] = "From "; 397*433d6423SLionel Sambuc char *p, *q; 398*433d6423SLionel Sambuc 399*433d6423SLionel Sambuc /* Here we copy until we reach the end of the letter (end of file or 400*433d6423SLionel Sambuc * a line containing only a '.'). Postmarks (lines beginning with 401*433d6423SLionel Sambuc * "From ") are copied with a ">" prepended. Here we also complicate 402*433d6423SLionel Sambuc * things by not setting a line limit. */ 403*433d6423SLionel Sambuc state = '\n'; 404*433d6423SLionel Sambuc p = postmark; 405*433d6423SLionel Sambuc while (EOF != (c = getc(fromfp))) { 406*433d6423SLionel Sambuc switch (state) { 407*433d6423SLionel Sambuc case '\n': 408*433d6423SLionel Sambuc if ('.' == c) /* '.' at BOL */ 409*433d6423SLionel Sambuc state = '.'; 410*433d6423SLionel Sambuc else if (*p == c) { /* start of postmark */ 411*433d6423SLionel Sambuc ++p; 412*433d6423SLionel Sambuc state = 'P'; 413*433d6423SLionel Sambuc } else { /* anything else */ 414*433d6423SLionel Sambuc if ('\n' == c) 415*433d6423SLionel Sambuc blankline = 1; 416*433d6423SLionel Sambuc else { 417*433d6423SLionel Sambuc state = '\0'; 418*433d6423SLionel Sambuc blankline = 0; 419*433d6423SLionel Sambuc } 420*433d6423SLionel Sambuc putc(c, tofp); 421*433d6423SLionel Sambuc } 422*433d6423SLionel Sambuc break; 423*433d6423SLionel Sambuc case '.': 424*433d6423SLionel Sambuc if ('\n' == c) goto done; 425*433d6423SLionel Sambuc state = '\0'; 426*433d6423SLionel Sambuc putc('.', tofp); 427*433d6423SLionel Sambuc putc(c, tofp); 428*433d6423SLionel Sambuc break; 429*433d6423SLionel Sambuc case 'P': 430*433d6423SLionel Sambuc if (*p == c) { 431*433d6423SLionel Sambuc if (*++p == '\0') { /* successfully reached end */ 432*433d6423SLionel Sambuc p = postmark; 433*433d6423SLionel Sambuc putc('>', tofp); 434*433d6423SLionel Sambuc fputs(postmark, tofp); 435*433d6423SLionel Sambuc state = '\0'; 436*433d6423SLionel Sambuc break; 437*433d6423SLionel Sambuc } 438*433d6423SLionel Sambuc break; /* not there yet */ 439*433d6423SLionel Sambuc } 440*433d6423SLionel Sambuc state = ('\n' == c) ? '\n' : '\0'; 441*433d6423SLionel Sambuc for (q = postmark; q < p; ++q) putc(*q, tofp); 442*433d6423SLionel Sambuc putc(c, tofp); 443*433d6423SLionel Sambuc blankline = 0; 444*433d6423SLionel Sambuc p = postmark; 445*433d6423SLionel Sambuc break; 446*433d6423SLionel Sambuc default: 447*433d6423SLionel Sambuc state = ('\n' == c) ? '\n' : '\0'; 448*433d6423SLionel Sambuc putc(c, tofp); 449*433d6423SLionel Sambuc } 450*433d6423SLionel Sambuc } 451*433d6423SLionel Sambuc if ('\n' != state) putc('\n', tofp); 452*433d6423SLionel Sambuc done: 453*433d6423SLionel Sambuc if (!blankline) putc('\n', tofp); 454*433d6423SLionel Sambuc if (ferror(tofp)) return -1; 455*433d6423SLionel Sambuc return 0; 456*433d6423SLionel Sambuc } 457*433d6423SLionel Sambuc 458*433d6423SLionel Sambuc void readbox() 459*433d6423SLionel Sambuc { 460*433d6423SLionel Sambuc char linebuf[512]; 461*433d6423SLionel Sambuc struct letter *let; 462*433d6423SLionel Sambuc off_t current; 463*433d6423SLionel Sambuc 464*433d6423SLionel Sambuc firstlet = lastlet = NULL; 465*433d6423SLionel Sambuc 466*433d6423SLionel Sambuc if (access(mailbox, 4) < 0 || NULL == (boxfp = fopen(mailbox, "r"))) { 467*433d6423SLionel Sambuc if (usedrop && ENOENT == errno) return; 468*433d6423SLionel Sambuc fprintf(stderr, "can't access mailbox "); 469*433d6423SLionel Sambuc perror(mailbox); 470*433d6423SLionel Sambuc exit(1); 471*433d6423SLionel Sambuc } 472*433d6423SLionel Sambuc current = 0L; 473*433d6423SLionel Sambuc while (1) { 474*433d6423SLionel Sambuc if (NULL == fgets(linebuf, sizeof linebuf, boxfp)) break; 475*433d6423SLionel Sambuc 476*433d6423SLionel Sambuc if (!strncmp(linebuf, "From ", (size_t)5)) { 477*433d6423SLionel Sambuc if (NULL == (let = (struct letter *) malloc(sizeof(struct letter)))) { 478*433d6423SLionel Sambuc fprintf(stderr, "Out of memory.\n"); 479*433d6423SLionel Sambuc exit(1); 480*433d6423SLionel Sambuc } 481*433d6423SLionel Sambuc if (NULL == lastlet) { 482*433d6423SLionel Sambuc firstlet = let; 483*433d6423SLionel Sambuc let->prev = NULL; 484*433d6423SLionel Sambuc } else { 485*433d6423SLionel Sambuc let->prev = lastlet; 486*433d6423SLionel Sambuc lastlet->next = let; 487*433d6423SLionel Sambuc } 488*433d6423SLionel Sambuc lastlet = let; 489*433d6423SLionel Sambuc let->next = NULL; 490*433d6423SLionel Sambuc 491*433d6423SLionel Sambuc let->status = UNREAD; 492*433d6423SLionel Sambuc let->location = current; 493*433d6423SLionel Sambuc D(printf("letter at %ld\n", current)); 494*433d6423SLionel Sambuc } 495*433d6423SLionel Sambuc current += strlen(linebuf); 496*433d6423SLionel Sambuc } 497*433d6423SLionel Sambuc } 498*433d6423SLionel Sambuc 499*433d6423SLionel Sambuc void printall() 500*433d6423SLionel Sambuc { 501*433d6423SLionel Sambuc struct letter *let; 502*433d6423SLionel Sambuc 503*433d6423SLionel Sambuc let = reversemode ? firstlet : lastlet; 504*433d6423SLionel Sambuc 505*433d6423SLionel Sambuc if (NULL == let) { 506*433d6423SLionel Sambuc printf("No mail.\n"); 507*433d6423SLionel Sambuc return; 508*433d6423SLionel Sambuc } 509*433d6423SLionel Sambuc while (NULL != let) { 510*433d6423SLionel Sambuc printlet(let, stdout); 511*433d6423SLionel Sambuc let = reversemode ? let->next : let->prev; 512*433d6423SLionel Sambuc } 513*433d6423SLionel Sambuc } 514*433d6423SLionel Sambuc 515*433d6423SLionel Sambuc void interact() 516*433d6423SLionel Sambuc { 517*433d6423SLionel Sambuc char linebuf[512]; /* user input line */ 518*433d6423SLionel Sambuc struct letter *let, *next; /* current and next letter */ 519*433d6423SLionel Sambuc int interrupted = 0; /* SIGINT hit during letter print */ 520*433d6423SLionel Sambuc int needprint = 1; /* need to print this letter */ 521*433d6423SLionel Sambuc char *savefile; /* filename to save into */ 522*433d6423SLionel Sambuc 523*433d6423SLionel Sambuc if (NULL == firstlet) { 524*433d6423SLionel Sambuc printf("No mail.\n"); 525*433d6423SLionel Sambuc return; 526*433d6423SLionel Sambuc } 527*433d6423SLionel Sambuc let = reversemode ? firstlet : lastlet; 528*433d6423SLionel Sambuc 529*433d6423SLionel Sambuc while (1) { 530*433d6423SLionel Sambuc next = reversemode ? let->next : let->prev; 531*433d6423SLionel Sambuc if (NULL == next) next = let; 532*433d6423SLionel Sambuc 533*433d6423SLionel Sambuc if (!quitmode) { 534*433d6423SLionel Sambuc interrupted = setjmp(printjump); 535*433d6423SLionel Sambuc signal(SIGINT, onint); 536*433d6423SLionel Sambuc } 537*433d6423SLionel Sambuc if (!interrupted && needprint) { 538*433d6423SLionel Sambuc if (DELETED != let->status) let->status = READ; 539*433d6423SLionel Sambuc printlet(let, stdout); 540*433d6423SLionel Sambuc } 541*433d6423SLionel Sambuc if (interrupted) putchar('\n'); 542*433d6423SLionel Sambuc needprint = 0; 543*433d6423SLionel Sambuc fputs(PROMPT, stdout); 544*433d6423SLionel Sambuc fflush(stdout); 545*433d6423SLionel Sambuc 546*433d6423SLionel Sambuc if (fgets(linebuf, sizeof linebuf, stdin) == NULL) break; 547*433d6423SLionel Sambuc 548*433d6423SLionel Sambuc if (!quitmode) signal(SIGINT, SIG_IGN); 549*433d6423SLionel Sambuc 550*433d6423SLionel Sambuc switch (linebuf[0]) { 551*433d6423SLionel Sambuc case '\n': 552*433d6423SLionel Sambuc let = next; 553*433d6423SLionel Sambuc needprint = 1; 554*433d6423SLionel Sambuc continue; 555*433d6423SLionel Sambuc case 'd': 556*433d6423SLionel Sambuc let->status = DELETED; 557*433d6423SLionel Sambuc if (next != let)/* look into this */ 558*433d6423SLionel Sambuc needprint = 1; 559*433d6423SLionel Sambuc needupdate = 1; 560*433d6423SLionel Sambuc let = next; 561*433d6423SLionel Sambuc continue; 562*433d6423SLionel Sambuc case 'p': 563*433d6423SLionel Sambuc needprint = 1; 564*433d6423SLionel Sambuc continue; 565*433d6423SLionel Sambuc case '-': 566*433d6423SLionel Sambuc next = reversemode ? let->prev : let->next; 567*433d6423SLionel Sambuc if (NULL == next) next = let; 568*433d6423SLionel Sambuc let = next; 569*433d6423SLionel Sambuc needprint = 1; 570*433d6423SLionel Sambuc continue; 571*433d6423SLionel Sambuc case 's': 572*433d6423SLionel Sambuc for (savefile = strtok(linebuf + 1, " \t\n"); 573*433d6423SLionel Sambuc savefile != NULL; 574*433d6423SLionel Sambuc savefile = strtok((char *) NULL, " \t\n")) { 575*433d6423SLionel Sambuc savelet(let, savefile); 576*433d6423SLionel Sambuc } 577*433d6423SLionel Sambuc continue; 578*433d6423SLionel Sambuc case '!': 579*433d6423SLionel Sambuc doshell(linebuf + 1); 580*433d6423SLionel Sambuc continue; 581*433d6423SLionel Sambuc case '*': 582*433d6423SLionel Sambuc dohelp(); 583*433d6423SLionel Sambuc continue; 584*433d6423SLionel Sambuc case 'q': 585*433d6423SLionel Sambuc return; 586*433d6423SLionel Sambuc case 'x': 587*433d6423SLionel Sambuc exit(0); 588*433d6423SLionel Sambuc default: 589*433d6423SLionel Sambuc fprintf(stderr, "Illegal command\n"); 590*433d6423SLionel Sambuc continue; 591*433d6423SLionel Sambuc } 592*433d6423SLionel Sambuc } 593*433d6423SLionel Sambuc } 594*433d6423SLionel Sambuc 595*433d6423SLionel Sambuc void onint(dummy) 596*433d6423SLionel Sambuc int dummy; /* to satisfy ANSI compilers */ 597*433d6423SLionel Sambuc { 598*433d6423SLionel Sambuc longjmp(printjump, 1); 599*433d6423SLionel Sambuc } 600*433d6423SLionel Sambuc 601*433d6423SLionel Sambuc void savelet(let, savefile) 602*433d6423SLionel Sambuc struct letter *let; 603*433d6423SLionel Sambuc char *savefile; 604*433d6423SLionel Sambuc { 605*433d6423SLionel Sambuc int waitstat, pid; 606*433d6423SLionel Sambuc FILE *savefp; 607*433d6423SLionel Sambuc 608*433d6423SLionel Sambuc if ((pid = fork()) < 0) { 609*433d6423SLionel Sambuc perror("mail: couldn't fork"); 610*433d6423SLionel Sambuc return; 611*433d6423SLionel Sambuc } else if (pid != 0) { /* parent */ 612*433d6423SLionel Sambuc wait(&waitstat); 613*433d6423SLionel Sambuc return; 614*433d6423SLionel Sambuc } 615*433d6423SLionel Sambuc 616*433d6423SLionel Sambuc /* Child */ 617*433d6423SLionel Sambuc setgid(getgid()); 618*433d6423SLionel Sambuc setuid(getuid()); 619*433d6423SLionel Sambuc if ((savefp = fopen(savefile, "a")) == NULL) { 620*433d6423SLionel Sambuc perror(savefile); 621*433d6423SLionel Sambuc exit(0); 622*433d6423SLionel Sambuc } 623*433d6423SLionel Sambuc printlet(let, savefp); 624*433d6423SLionel Sambuc if ((ferror(savefp) != 0) | (fclose(savefp) != 0)) { 625*433d6423SLionel Sambuc fprintf(stderr, "savefile write error:"); 626*433d6423SLionel Sambuc perror(savefile); 627*433d6423SLionel Sambuc } 628*433d6423SLionel Sambuc exit(0); 629*433d6423SLionel Sambuc } 630*433d6423SLionel Sambuc 631*433d6423SLionel Sambuc void updatebox() 632*433d6423SLionel Sambuc { 633*433d6423SLionel Sambuc FILE *tempfp; /* fp for tempfile */ 634*433d6423SLionel Sambuc char lockname[PATHLEN]; /* maildrop lock */ 635*433d6423SLionel Sambuc int locktries = 0; /* tries when box is locked */ 636*433d6423SLionel Sambuc struct letter *let; /* current letter */ 637*433d6423SLionel Sambuc int c; 638*433d6423SLionel Sambuc 639*433d6423SLionel Sambuc sprintf(lockname, LOCKNAME, whoami()); 640*433d6423SLionel Sambuc 641*433d6423SLionel Sambuc if (NULL == (tempfp = fopen(tempname, "w"))) { 642*433d6423SLionel Sambuc perror("mail: can't create temporary file"); 643*433d6423SLionel Sambuc return; 644*433d6423SLionel Sambuc } 645*433d6423SLionel Sambuc for (let = firstlet; let != NULL; let = let->next) { 646*433d6423SLionel Sambuc if (let->status != DELETED) { 647*433d6423SLionel Sambuc printlet(let, tempfp); 648*433d6423SLionel Sambuc D(printf("printed letter at %ld\n", let->location)); 649*433d6423SLionel Sambuc } 650*433d6423SLionel Sambuc } 651*433d6423SLionel Sambuc 652*433d6423SLionel Sambuc if (ferror(tempfp) || NULL == (tempfp = freopen(tempname, "r", tempfp))) { 653*433d6423SLionel Sambuc perror("mail: temporary file write error"); 654*433d6423SLionel Sambuc unlink(tempname); 655*433d6423SLionel Sambuc return; 656*433d6423SLionel Sambuc } 657*433d6423SLionel Sambuc 658*433d6423SLionel Sambuc /* Shut off signals during the update */ 659*433d6423SLionel Sambuc signal(SIGINT, SIG_IGN); 660*433d6423SLionel Sambuc signal(SIGHUP, SIG_IGN); 661*433d6423SLionel Sambuc signal(SIGQUIT, SIG_IGN); 662*433d6423SLionel Sambuc 663*433d6423SLionel Sambuc if (usedrop) while (link(mailbox, lockname) != 0) { 664*433d6423SLionel Sambuc if (++locktries >= LOCKTRIES) { 665*433d6423SLionel Sambuc fprintf(stderr, "mail: couldn't lock maildrop for update\n"); 666*433d6423SLionel Sambuc return; 667*433d6423SLionel Sambuc } 668*433d6423SLionel Sambuc sleep(LOCKWAIT); 669*433d6423SLionel Sambuc } 670*433d6423SLionel Sambuc 671*433d6423SLionel Sambuc if (NULL == (boxfp = freopen(mailbox, "w", boxfp))) { 672*433d6423SLionel Sambuc perror("mail: couldn't reopen maildrop"); 673*433d6423SLionel Sambuc fprintf(stderr, "mail may have been lost; look in %s\n", tempname); 674*433d6423SLionel Sambuc if (usedrop) unlink(lockname); 675*433d6423SLionel Sambuc return; 676*433d6423SLionel Sambuc } 677*433d6423SLionel Sambuc unlink(tempname); 678*433d6423SLionel Sambuc 679*433d6423SLionel Sambuc while ((c = getc(tempfp)) != EOF) putc(c, boxfp); 680*433d6423SLionel Sambuc 681*433d6423SLionel Sambuc fclose(boxfp); 682*433d6423SLionel Sambuc 683*433d6423SLionel Sambuc if (usedrop) unlink(lockname); 684*433d6423SLionel Sambuc } 685*433d6423SLionel Sambuc 686*433d6423SLionel Sambuc void printlet(let, tofp) 687*433d6423SLionel Sambuc struct letter *let; 688*433d6423SLionel Sambuc FILE *tofp; 689*433d6423SLionel Sambuc { 690*433d6423SLionel Sambuc off_t current, limit; 691*433d6423SLionel Sambuc int c; 692*433d6423SLionel Sambuc 693*433d6423SLionel Sambuc fseek(boxfp, (current = let->location), 0); 694*433d6423SLionel Sambuc limit = (NULL != let->next) ? let->next->location : -1; 695*433d6423SLionel Sambuc 696*433d6423SLionel Sambuc while (current != limit && (c = getc(boxfp)) != EOF) { 697*433d6423SLionel Sambuc putc(c, tofp); 698*433d6423SLionel Sambuc ++current; 699*433d6423SLionel Sambuc } 700*433d6423SLionel Sambuc } 701*433d6423SLionel Sambuc 702*433d6423SLionel Sambuc void doshell(command) 703*433d6423SLionel Sambuc char *command; 704*433d6423SLionel Sambuc { 705*433d6423SLionel Sambuc int waitstat, pid; 706*433d6423SLionel Sambuc char *shell; 707*433d6423SLionel Sambuc 708*433d6423SLionel Sambuc if (NULL == (shell = getenv("SHELL"))) shell = SHELL; 709*433d6423SLionel Sambuc 710*433d6423SLionel Sambuc if ((pid = fork()) < 0) { 711*433d6423SLionel Sambuc perror("mail: couldn't fork"); 712*433d6423SLionel Sambuc return; 713*433d6423SLionel Sambuc } else if (pid != 0) { /* parent */ 714*433d6423SLionel Sambuc wait(&waitstat); 715*433d6423SLionel Sambuc return; 716*433d6423SLionel Sambuc } 717*433d6423SLionel Sambuc 718*433d6423SLionel Sambuc /* Child */ 719*433d6423SLionel Sambuc setgid(getgid()); 720*433d6423SLionel Sambuc setuid(getuid()); 721*433d6423SLionel Sambuc umask(oldmask); 722*433d6423SLionel Sambuc 723*433d6423SLionel Sambuc execl(shell, shell, "-c", command, (char *) NULL); 724*433d6423SLionel Sambuc fprintf(stderr, "can't exec shell\n"); 725*433d6423SLionel Sambuc exit(127); 726*433d6423SLionel Sambuc } 727*433d6423SLionel Sambuc 728*433d6423SLionel Sambuc void usage() 729*433d6423SLionel Sambuc { 730*433d6423SLionel Sambuc fprintf(stderr, "usage: mail [-epqr] [-f file]\n"); 731*433d6423SLionel Sambuc fprintf(stderr, " mail [-dtv] [-s subject] user [...]\n"); 732*433d6423SLionel Sambuc } 733*433d6423SLionel Sambuc 734*433d6423SLionel Sambuc char *basename(name) 735*433d6423SLionel Sambuc char *name; 736*433d6423SLionel Sambuc { 737*433d6423SLionel Sambuc char *p; 738*433d6423SLionel Sambuc 739*433d6423SLionel Sambuc if (NULL == (p = rindex(name, '/'))) 740*433d6423SLionel Sambuc return name; 741*433d6423SLionel Sambuc else 742*433d6423SLionel Sambuc return p + 1; 743*433d6423SLionel Sambuc } 744*433d6423SLionel Sambuc 745*433d6423SLionel Sambuc char *whoami() 746*433d6423SLionel Sambuc { 747*433d6423SLionel Sambuc struct passwd *pw; 748*433d6423SLionel Sambuc 749*433d6423SLionel Sambuc if (NULL != (pw = getpwuid(getuid()))) 750*433d6423SLionel Sambuc return pw->pw_name; 751*433d6423SLionel Sambuc else 752*433d6423SLionel Sambuc return "nobody"; 753*433d6423SLionel Sambuc } 754*433d6423SLionel Sambuc 755*433d6423SLionel Sambuc void dohelp() 756*433d6423SLionel Sambuc { 757*433d6423SLionel Sambuc FILE *fp; 758*433d6423SLionel Sambuc char buffer[80]; 759*433d6423SLionel Sambuc 760*433d6423SLionel Sambuc if ( (fp = fopen(HELPFILE, "r")) == NULL) 761*433d6423SLionel Sambuc fprintf(stdout, "can't open helpfile %s\n", HELPFILE); 762*433d6423SLionel Sambuc else 763*433d6423SLionel Sambuc while (fgets(buffer, 80, fp)) 764*433d6423SLionel Sambuc fputs(buffer, stdout); 765*433d6423SLionel Sambuc } 766*433d6423SLionel Sambuc 767*433d6423SLionel Sambuc int filesize(name) 768*433d6423SLionel Sambuc char *name ; 769*433d6423SLionel Sambuc { 770*433d6423SLionel Sambuc struct stat buf; 771*433d6423SLionel Sambuc 772*433d6423SLionel Sambuc if (stat(name, &buf) == -1) 773*433d6423SLionel Sambuc buf.st_size = 0L; 774*433d6423SLionel Sambuc 775*433d6423SLionel Sambuc return (buf.st_size ? 1 : 0); 776*433d6423SLionel Sambuc } 777