+#include <stdio.h>
+#include <pwd.h>
+#include <utmp.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <setjmp.h>
+#include <whoami.h>
+
+/*copylet flags */
+ /*remote mail, add rmtmsg */
+#define REMOTE 1
+ /* zap header and trailing empty line */
+#define ZAP 3
+#define ORDINARY 2
+#define FORWARD 4
+#define LSIZE 256
+#define MAXLET 300 /* maximum number of letters */
+#define MAILMODE (~0644) /* mode of created mail */
+
+char line[LSIZE];
+char resp[LSIZE];
+struct let {
+ long adr;
+ char change;
+} let[MAXLET];
+int nlet = 0;
+char lfil[50];
+long iop, time();
+char lettmp[] = "/tmp/maXXXXX";
+char maildir[] = "/usr/spool/mail/";
+char mailfile[] = "/usr/spool/mail/xxxxxxxxxxxxxxxxxxxxxxx";
+char dead[] = "dead.letter";
+char *thissys = sysname;
+char forwmsg[] = " forwarded\n";
+char *curlock;
+int lockerror;
+FILE *tmpf;
+FILE *malf;
+char *my_name;
+char *getlogin();
+struct passwd *getpwuid();
+int error;
+int locked;
+int changed;
+int forward;
+char from[] = "From ";
+long ftell();
+int delete();
+char *ctime();
+int flgf;
+int flgp;
+int delflg = 1;
+jmp_buf sjbuf;
+
+main(argc, argv)
+char **argv;
+{
+ register i;
+ char sobuf[BUFSIZ];
+
+ setbuf(stdout, sobuf);
+ mktemp(lettmp);
+ unlink(lettmp);
+ my_name = getlogin();
+ if (my_name == NULL) {
+ struct passwd *pwent;
+ pwent = getpwuid(getuid());
+ if (pwent==NULL)
+ my_name = "???";
+ else
+ my_name = pwent->pw_name;
+ }
+ if(setjmp(sjbuf)) done();
+ for (i=0; i<20; i++)
+ setsig(i, delete);
+ tmpf = fopen(lettmp, "w");
+ if (tmpf == NULL) {
+ fprintf(stderr, "mail: cannot open %s for writing\n", lettmp);
+ done();
+ }
+ if (argv[0][0] != 'r' && /* no favors for rmail*/
+ (argc == 1 || argv[1][0] == '-'))
+ printmail(argc, argv);
+ else
+ sendmail(argc, argv);
+ done();
+}
+
+setsig(i, f)
+int i;
+int (*f)();
+{
+ if(signal(i, SIG_IGN)!=SIG_IGN)
+ signal(i, f);
+}
+
+printmail(argc, argv)
+char **argv;
+{
+ int flg, i, j, print;
+ char *p, *getarg();
+
+ setuid(getuid());
+ cat(mailfile, maildir, my_name);
+ for (; argc>1; argv++, argc--) {
+ if (argv[1][0]=='-') {
+ if (argv[1][1]=='q')
+ delflg = 0;
+ else if (argv[1][1]=='p') {
+ flgp++;
+ delflg = 0;
+ } else if (argv[1][1]=='f') {
+ if (argc>=3) {
+ strcpy(mailfile, argv[2]);
+ argv++;
+ argc--;
+ }
+ } else if (argv[1][1]=='r') {
+ forward = 1;
+ } else {
+ fprintf(stderr, "mail: unknown option %c\n", argv[1][1]);
+ done();
+ }
+ } else
+ break;
+ }
+ malf = fopen(mailfile, "r");
+ if (malf == NULL) {
+ fprintf(stdout, "No mail.\n");
+ return;
+ }
+ lock(mailfile);
+ copymt(malf, tmpf);
+ fclose(malf);
+ fclose(tmpf);
+ unlock();
+ tmpf = fopen(lettmp, "r");
+
+ changed = 0;
+ print = 1;
+ for (i = 0; i < nlet; ) {
+ j = forward ? i : nlet - i - 1;
+ if(setjmp(sjbuf)) {
+ print=0;
+ } else {
+ if (print)
+ copylet(j, stdout, ORDINARY);
+ print = 1;
+ }
+ if (flgp) {
+ i++;
+ continue;
+ }
+ setjmp(sjbuf);
+ fprintf(stdout, "? ");
+ fflush(stdout);
+ if (fgets(resp, LSIZE, stdin) == NULL)
+ break;
+ switch (resp[0]) {
+
+ default:
+ fprintf(stderr, "usage\n");
+ case '?':
+ print = 0;
+ fprintf(stderr, "q\tquit\n");
+ fprintf(stderr, "x\texit without changing mail\n");
+ fprintf(stderr, "p\tprint\n");
+ fprintf(stderr, "s[file]\tsave (default mbox)\n");
+ fprintf(stderr, "w[file]\tsame without header\n");
+ fprintf(stderr, "-\tprint previous\n");
+ fprintf(stderr, "d\tdelete\n");
+ fprintf(stderr, "+\tnext (no delete)\n");
+ fprintf(stderr, "m user\tmail to user\n");
+ fprintf(stderr, "! cmd\texecute cmd\n");
+ break;
+
+ case '+':
+ case 'n':
+ case '\n':
+ i++;
+ break;
+ case 'x':
+ changed = 0;
+ case 'q':
+ goto donep;
+ case 'p':
+ break;
+ case '^':
+ case '-':
+ if (--i < 0)
+ i = 0;
+ break;
+ case 'y':
+ case 'w':
+ case 's':
+ flg = 0;
+ if (resp[1] != '\n' && resp[1] != ' ') {
+ printf("illegal\n");
+ flg++;
+ print = 0;
+ continue;
+ }
+ if (resp[1] == '\n' || resp[1] == '\0')
+ cat(resp+1, "mbox", "");
+ for (p = resp+1; (p = getarg(lfil, p)) != NULL; ) {
+ malf = fopen(lfil, "a");
+ if (malf == NULL) {
+ fprintf(stdout, "mail: cannot append to %s\n", lfil);
+ flg++;
+ continue;
+ }
+ copylet(j, malf, resp[0]=='w'? ZAP: ORDINARY);
+ fclose(malf);
+ }
+ if (flg)
+ print = 0;
+ else {
+ let[j].change = 'd';
+ changed++;
+ i++;
+ }
+ break;
+ case 'm':
+ flg = 0;
+ if (resp[1] == '\n' || resp[1] == '\0') {
+ i++;
+ continue;
+ }
+ if (resp[1] != ' ') {
+ printf("invalid command\n");
+ flg++;
+ print = 0;
+ continue;
+ }
+ for (p = resp+1; (p = getarg(lfil, p)) != NULL; )
+ if (!sendrmt(j, lfil)) /* couldn't send it */
+ flg++;
+ if (flg)
+ print = 0;
+ else {
+ let[j].change = 'd';
+ changed++;
+ i++;
+ }
+ break;
+ case '!':
+ system(resp+1);
+ printf("!\n");
+ print = 0;
+ break;
+ case 'd':
+ let[j].change = 'd';
+ changed++;
+ i++;
+ if (resp[1] == 'q')
+ goto donep;
+ break;
+ }
+ }
+ donep:
+ if (changed)
+ copyback();
+}
+
+copyback() /* copy temp or whatever back to /usr/spool/mail */
+{
+ register i, n, c;
+ int new = 0;
+ struct stat stbuf;
+
+ signal(SIGINT, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ lock(mailfile);
+ stat(mailfile, &stbuf);
+ if (stbuf.st_size != let[nlet].adr) { /* new mail has arrived */
+ malf = fopen(mailfile, "r");
+ if (malf == NULL) {
+ fprintf(stdout, "mail: can't re-read %s\n", mailfile);
+ done();
+ }
+ fseek(malf, let[nlet].adr, 0);
+ fclose(tmpf);
+ tmpf = fopen(lettmp, "a");
+ fseek(tmpf, let[nlet].adr, 0);
+ while ((c = fgetc(malf)) != EOF)
+ fputc(c, tmpf);
+ fclose(malf);
+ fclose(tmpf);
+ tmpf = fopen(lettmp, "r");
+ let[++nlet].adr = stbuf.st_size;
+ new = 1;
+ }
+ malf = fopen(mailfile, "w");
+ if (malf == NULL) {
+ fprintf(stderr, "mail: can't rewrite %s\n", lfil);
+ done();
+ }
+ n = 0;
+ for (i = 0; i < nlet; i++)
+ if (let[i].change != 'd') {
+ copylet(i, malf, ORDINARY);
+ n++;
+ }
+ fclose(malf);
+ if (new)
+ fprintf(stdout, "new mail arrived\n");
+ unlock();
+}
+
+copymt(f1, f2) /* copy mail (f1) to temp (f2) */
+FILE *f1, *f2;
+{
+ long nextadr;
+
+ nlet = nextadr = 0;
+ let[0].adr = 0;
+ while (fgets(line, LSIZE, f1) != NULL) {
+ if (isfrom(line))
+ let[nlet++].adr = nextadr;
+ nextadr += strlen(line);
+ fputs(line, f2);
+ }
+ let[nlet].adr = nextadr; /* last plus 1 */
+}
+
+copylet(n, f, type) FILE *f;
+{ int ch, k;
+ fseek(tmpf, let[n].adr, 0);
+ k = let[n+1].adr - let[n].adr;
+ while(k-- > 1 && (ch=fgetc(tmpf))!='\n')
+ if(type!=ZAP) fputc(ch,f);
+ if(type==REMOTE)
+ fprintf(f, " remote from %s\n", thissys);
+ else if (type==FORWARD)
+ fprintf(f, forwmsg);
+ else if(type==ORDINARY)
+ fputc(ch,f);
+ while(k-->1)
+ fputc(ch=fgetc(tmpf), f);
+ if(type!=ZAP || ch!= '\n')
+ fputc(fgetc(tmpf), f);
+}
+
+isfrom(lp)
+register char *lp;
+{
+ register char *p;
+
+ for (p = from; *p; )
+ if (*lp++ != *p++)
+ return(0);
+ return(1);
+}
+
+sendmail(argc, argv)
+char **argv;
+{
+
+ time(&iop);
+ fprintf(tmpf, "%s%s %s", from, my_name, ctime(&iop));
+ iop = ftell(tmpf);
+ flgf = 1;
+ while (fgets(line, LSIZE, stdin) != NULL) {
+ if (line[0] == '.' && line[1] == '\n')
+ break;
+ if (isfrom(line))
+ fputs(">", tmpf);
+ fputs(line, tmpf);
+ flgf = 0;
+ }
+ fputs("\n", tmpf);
+ nlet = 1;
+ let[0].adr = 0;
+ let[1].adr = ftell(tmpf);
+ fclose(tmpf);
+ if (flgf)
+ return;
+ tmpf = fopen(lettmp, "r");
+ if (tmpf == NULL) {
+ fprintf(stderr, "mail: cannot reopen %s for reading\n", lettmp);
+ return;
+ }
+ while (--argc > 0)
+ if (!send(0, *++argv)) /* couldn't send to him */
+ error++;
+ if (error) {
+ setuid(getuid());
+ malf = fopen(dead, "w");
+ if (malf == NULL) {
+ fprintf(stdout, "mail: cannot open %s\n", dead);
+ fclose(tmpf);
+ return;
+ }
+ copylet(0, malf, ZAP);
+ fclose(malf);
+ fprintf(stdout, "Mail saved in %s\n", dead);
+ }
+ fclose(tmpf);
+}
+
+sendrmt(n, name)
+char *name;
+{
+ FILE *rmf, *popen();
+ register char *p;
+ char rsys[64], cmd[64];
+ register local, pid;
+ int sts;
+
+ local = 0;
+ if (*name=='!')
+ name++;
+ for(p=rsys; *name!='!'; *p++ = *name++)
+ if (*name=='\0') {
+ local++;
+ break;
+ }
+ *p = '\0';
+ if ((!local && *name=='\0') || (local && *rsys=='\0')) {
+ fprintf(stdout, "null name\n");
+ return(0);
+ }
+ if ((pid = fork()) == -1) {
+ fprintf(stderr, "mail: can't create proc for remote\n");
+ return(0);
+ }
+ if (pid) {
+ while (wait(&sts) != pid) {
+ if (wait(&sts)==-1)
+ return(0);
+ }
+ return(!sts);
+ }
+ setuid(getuid());
+ if (local)
+ sprintf(cmd, "mail %s", rsys);
+ else {
+ if (index(name+1, '!'))
+ sprintf(cmd, "uux - %s!rmail \\(%s\\)", rsys, name+1);
+ else
+ sprintf(cmd, "uux - %s!rmail %s", rsys, name+1);
+ }
+ if ((rmf=popen(cmd, "w")) == NULL)
+ exit(1);
+ copylet(n, rmf, local? FORWARD: REMOTE);
+ pclose(rmf);
+ exit(0);
+}
+
+send(n, name) /* send letter n to name */
+int n;
+char *name;
+{
+ char file[50];
+ register char *p;
+ register mask;
+ struct passwd *pw, *getpwnam();
+
+ for(p=name; *p!='!' &&*p!='\0'; p++)
+ ;
+ if (*p == '!')
+ return(sendrmt(n, name));
+ if ((pw = getpwnam(name)) == NULL) {
+ fprintf(stdout, "mail: can't send to %s\n", name);
+ return(0);
+ }
+ cat(file, maildir, name);
+ mask = umask(MAILMODE);
+ malf = fopen(file, "a");
+ umask(mask);
+ if (malf == NULL) {
+ fprintf(stdout, "mail: cannot append to %s\n", file);
+ return(0);
+ }
+ lock(file);
+ chown(file, pw->pw_uid, pw->pw_gid);
+ copylet(n, malf, ORDINARY);
+ fclose(malf);
+ unlock();
+ return(1);
+}
+
+delete(i)
+{
+ setsig(i, delete);
+ fprintf(stderr, "\n");
+ if(delflg)
+ longjmp(sjbuf, 1);
+ done();
+}
+
+done()
+{
+ if(!lockerror)
+ unlock();
+ unlink(lettmp);
+ exit(error+lockerror);
+}
+
+lock(file)
+char *file;
+{
+ struct stat stbuf;
+
+ if (locked || flgf)
+ return;
+ if (stat(file, &stbuf)<0)
+ return;
+ if (stbuf.st_mode&01) { /* user x bit is the lock */
+ if (stbuf.st_ctime+60 >= time((long *)0)) {
+ fprintf(stderr, "%s busy; try again in a minute\n", file);
+ lockerror++;
+ done();
+ }
+ }
+ locked = stbuf.st_mode & ~01;
+ curlock = file;
+ chmod(file, stbuf.st_mode|01);
+}
+
+unlock()
+{
+ if (locked)
+ chmod(curlock, locked);
+ locked = 0;
+}
+
+cat(to, from1, from2)
+char *to, *from1, *from2;
+{
+ int i, j;
+
+ j = 0;
+ for (i=0; from1[i]; i++)
+ to[j++] = from1[i];
+ for (i=0; from2[i]; i++)
+ to[j++] = from2[i];
+ to[j] = 0;
+}
+
+char *getarg(s, p) /* copy p... into s, update p */
+register char *s, *p;
+{
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (*p == '\n' || *p == '\0')
+ return(NULL);
+ while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
+ *s++ = *p++;
+ *s = '\0';
+ return(p);
+}