use mkstemp so people can't substitute a symlink for the temp file.
[unix-history] / usr / src / libexec / mail.local / mail.local.c
CommitLineData
205a2d85 1#ifndef lint
5e3e9105 2static char sccsid[] = "@(#)mail.local.c 4.24 (Berkeley) %G%";
205a2d85
SL
3#endif
4
4e161d3b
RC
5#include <sys/types.h>
6#include <sys/stat.h>
7#include <sys/file.h>
8
1c12a606
BJ
9#include <ctype.h>
10#include <stdio.h>
11#include <pwd.h>
12#include <utmp.h>
13#include <signal.h>
1c12a606 14#include <setjmp.h>
1c12a606
BJ
15#include <sysexits.h>
16
ac57be53 17#define SENDMAIL "/usr/lib/sendmail"
1c12a606 18
4e161d3b
RC
19 /* copylet flags */
20#define REMOTE 1 /* remote mail, add rmtmsg */
21#define ORDINARY 2
22#define ZAP 3 /* zap header and trailing empty line */
23#define FORWARD 4
1c12a606 24
4e161d3b
RC
25#define LSIZE 256
26#define MAXLET 300 /* maximum number of letters */
27#define MAILMODE 0600 /* mode of created mail */
1c12a606
BJ
28
29char line[LSIZE];
30char resp[LSIZE];
31struct let {
32 long adr;
33 char change;
34} let[MAXLET];
35int nlet = 0;
36char lfil[50];
37long iop, time();
38char *getenv();
39char *index();
40char lettmp[] = "/tmp/maXXXXX";
41char maildir[] = "/usr/spool/mail/";
42char mailfile[] = "/usr/spool/mail/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
43char dead[] = "dead.letter";
1c12a606
BJ
44char forwmsg[] = " forwarded\n";
45FILE *tmpf;
46FILE *malf;
47char *my_name;
48char *getlogin();
1c12a606
BJ
49int error;
50int changed;
51int forward;
52char from[] = "From ";
53long ftell();
4e161d3b 54int delex();
1c12a606
BJ
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;
1c12a606 67
1c12a606 68 my_name = getlogin();
4e161d3b 69 if (my_name == NULL || *my_name == '\0') {
1c12a606
BJ
70 struct passwd *pwent;
71 pwent = getpwuid(getuid());
72 if (pwent==NULL)
73 my_name = "???";
74 else
75 my_name = pwent->pw_name;
76 }
4e161d3b 77 if (setjmp(sjbuf))
1c12a606 78 done();
4e161d3b
RC
79 for (i=SIGHUP; i<=SIGTERM; i++)
80 setsig(i, delex);
5e3e9105
RC
81 i = mkstemp(lettmp);
82 tmpf = fdopen(i, "r+w");
83 if (i < 0 || tmpf == NULL)
4e161d3b
RC
84 panic("mail: %s: cannot open for writing", lettmp);
85 /*
86 * This protects against others reading mail from temp file and
87 * if we exit, the file will be deleted already.
88 */
89 unlink(lettmp);
1c12a606
BJ
90 if (argv[0][0] == 'r')
91 rmail++;
92 if (argv[0][0] != 'r' && /* no favors for rmail*/
93 (argc == 1 || argv[1][0] == '-' && !any(argv[1][1], "rhd")))
94 printmail(argc, argv);
95 else
f914d8ba 96 bulkmail(argc, argv);
1c12a606
BJ
97 done();
98}
99
100setsig(i, f)
101int i;
102int (*f)();
103{
4e161d3b 104 if (signal(i, SIG_IGN) != SIG_IGN)
1c12a606
BJ
105 signal(i, f);
106}
107
108any(c, str)
109 register int c;
110 register char *str;
111{
112
113 while (*str)
114 if (c == *str++)
115 return(1);
116 return(0);
117}
118
119printmail(argc, argv)
4e161d3b 120 char **argv;
1c12a606
BJ
121{
122 int flg, i, j, print;
123 char *p, *getarg();
124 struct stat statb;
125
126 setuid(getuid());
127 cat(mailfile, maildir, my_name);
4e161d3b 128#ifdef notdef
1c12a606
BJ
129 if (stat(mailfile, &statb) >= 0
130 && (statb.st_mode & S_IFMT) == S_IFDIR) {
131 strcat(mailfile, "/");
132 strcat(mailfile, my_name);
133 }
4e161d3b
RC
134#endif
135 for (; argc > 1; argv++, argc--) {
136 if (argv[1][0] != '-')
137 break;
138 switch (argv[1][1]) {
139
140 case 'p':
141 flgp++;
142 /* fall thru... */
143 case 'q':
144 delflg = 0;
145 break;
146
147 case 'f':
148 if (argc >= 3) {
149 strcpy(mailfile, argv[2]);
150 argv++, argc--;
1c12a606 151 }
1c12a606 152 break;
4e161d3b
RC
153
154 case 'b':
155 forward = 1;
156 break;
157
158 default:
159 panic("unknown option %c", argv[1][1]);
160 /*NOTREACHED*/
161 }
1c12a606
BJ
162 }
163 malf = fopen(mailfile, "r");
164 if (malf == NULL) {
4e161d3b 165 printf("No mail.\n");
1c12a606
BJ
166 return;
167 }
4e161d3b 168 flock(fileno(malf), LOCK_SH);
1c12a606 169 copymt(malf, tmpf);
4e161d3b
RC
170 fclose(malf); /* implicit unlock */
171 fseek(tmpf, 0, L_SET);
1c12a606
BJ
172
173 changed = 0;
174 print = 1;
175 for (i = 0; i < nlet; ) {
176 j = forward ? i : nlet - i - 1;
4e161d3b
RC
177 if (setjmp(sjbuf)) {
178 print = 0;
1c12a606
BJ
179 } else {
180 if (print)
181 copylet(j, stdout, ORDINARY);
182 print = 1;
183 }
184 if (flgp) {
185 i++;
186 continue;
187 }
188 setjmp(sjbuf);
4e161d3b 189 fputs("? ", stdout);
1c12a606
BJ
190 fflush(stdout);
191 if (fgets(resp, LSIZE, stdin) == NULL)
192 break;
193 switch (resp[0]) {
194
195 default:
4e161d3b 196 printf("usage\n");
1c12a606
BJ
197 case '?':
198 print = 0;
4e161d3b
RC
199 printf("q\tquit\n");
200 printf("x\texit without changing mail\n");
201 printf("p\tprint\n");
202 printf("s[file]\tsave (default mbox)\n");
203 printf("w[file]\tsame without header\n");
204 printf("-\tprint previous\n");
205 printf("d\tdelete\n");
206 printf("+\tnext (no delete)\n");
207 printf("m user\tmail to user\n");
208 printf("! cmd\texecute cmd\n");
1c12a606
BJ
209 break;
210
211 case '+':
212 case 'n':
213 case '\n':
214 i++;
215 break;
216 case 'x':
217 changed = 0;
218 case 'q':
219 goto donep;
220 case 'p':
221 break;
222 case '^':
223 case '-':
224 if (--i < 0)
225 i = 0;
226 break;
227 case 'y':
228 case 'w':
229 case 's':
230 flg = 0;
231 if (resp[1] != '\n' && resp[1] != ' ') {
232 printf("illegal\n");
233 flg++;
234 print = 0;
235 continue;
236 }
237 if (resp[1] == '\n' || resp[1] == '\0') {
238 p = getenv("HOME");
4e161d3b 239 if (p != 0)
1c12a606
BJ
240 cat(resp+1, p, "/mbox");
241 else
242 cat(resp+1, "", "mbox");
243 }
244 for (p = resp+1; (p = getarg(lfil, p)) != NULL; ) {
245 malf = fopen(lfil, "a");
246 if (malf == NULL) {
4e161d3b
RC
247 printf("mail: %s: cannot append\n",
248 lfil);
1c12a606
BJ
249 flg++;
250 continue;
251 }
252 copylet(j, malf, resp[0]=='w'? ZAP: ORDINARY);
253 fclose(malf);
254 }
255 if (flg)
256 print = 0;
257 else {
258 let[j].change = 'd';
259 changed++;
260 i++;
261 }
262 break;
263 case 'm':
264 flg = 0;
265 if (resp[1] == '\n' || resp[1] == '\0') {
266 i++;
267 continue;
268 }
269 if (resp[1] != ' ') {
270 printf("invalid command\n");
271 flg++;
272 print = 0;
273 continue;
274 }
275 for (p = resp+1; (p = getarg(lfil, p)) != NULL; )
69a0e58c 276 if (!sendmail(j, lfil, my_name))
1c12a606
BJ
277 flg++;
278 if (flg)
279 print = 0;
280 else {
281 let[j].change = 'd';
282 changed++;
283 i++;
284 }
285 break;
286 case '!':
287 system(resp+1);
288 printf("!\n");
289 print = 0;
290 break;
291 case 'd':
292 let[j].change = 'd';
293 changed++;
294 i++;
295 if (resp[1] == 'q')
296 goto donep;
297 break;
298 }
299 }
300 donep:
301 if (changed)
302 copyback();
303}
304
4e161d3b
RC
305/* copy temp or whatever back to /usr/spool/mail */
306copyback()
1c12a606 307{
4e161d3b
RC
308 register i, c;
309 int fd, new = 0, oldmask;
1c12a606
BJ
310 struct stat stbuf;
311
4e161d3b
RC
312#define mask(s) (1 << ((s) - 1))
313 oldmask = sigblock(mask(SIGINT)|mask(SIGHUP)|mask(SIGQUIT));
314#undef mask
315 fd = open(mailfile, O_RDWR | O_CREAT, MAILMODE);
316 if (fd >= 0) {
317 flock(fd, LOCK_EX);
318 malf = fdopen(fd, "r+w");
319 }
320 if (fd < 0 || malf == NULL)
321 panic("can't rewrite %s", lfil);
322 fstat(fd, &stbuf);
1c12a606 323 if (stbuf.st_size != let[nlet].adr) { /* new mail has arrived */
4e161d3b
RC
324 fseek(malf, let[nlet].adr, L_SET);
325 fseek(tmpf, let[nlet].adr, L_SET);
326 while ((c = getc(malf)) != EOF)
327 putc(c, tmpf);
1c12a606
BJ
328 let[++nlet].adr = stbuf.st_size;
329 new = 1;
4e161d3b 330 fseek(malf, 0, L_SET);
1c12a606 331 }
4e161d3b 332 ftruncate(fd, 0);
1c12a606 333 for (i = 0; i < nlet; i++)
4e161d3b 334 if (let[i].change != 'd')
1c12a606 335 copylet(i, malf, ORDINARY);
4e161d3b 336 fclose(malf); /* implict unlock */
1c12a606 337 if (new)
4e161d3b
RC
338 printf("New mail has arrived.\n");
339 sigsetmask(oldmask);
1c12a606
BJ
340}
341
4e161d3b
RC
342/* copy mail (f1) to temp (f2) */
343copymt(f1, f2)
344 FILE *f1, *f2;
1c12a606
BJ
345{
346 long nextadr;
347
348 nlet = nextadr = 0;
349 let[0].adr = 0;
350 while (fgets(line, LSIZE, f1) != NULL) {
351 if (isfrom(line))
352 let[nlet++].adr = nextadr;
353 nextadr += strlen(line);
354 fputs(line, f2);
355 }
356 let[nlet].adr = nextadr; /* last plus 1 */
357}
358
13bd902d
EA
359copylet(n, f, type)
360 FILE *f;
361{
362 int ch;
363 long k;
4e161d3b 364 char hostname[32];
13bd902d 365
4e161d3b 366 fseek(tmpf, let[n].adr, L_SET);
1c12a606 367 k = let[n+1].adr - let[n].adr;
4e161d3b
RC
368 while (k-- > 1 && (ch = getc(tmpf)) != '\n')
369 if (type != ZAP)
370 putc(ch, f);
371 switch (type) {
372
373 case REMOTE:
cf288731
BJ
374 gethostname(hostname, sizeof (hostname));
375 fprintf(f, " remote from %s\n", hostname);
4e161d3b
RC
376 break;
377
378 case FORWARD:
1c12a606 379 fprintf(f, forwmsg);
4e161d3b
RC
380 break;
381
382 case ORDINARY:
383 putc(ch, f);
384 break;
385
69a0e58c
RC
386 case ZAP:
387 break;
388
4e161d3b
RC
389 default:
390 panic("Bad letter type %d to copylet.", type);
391 }
392 while (k-- > 1) {
393 ch = getc(tmpf);
394 putc(ch, f);
395 }
396 if (type != ZAP || ch != '\n')
397 putc(getc(tmpf), f);
1c12a606
BJ
398}
399
400isfrom(lp)
401register char *lp;
402{
403 register char *p;
404
405 for (p = from; *p; )
406 if (*lp++ != *p++)
407 return(0);
408 return(1);
409}
410
f914d8ba 411bulkmail(argc, argv)
1c12a606
BJ
412char **argv;
413{
414 char truename[100];
415 int first;
416 register char *cp;
417 int gaver = 0;
1c12a606
BJ
418 char *newargv[1000];
419 register char **ap;
420 register char **vp;
421 int dflag;
422
423 dflag = 0;
4e161d3b 424 if (argc < 1) {
1c12a606 425 fprintf(stderr, "puke\n");
4e161d3b
RC
426 return;
427 }
1c12a606 428 for (vp = argv, ap = newargv + 1; (*ap = *vp++) != 0; ap++)
1c12a606
BJ
429 if (ap[0][0] == '-' && ap[0][1] == 'd')
430 dflag++;
4e161d3b 431 if (!dflag) {
ac57be53 432 /* give it to sendmail, rah rah! */
1c12a606
BJ
433 unlink(lettmp);
434 ap = newargv+1;
435 if (rmail)
436 *ap-- = "-s";
ac57be53 437 *ap = "-sendmail";
608f9cfc 438 setuid(getuid());
ac57be53
EA
439 execv(SENDMAIL, ap);
440 perror(SENDMAIL);
1c12a606
BJ
441 exit(EX_UNAVAILABLE);
442 }
1c12a606
BJ
443
444 truename[0] = 0;
445 line[0] = '\0';
446
447 /*
448 * When we fall out of this, argv[1] should be first name,
449 * argc should be number of names + 1.
450 */
451
452 while (argc > 1 && *argv[1] == '-') {
453 cp = *++argv;
454 argc--;
455 switch (cp[1]) {
456 case 'r':
4e161d3b 457 if (argc <= 1)
1c12a606 458 usage();
413d039a
EA
459 if (strcmp(my_name, "root") == 0 ||
460 strcmp(my_name, "daemon") == 0 ||
461 strcmp(my_name, "network") == 0 ||
462 strcmp(my_name, "uucp")) {
463 gaver++;
464 strcpy(truename, argv[1]);
465 fgets(line, LSIZE, stdin);
466 if (strncmp("From", line, 4) == 0)
467 line[0] = '\0';
8bc5e57f 468 }
1c12a606
BJ
469 argv++;
470 argc--;
471 break;
472
473 case 'h':
4e161d3b 474 if (argc <= 1)
1c12a606 475 usage();
1c12a606
BJ
476 hseqno = atoi(argv[1]);
477 argv++;
478 argc--;
479 break;
480
1c12a606
BJ
481 case 'd':
482 break;
1c12a606
BJ
483
484 default:
485 usage();
1c12a606
BJ
486 }
487 }
4e161d3b 488 if (argc <= 1)
1c12a606 489 usage();
1c12a606
BJ
490 if (gaver == 0)
491 strcpy(truename, my_name);
1c12a606
BJ
492 time(&iop);
493 fprintf(tmpf, "%s%s %s", from, truename, ctime(&iop));
494 iop = ftell(tmpf);
4e161d3b
RC
495 flgf = first = 1;
496 for (;;) {
497 if (first) {
498 first = 0;
499 if (*line == '\0' && fgets(line, LSIZE, stdin) == NULL)
500 break;
501 } else {
502 if (fgets(line, LSIZE, stdin) == NULL)
503 break;
504 }
505 if (*line == '.' && line[1] == '\n' && isatty(fileno(stdin)))
1c12a606
BJ
506 break;
507 if (isfrom(line))
4e161d3b 508 putc('>', tmpf);
1c12a606
BJ
509 fputs(line, tmpf);
510 flgf = 0;
511 }
4e161d3b 512 putc('\n', tmpf);
1c12a606
BJ
513 nlet = 1;
514 let[0].adr = 0;
515 let[1].adr = ftell(tmpf);
1c12a606
BJ
516 if (flgf)
517 return;
4e161d3b 518 while (--argc > 0)
f914d8ba 519 if (!sendmail(0, *++argv, truename))
1c12a606 520 error++;
fd64b7dc 521 if (error && safefile(dead)) {
1c12a606
BJ
522 setuid(getuid());
523 malf = fopen(dead, "w");
524 if (malf == NULL) {
4e161d3b 525 printf("mail: cannot open %s\n", dead);
1c12a606
BJ
526 fclose(tmpf);
527 return;
528 }
529 copylet(0, malf, ZAP);
530 fclose(malf);
4e161d3b 531 printf("Mail saved in %s\n", dead);
1c12a606
BJ
532 }
533 fclose(tmpf);
534}
535
69a0e58c 536sendrmt(n, name)
1c12a606 537char *name;
1c12a606
BJ
538{
539 FILE *rmf, *popen();
540 register char *p;
541 char rsys[64], cmd[64];
69a0e58c 542 register pid;
1c12a606
BJ
543 int sts;
544
69a0e58c
RC
545#ifdef notdef
546 if (any('^', name)) {
1c12a606
BJ
547 while (p = index(name, '^'))
548 *p = '!';
549 if (strncmp(name, "researc", 7)) {
550 strcpy(rsys, "research");
551 if (*name != '!')
552 --name;
553 goto skip;
554 }
555 }
69a0e58c
RC
556#endif
557 for (p=rsys; *name!='!'; *p++ = *name++)
558 if (*name=='\0')
559 return(0); /* local address, no '!' */
1c12a606 560 *p = '\0';
69a0e58c 561 if (name[1]=='\0') {
4e161d3b 562 printf("null name\n");
1c12a606
BJ
563 return(0);
564 }
565skip:
566 if ((pid = fork()) == -1) {
567 fprintf(stderr, "mail: can't create proc for remote\n");
568 return(0);
569 }
570 if (pid) {
571 while (wait(&sts) != pid) {
572 if (wait(&sts)==-1)
573 return(0);
574 }
575 return(!sts);
576 }
577 setuid(getuid());
69a0e58c
RC
578 if (any('!', name+1))
579 sprintf(cmd, "uux - %s!rmail \\(%s\\)", rsys, name+1);
580 else
581 sprintf(cmd, "uux - %s!rmail %s", rsys, name+1);
1c12a606
BJ
582 if ((rmf=popen(cmd, "w")) == NULL)
583 exit(1);
69a0e58c 584 copylet(n, rmf, REMOTE);
aa26b6a1 585 exit(pclose(rmf) != 0);
1c12a606
BJ
586}
587
1c12a606
BJ
588usage()
589{
590
591 fprintf(stderr, "Usage: mail [ -f ] people . . .\n");
f2a88515 592 error = EX_USAGE;
4e161d3b 593 done();
1c12a606
BJ
594}
595
f914d8ba 596#include <sys/socket.h>
b1198826 597#include <netinet/in.h>
a341edb7 598#include <netdb.h>
4e161d3b
RC
599
600notifybiff(msg)
601 char *msg;
602{
603 static struct sockaddr_in addr;
604 static int f = -1;
605
606 if (addr.sin_family == 0) {
607 struct hostent *hp = gethostbyname("localhost");
608 struct servent *sp = getservbyname("biff", "udp");
609
610 if (hp && sp) {
611 addr.sin_family = hp->h_addrtype;
612 bcopy(hp->h_addr, &addr.sin_addr, hp->h_length);
613 addr.sin_port = sp->s_port;
614 }
615 }
616 if (addr.sin_family) {
617 if (f < 0)
618 f = socket(AF_INET, SOCK_DGRAM, 0);
619 sendto(f, msg, strlen(msg)+1, 0, &addr, sizeof (addr));
620 }
621}
f914d8ba
BJ
622
623sendmail(n, name, fromaddr)
4e161d3b
RC
624 int n;
625 char *name, *fromaddr;
1c12a606 626{
4e161d3b
RC
627 char file[256];
628 int mask, fd;
629 struct passwd *pw;
630#ifdef notdef
1c12a606 631 struct stat statb;
4e161d3b 632#endif
517d4a63 633 char buf[128];
1c12a606 634
69a0e58c
RC
635 if (*name=='!')
636 name++;
637 if (any('!', name))
638 return (sendrmt(n, name));
1c12a606 639 if ((pw = getpwnam(name)) == NULL) {
4e161d3b 640 printf("mail: can't send to %s\n", name);
1c12a606
BJ
641 return(0);
642 }
643 cat(file, maildir, name);
4e161d3b 644#ifdef notdef
1c12a606
BJ
645 if (stat(file, &statb) >= 0 && (statb.st_mode & S_IFMT) == S_IFDIR) {
646 strcat(file, "/");
647 strcat(file, name);
648 }
4e161d3b 649#endif
fd64b7dc 650 if (!safefile(file))
47fe5c20 651 return(0);
4e161d3b
RC
652 fd = open(file, O_WRONLY | O_CREAT, MAILMODE);
653 if (fd >= 0) {
654 flock(fd, LOCK_EX);
655 malf = fdopen(fd, "a");
1c12a606 656 }
4e161d3b
RC
657 if (fd < 0 || malf == NULL) {
658 close(fd);
659 printf("mail: %s: cannot append\n", file);
660 return(0);
517d4a63 661 }
4e161d3b
RC
662 fchown(fd, pw->pw_uid, pw->pw_gid);
663 sprintf(buf, "%s@%d\n", name, ftell(malf));
1c12a606 664 copylet(n, malf, ORDINARY);
f914d8ba 665 fclose(malf);
4e161d3b 666 notifybiff(buf);
1c12a606
BJ
667 return(1);
668}
669
4e161d3b 670delex(i)
1c12a606 671{
4e161d3b
RC
672 setsig(i, delex);
673 putc('\n', stderr);
674 if (delflg)
1c12a606
BJ
675 longjmp(sjbuf, 1);
676 done();
677}
678
1c12a606
BJ
679done()
680{
4e161d3b 681
1c12a606 682 unlink(lettmp);
1c12a606
BJ
683 exit(error);
684}
685
686cat(to, from1, from2)
4e161d3b 687 char *to, *from1, *from2;
1c12a606 688{
4e161d3b
RC
689 register char *cp, *dp;
690
691 cp = to;
692 for (dp = from1; *cp = *dp++; cp++)
693 ;
694 for (dp = from2; *cp++ = *dp++; )
695 ;
1c12a606
BJ
696}
697
4e161d3b
RC
698/* copy p... into s, update p */
699char *
700getarg(s, p)
701 register char *s, *p;
1c12a606
BJ
702{
703 while (*p == ' ' || *p == '\t')
704 p++;
705 if (*p == '\n' || *p == '\0')
706 return(NULL);
707 while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
708 *s++ = *p++;
709 *s = '\0';
710 return(p);
711}
fd64b7dc
EA
712
713safefile(f)
714 char *f;
715{
716 struct stat statb;
717
718 if (lstat(f, &statb) < 0)
719 return (1);
720 if (statb.st_nlink != 1 || (statb.st_mode & S_IFMT) == S_IFLNK) {
4e161d3b
RC
721 fprintf(stderr,
722 "mail: %s has more than one link or is a symbolic link\n",
723 f);
fd64b7dc
EA
724 return (0);
725 }
726 return (1);
727}
4e161d3b
RC
728
729panic(msg, a1, a2, a3)
730 char *msg;
731{
732
733 fprintf(stderr, "mail: ");
734 fprintf(stderr, msg, a1, a2, a3);
735 fprintf(stderr, "\n");
736 done();
737}