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