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