BSD 4 release
[unix-history] / usr / src / cmd / delivermail / vax-mail.c
CommitLineData
1c12a606
BJ
1#include <ctype.h>
2#include <stdio.h>
3#include <pwd.h>
4#include <utmp.h>
5#include <signal.h>
6#include <sys/types.h>
7#include <sys/stat.h>
8#include <setjmp.h>
9#include <whoami.h>
10#include <sysexits.h>
11
31cef89c 12static char SccsId[] = "@(#)mail.c 4.1 10/1/80";
6f70d036 13
1c12a606
BJ
14#define DELIVERMAIL "/etc/delivermail"
15
16
17/*copylet flags */
18 /*remote mail, add rmtmsg */
19#define REMOTE 1
20 /* zap header and trailing empty line */
21#define ZAP 3
22#define ORDINARY 2
23#define FORWARD 4
24#define LSIZE 256
25#define MAXLET 300 /* maximum number of letters */
26#define MAILMODE (~0644) /* mode of created mail */
6f70d036 27# ifndef DELIVERMAIL
1c12a606
BJ
28#define RMAIL "/usr/net/bin/sendberkmail"
29#define LOCNAM1 "csvax"
30#define LOCNAM2 "ucbvax"
31#define LOCNAM3 "vax"
32#define LOCNAM4 "v"
6f70d036 33# endif
1c12a606
BJ
34
35char line[LSIZE];
36char resp[LSIZE];
37struct let {
38 long adr;
39 char change;
40} let[MAXLET];
41int nlet = 0;
42char lfil[50];
43long iop, time();
44char *getenv();
45char *index();
46char lettmp[] = "/tmp/maXXXXX";
47char maildir[] = "/usr/spool/mail/";
48char mailfile[] = "/usr/spool/mail/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
49char dead[] = "dead.letter";
50char *thissys = sysname;
51char *netname = "vax";
52char forwmsg[] = " forwarded\n";
53FILE *tmpf;
54FILE *malf;
55char *my_name;
56char *getlogin();
57struct passwd *getpwuid();
58int error;
59int changed;
60int forward;
61char from[] = "From ";
62long ftell();
63int delete();
64char *ctime();
65int flgf;
66int flgp;
67int delflg = 1;
68int hseqno;
69jmp_buf sjbuf;
70int rmail;
71
72main(argc, argv)
73char **argv;
74{
75 register i;
76 char sobuf[BUFSIZ];
77
78 setbuf(stdout, sobuf);
79 mktemp(lettmp);
80 unlink(lettmp);
81 my_name = getlogin();
82 if (my_name == NULL || strlen(my_name) == 0) {
83 struct passwd *pwent;
84 pwent = getpwuid(getuid());
85 if (pwent==NULL)
86 my_name = "???";
87 else
88 my_name = pwent->pw_name;
89 }
90 if(setjmp(sjbuf)) done();
91 for (i=0; i<20; i++)
92 setsig(i, delete);
93 tmpf = fopen(lettmp, "w");
94 if (tmpf == NULL) {
95 fprintf(stderr, "mail: cannot open %s for writing\n", lettmp);
96 done();
97 }
98 if (argv[0][0] == 'r')
99 rmail++;
100 if (argv[0][0] != 'r' && /* no favors for rmail*/
101 (argc == 1 || argv[1][0] == '-' && !any(argv[1][1], "rhd")))
102 printmail(argc, argv);
103 else
104 sendmail(argc, argv);
105 done();
106}
107
108setsig(i, f)
109int i;
110int (*f)();
111{
112 if(signal(i, SIG_IGN)!=SIG_IGN)
113 signal(i, f);
114}
115
116any(c, str)
117 register int c;
118 register char *str;
119{
120
121 while (*str)
122 if (c == *str++)
123 return(1);
124 return(0);
125}
126
127printmail(argc, argv)
128char **argv;
129{
130 int flg, i, j, print;
131 char *p, *getarg();
132 struct stat statb;
133
134 setuid(getuid());
135 cat(mailfile, maildir, my_name);
136 if (stat(mailfile, &statb) >= 0
137 && (statb.st_mode & S_IFMT) == S_IFDIR) {
138 strcat(mailfile, "/");
139 strcat(mailfile, my_name);
140 }
141 for (; argc>1; argv++, argc--) {
142 if (argv[1][0]=='-') {
143 if (argv[1][1]=='q')
144 delflg = 0;
145 else if (argv[1][1]=='p') {
146 flgp++;
147 delflg = 0;
148 } else if (argv[1][1]=='f') {
149 if (argc>=3) {
150 strcpy(mailfile, argv[2]);
151 argv++;
152 argc--;
153 }
154 } else if (argv[1][1]=='r') {
155 forward = 1;
156 } else if (argv[1][1]=='h') {
157 forward = 1;
158 } else {
159 fprintf(stderr, "mail: unknown option %c\n", argv[1][1]);
160 done();
161 }
162 } else
163 break;
164 }
165 malf = fopen(mailfile, "r");
166 if (malf == NULL) {
167 fprintf(stdout, "No mail.\n");
168 return;
169 }
170 lock(mailfile);
171 copymt(malf, tmpf);
172 fclose(malf);
173 fclose(tmpf);
174 unlock();
175 tmpf = fopen(lettmp, "r");
176
177 changed = 0;
178 print = 1;
179 for (i = 0; i < nlet; ) {
180 j = forward ? i : nlet - i - 1;
181 if(setjmp(sjbuf)) {
182 print=0;
183 } else {
184 if (print)
185 copylet(j, stdout, ORDINARY);
186 print = 1;
187 }
188 if (flgp) {
189 i++;
190 continue;
191 }
192 setjmp(sjbuf);
193 fprintf(stdout, "? ");
194 fflush(stdout);
195 if (fgets(resp, LSIZE, stdin) == NULL)
196 break;
197 switch (resp[0]) {
198
199 default:
200 fprintf(stderr, "usage\n");
201 case '?':
202 print = 0;
203 fprintf(stderr, "q\tquit\n");
204 fprintf(stderr, "x\texit without changing mail\n");
205 fprintf(stderr, "p\tprint\n");
206 fprintf(stderr, "s[file]\tsave (default mbox)\n");
207 fprintf(stderr, "w[file]\tsame without header\n");
208 fprintf(stderr, "-\tprint previous\n");
209 fprintf(stderr, "d\tdelete\n");
210 fprintf(stderr, "+\tnext (no delete)\n");
211 fprintf(stderr, "m user\tmail to user\n");
212 fprintf(stderr, "! cmd\texecute cmd\n");
213 break;
214
215 case '+':
216 case 'n':
217 case '\n':
218 i++;
219 break;
220 case 'x':
221 changed = 0;
222 case 'q':
223 goto donep;
224 case 'p':
225 break;
226 case '^':
227 case '-':
228 if (--i < 0)
229 i = 0;
230 break;
231 case 'y':
232 case 'w':
233 case 's':
234 flg = 0;
235 if (resp[1] != '\n' && resp[1] != ' ') {
236 printf("illegal\n");
237 flg++;
238 print = 0;
239 continue;
240 }
241 if (resp[1] == '\n' || resp[1] == '\0') {
242 p = getenv("HOME");
243 if(p != 0)
244 cat(resp+1, p, "/mbox");
245 else
246 cat(resp+1, "", "mbox");
247 }
248 for (p = resp+1; (p = getarg(lfil, p)) != NULL; ) {
249 malf = fopen(lfil, "a");
250 if (malf == NULL) {
251 fprintf(stdout, "mail: cannot append to %s\n", lfil);
252 flg++;
253 continue;
254 }
255 copylet(j, malf, resp[0]=='w'? ZAP: ORDINARY);
256 fclose(malf);
257 }
258 if (flg)
259 print = 0;
260 else {
261 let[j].change = 'd';
262 changed++;
263 i++;
264 }
265 break;
266 case 'm':
267 flg = 0;
268 if (resp[1] == '\n' || resp[1] == '\0') {
269 i++;
270 continue;
271 }
272 if (resp[1] != ' ') {
273 printf("invalid command\n");
274 flg++;
275 print = 0;
276 continue;
277 }
278 for (p = resp+1; (p = getarg(lfil, p)) != NULL; )
279 if (!sendrmt(j, lfil, "/bin/mail")) /* couldn't send it */
280 flg++;
281 if (flg)
282 print = 0;
283 else {
284 let[j].change = 'd';
285 changed++;
286 i++;
287 }
288 break;
289 case '!':
290 system(resp+1);
291 printf("!\n");
292 print = 0;
293 break;
294 case 'd':
295 let[j].change = 'd';
296 changed++;
297 i++;
298 if (resp[1] == 'q')
299 goto donep;
300 break;
301 }
302 }
303 donep:
304 if (changed)
305 copyback();
306}
307
308copyback() /* copy temp or whatever back to /usr/spool/mail */
309{
310 register i, n, c;
311 int new = 0;
312 struct stat stbuf;
313
314 signal(SIGINT, SIG_IGN);
315 signal(SIGHUP, SIG_IGN);
316 signal(SIGQUIT, SIG_IGN);
317 lock(mailfile);
318 stat(mailfile, &stbuf);
319 if (stbuf.st_size != let[nlet].adr) { /* new mail has arrived */
320 malf = fopen(mailfile, "r");
321 if (malf == NULL) {
322 fprintf(stdout, "mail: can't re-read %s\n", mailfile);
323 done();
324 }
325 fseek(malf, let[nlet].adr, 0);
326 fclose(tmpf);
327 tmpf = fopen(lettmp, "a");
328 fseek(tmpf, let[nlet].adr, 0);
329 while ((c = fgetc(malf)) != EOF)
330 fputc(c, tmpf);
331 fclose(malf);
332 fclose(tmpf);
333 tmpf = fopen(lettmp, "r");
334 let[++nlet].adr = stbuf.st_size;
335 new = 1;
336 }
337 malf = fopen(mailfile, "w");
338 if (malf == NULL) {
339 fprintf(stderr, "mail: can't rewrite %s\n", lfil);
340 done();
341 }
342 n = 0;
343 for (i = 0; i < nlet; i++)
344 if (let[i].change != 'd') {
345 copylet(i, malf, ORDINARY);
346 n++;
347 }
348 fclose(malf);
349 if (new)
350 fprintf(stdout, "new mail arrived\n");
351 unlock();
352}
353
354copymt(f1, f2) /* copy mail (f1) to temp (f2) */
355FILE *f1, *f2;
356{
357 long nextadr;
358
359 nlet = nextadr = 0;
360 let[0].adr = 0;
361 while (fgets(line, LSIZE, f1) != NULL) {
362 if (isfrom(line))
363 let[nlet++].adr = nextadr;
364 nextadr += strlen(line);
365 fputs(line, f2);
366 }
367 let[nlet].adr = nextadr; /* last plus 1 */
368}
369
370copylet(n, f, type) FILE *f;
371{ int ch, k;
372 fseek(tmpf, let[n].adr, 0);
373 k = let[n+1].adr - let[n].adr;
374 while(k-- > 1 && (ch=fgetc(tmpf))!='\n')
375 if(type!=ZAP) fputc(ch,f);
376 if(type==REMOTE)
377 fprintf(f, " remote from %s\n", thissys);
378 else if (type==FORWARD)
379 fprintf(f, forwmsg);
380 else if(type==ORDINARY)
381 fputc(ch,f);
382 while(k-->1)
383 fputc(ch=fgetc(tmpf), f);
384 if(type!=ZAP || ch!= '\n')
385 fputc(fgetc(tmpf), f);
386}
387
388isfrom(lp)
389register char *lp;
390{
391 register char *p;
392
393 for (p = from; *p; )
394 if (*lp++ != *p++)
395 return(0);
396 return(1);
397}
398
399sendmail(argc, argv)
400char **argv;
401{
402 char truename[100];
403 int first;
404 register char *cp;
405 int gaver = 0;
406# ifdef DELIVERMAIL
407 char *newargv[1000];
408 register char **ap;
409 register char **vp;
410 int dflag;
411
412 dflag = 0;
413 if (argc < 1)
414 fprintf(stderr, "puke\n");
415 for (vp = argv, ap = newargv + 1; (*ap = *vp++) != 0; ap++)
416 {
417 if (ap[0][0] == '-' && ap[0][1] == 'd')
418 dflag++;
419 }
420 if (!dflag)
421 {
422 /* give it to delivermail, rah rah! */
423 unlink(lettmp);
424 ap = newargv+1;
425 if (rmail)
426 *ap-- = "-s";
427 *ap = "-delivermail";
428 execv(DELIVERMAIL, ap);
429 perror(DELIVERMAIL);
430 exit(EX_UNAVAILABLE);
431 }
432# endif DELIVERMAIL
433
434 truename[0] = 0;
435 line[0] = '\0';
436
437 /*
438 * When we fall out of this, argv[1] should be first name,
439 * argc should be number of names + 1.
440 */
441
442 while (argc > 1 && *argv[1] == '-') {
443 cp = *++argv;
444 argc--;
445 switch (cp[1]) {
446 case 'r':
447 if (argc <= 0) {
448 usage();
449 done();
450 }
451 gaver++;
452 strcpy(truename, argv[1]);
453 fgets(line, LSIZE, stdin);
454 if (strcmpn("From", line, 4) == 0)
455 line[0] = '\0';
456 argv++;
457 argc--;
458 break;
459
460 case 'h':
461 if (argc <= 0) {
462 usage();
463 done();
464 }
465 hseqno = atoi(argv[1]);
466 argv++;
467 argc--;
468 break;
469
470# ifdef DELIVERMAIL
471 case 'd':
472 break;
473# endif DELIVERMAIL
474
475 default:
476 usage();
477 done();
478 }
479 }
480 if (argc <= 1) {
481 usage();
482 done();
483 }
484 if (gaver == 0)
485 strcpy(truename, my_name);
486 /*
487 if (argc > 4 && strcmp(argv[1], "-r") == 0) {
488 strcpy(truename, argv[2]);
489 argc -= 2;
490 argv += 2;
491 fgets(line, LSIZE, stdin);
492 if (strcmpn("From", line, 4) == 0)
493 line[0] = '\0';
494 } else
495 strcpy(truename, my_name);
496 */
497 time(&iop);
498 fprintf(tmpf, "%s%s %s", from, truename, ctime(&iop));
499 iop = ftell(tmpf);
500 flgf = 1;
501 for (first = 1;; first = 0) {
502 if (first && line[0] == '\0' && fgets(line, LSIZE, stdin) == NULL)
503 break;
504 if (!first && fgets(line, LSIZE, stdin) == NULL)
505 break;
506 if (line[0] == '.' && line[1] == '\n' && isatty(fileno(stdin)))
507 break;
508 if (isfrom(line))
509 fputs(">", tmpf);
510 fputs(line, tmpf);
511 flgf = 0;
512 }
513 fputs("\n", tmpf);
514 nlet = 1;
515 let[0].adr = 0;
516 let[1].adr = ftell(tmpf);
517 fclose(tmpf);
518 if (flgf)
519 return;
520 tmpf = fopen(lettmp, "r");
521 if (tmpf == NULL) {
522 fprintf(stderr, "mail: cannot reopen %s for reading\n", lettmp);
523 return;
524 }
525 while (--argc > 0)
526 if (!send(0, *++argv, truename))
527 error++;
528 if (error) {
529 setuid(getuid());
530 malf = fopen(dead, "w");
531 if (malf == NULL) {
532 fprintf(stdout, "mail: cannot open %s\n", dead);
533 fclose(tmpf);
534 return;
535 }
536 copylet(0, malf, ZAP);
537 fclose(malf);
538 fprintf(stdout, "Mail saved in %s\n", dead);
539 }
540 fclose(tmpf);
541}
542
543sendrmt(n, name, rcmd)
544char *name;
545char *rcmd;
546{
547 FILE *rmf, *popen();
548 register char *p;
549 char rsys[64], cmd[64];
550 register local, pid;
551 int sts;
552
553 local = 0;
554 if (index(name, '^')) {
555 while (p = index(name, '^'))
556 *p = '!';
557 if (strncmp(name, "researc", 7)) {
558 strcpy(rsys, "research");
559 if (*name != '!')
560 --name;
561 goto skip;
562 }
563 }
564 if (*name=='!')
565 name++;
566 for(p=rsys; *name!='!'; *p++ = *name++)
567 if (*name=='\0') {
568 local++;
569 break;
570 }
571 *p = '\0';
572 if ((!local && *name=='\0') || (local && *rsys=='\0')) {
573 fprintf(stdout, "null name\n");
574 return(0);
575 }
576skip:
577 if ((pid = fork()) == -1) {
578 fprintf(stderr, "mail: can't create proc for remote\n");
579 return(0);
580 }
581 if (pid) {
582 while (wait(&sts) != pid) {
583 if (wait(&sts)==-1)
584 return(0);
585 }
586 return(!sts);
587 }
588 setuid(getuid());
589 if (local)
590 sprintf(cmd, "%s %s", rcmd, rsys);
591 else {
592 if (index(name+1, '!'))
593 sprintf(cmd, "uux - %s!rmail \\(%s\\)", rsys, name+1);
594 else
595 sprintf(cmd, "uux - %s!rmail %s", rsys, name+1);
596 }
597 if ((rmf=popen(cmd, "w")) == NULL)
598 exit(1);
599 copylet(n, rmf, local ? !strcmp(rcmd, "/bin/mail") ? FORWARD : ORDINARY : REMOTE);
600 pclose(rmf);
601 exit(0);
602}
603
6f70d036 604# ifndef DELIVERMAIL
1c12a606
BJ
605/*
606 * Send mail on the Berkeley network.
607 * Sorry Bill, sendrmt() is so awful we just gave up.
608 */
609
610sendberkmail(n, name, fromaddr)
611 char name[];
612 char fromaddr[];
613{
614 char cmd[200];
615 register FILE *cmdf;
616
617 sprintf(cmd, "%s -h %d -f %s -t %s", RMAIL, hseqno, fromaddr, name);
618 if ((cmdf = popen(cmd, "w")) == NULL) {
619 perror(RMAIL);
620 return(0);
621 }
622 copylet(n, cmdf, ORDINARY);
623 pclose(cmdf);
624 return(9);
625}
6f70d036 626# endif
1c12a606
BJ
627
628usage()
629{
630
631 fprintf(stderr, "Usage: mail [ -f ] people . . .\n");
632}
633
634send(n, name, fromaddr)
635int n;
636char *name;
637char *fromaddr;
638{
639 char file[100];
640 register char *p;
641 register mask;
642 struct passwd *pw, *getpwnam();
643 struct stat statb;
644
6f70d036 645# ifndef DELIVERMAIL
1c12a606
BJ
646 stripfx(LOCNAM1, &name);
647 stripfx(LOCNAM2, &name);
648 stripfx(LOCNAM3, &name);
649 stripfx(LOCNAM4, &name);
650 if(*name == ':')name++; /* skip colon in to-name */
6f70d036 651 for(p=name; *p!=':' && *p!='!' && *p!='^' &&*p!='\0'; p++);
1c12a606
BJ
652 /* if(*p == ':') return(sendrmt(n, name, RMAIL)); */
653 if (*p == ':')
654 return(sendberkmail(n, name, fromaddr));
6f70d036
EA
655 else if (*p=='\0' && strcmp(name, "msgs") == 0)
656 return(sendrmt(n, "-s", "/usr/ucb/msgs"));
657# endif
1c12a606
BJ
658 for(p=name; *p!='!'&&*p!='^' &&*p!='\0'; p++)
659 ;
660 if (*p == '!'|| *p=='^')
661 return(sendrmt(n, name, 0));
662 if ((pw = getpwnam(name)) == NULL) {
663 fprintf(stdout, "mail: can't send to %s\n", name);
664 return(0);
665 }
666 cat(file, maildir, name);
667 if (stat(file, &statb) >= 0 && (statb.st_mode & S_IFMT) == S_IFDIR) {
668 strcat(file, "/");
669 strcat(file, name);
670 }
671 mask = umask(MAILMODE);
672 malf = fopen(file, "a");
673 umask(mask);
674 if (malf == NULL) {
675 fprintf(stdout, "mail: cannot append to %s\n", file);
676 return(0);
677 }
678 lock(file);
679 chown(file, pw->pw_uid, pw->pw_gid);
680 copylet(n, malf, ORDINARY);
681 fclose(malf);
682 unlock();
683 return(1);
684}
685
686delete(i)
687{
688 setsig(i, delete);
689 fprintf(stderr, "\n");
690 if(delflg)
691 longjmp(sjbuf, 1);
692 done();
693}
694
695/*
696 * Lock the specified mail file by setting the file mailfile.lock.
697 * We must, of course, be careful to unlink the lock file by a call
698 * to unlock before we stop. The algorithm used here is to see if
699 * the lock exists, and if it does, to check its modify time. If it
700 * is older than 30 seconds, we assume error and set our own file.
701 * Otherwise, we wait for 5 seconds and try again.
702 */
703
704char *maillock = ".lock"; /* Lock suffix for mailname */
705char *lockname = "/usr/spool/mail/tmXXXXXX";
706char locktmp[30]; /* Usable lock temporary */
707char curlock[50]; /* Last used name of lock */
708int locked; /* To note that we locked it */
709
710lock(file)
711char *file;
712{
713 register int f;
714 struct stat sbuf;
715 long curtime;
716 int statfailed;
717
718 if (locked || flgf)
719 return(0);
720 strcpy(curlock, file);
721 strcat(curlock, maillock);
722 strcpy(locktmp, lockname);
723 mktemp(locktmp);
724 unlink(locktmp);
725 statfailed = 0;
726 for (;;) {
727 f = lock1(locktmp, curlock);
728 if (f == 0) {
729 locked = 1;
730 return(0);
731 }
732 if (stat(curlock, &sbuf) < 0) {
733 if (statfailed++ > 5)
734 return(-1);
735 sleep(5);
736 continue;
737 }
738 statfailed = 0;
739 time(&curtime);
740 if (curtime < sbuf.st_ctime + 30) {
741 sleep(5);
742 continue;
743 }
744 unlink(curlock);
745 }
746}
747
748/*
749 * Remove the mail lock, and note that we no longer
750 * have it locked.
751 */
752
753unlock()
754{
755
756 unlink(curlock);
757 locked = 0;
758}
759
760/*
761 * Attempt to set the lock by creating the temporary file,
762 * then doing a link/unlink. If it fails, return -1 else 0
763 */
764
765lock1(tempfile, name)
766 char tempfile[], name[];
767{
768 register int fd;
769
770 fd = creat(tempfile, 0);
771 if (fd < 0)
772 return(-1);
773 close(fd);
774 if (link(tempfile, name) < 0) {
775 unlink(tempfile);
776 return(-1);
777 }
778 unlink(tempfile);
779 return(0);
780}
781
782done()
783{
784 if(locked)
785 unlock();
786 unlink(lettmp);
787 unlink(locktmp);
788 exit(error);
789}
790
791cat(to, from1, from2)
792char *to, *from1, *from2;
793{
794 int i, j;
795
796 j = 0;
797 for (i=0; from1[i]; i++)
798 to[j++] = from1[i];
799 for (i=0; from2[i]; i++)
800 to[j++] = from2[i];
801 to[j] = 0;
802}
803
804char *getarg(s, p) /* copy p... into s, update p */
805register char *s, *p;
806{
807 while (*p == ' ' || *p == '\t')
808 p++;
809 if (*p == '\n' || *p == '\0')
810 return(NULL);
811 while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
812 *s++ = *p++;
813 *s = '\0';
814 return(p);
815}
6f70d036 816# ifndef DELIVERMAIL
1c12a606
BJ
817/*
818 stripfx(prefix string, pointer to string)
819
820 takes a ptr to string and compares it to prefix string.
821 may be called multiple times
822*/
823stripfx(pfx, name)
824 char *pfx;
825 char **name;
826{
827 register char *cp = *name;
828
829 while (*pfx && (*cp == *pfx || *cp == toupper(*pfx)))
830 cp++, pfx++;
831 if (*cp != ':' || *pfx != 0)
832 return;
833 *name = cp;
834}
6f70d036 835# endif