Bell 32V development
[unix-history] / usr / src / cmd / mail.c
CommitLineData
999bd51d
TL
1#include <stdio.h>
2#include <pwd.h>
3#include <utmp.h>
4#include <signal.h>
5#include <sys/types.h>
6#include <sys/stat.h>
7#include <setjmp.h>
8
9/*copylet flags */
10 /*remote mail, add rmtmsg */
11#define REMOTE 1
12 /* zap header and trailing empty line */
13#define ZAP 3
14#define ORDINARY 2
15#define FORWARD 4
16#define LSIZE 256
17#define MAXLET 300 /* maximum number of letters */
18#define MAILMODE (~0644) /* mode of created mail */
19
20char line[LSIZE];
21char resp[LSIZE];
22struct let {
23 long adr;
24 char change;
25} let[MAXLET];
26int nlet = 0;
27char lfil[50];
28long iop, time();
29char lettmp[] = "/tmp/maXXXXX";
30char maildir[] = "/usr/spool/mail/";
31char mailfile[] = "/usr/spool/mail/xxxxxxxxxxxxxxxxxxxxxxx";
32char dead[] = "dead.letter";
33char *rmtmsg = " remote from vax135\n"; /*LOCAL*/
34char *thissys = "vax135"; /*LOCAL*/
35char forwmsg[] = " forwarded\n";
36char *curlock;
37int lockerror;
38FILE *tmpf;
39FILE *malf;
40char *my_name;
41char *getlogin();
42struct passwd *getpwuid();
43int error;
44int locked;
45int changed;
46int forward;
47char from[] = "From ";
48long ftell();
49int delete();
50char *ctime();
51int flgf;
52int flgp;
53int delflg = 1;
54jmp_buf sjbuf;
55
56main(argc, argv)
57char **argv;
58{
59 register i;
60 char sobuf[BUFSIZ];
61
62 setbuf(stdout, sobuf);
63 mktemp(lettmp);
64 unlink(lettmp);
65 my_name = getlogin();
66 if (my_name == NULL) {
67 struct passwd *pwent;
68 pwent = getpwuid(getuid());
69 if (pwent==NULL)
70 my_name = "???";
71 else
72 my_name = pwent->pw_name;
73 }
74 if(setjmp(sjbuf)) done();
75 for (i=0; i<20; i++)
76 setsig(i, delete);
77 tmpf = fopen(lettmp, "w");
78 if (tmpf == NULL) {
79 fprintf(stderr, "mail: cannot open %s for writing\n", lettmp);
80 done();
81 }
82 if (argv[0][0] != 'r' && /* no favors for rmail*/
83 (argc == 1 || argv[1][0] == '-'))
84 printmail(argc, argv);
85 else
86 sendmail(argc, argv);
87 done();
88}
89
90setsig(i, f)
91int i;
92int (*f)();
93{
94 if(signal(i, SIG_IGN)!=SIG_IGN)
95 signal(i, f);
96}
97
98printmail(argc, argv)
99char **argv;
100{
101 int flg, i, j, print;
102 char *p, *getarg();
103
104 setuid(getuid());
105 cat(mailfile, maildir, my_name);
106 for (; argc>1; argv++, argc--) {
107 if (argv[1][0]=='-') {
108 if (argv[1][1]=='q')
109 delflg = 0;
110 else if (argv[1][1]=='p') {
111 flgp++;
112 delflg = 0;
113 } else if (argv[1][1]=='f') {
114 if (argc>=3) {
115 strcpy(mailfile, argv[2]);
116 argv++;
117 argc--;
118 }
119 } else if (argv[1][1]=='r') {
120 forward = 1;
121 } else {
122 fprintf(stderr, "mail: unknown option %c\n", argv[1][1]);
123 done();
124 }
125 } else
126 break;
127 }
128 malf = fopen(mailfile, "r");
129 if (malf == NULL) {
130 fprintf(stdout, "No mail.\n");
131 return;
132 }
133 lock(mailfile);
134 copymt(malf, tmpf);
135 fclose(malf);
136 fclose(tmpf);
137 unlock();
138 tmpf = fopen(lettmp, "r");
139
140 changed = 0;
141 print = 1;
142 for (i = 0; i < nlet; ) {
143 j = forward ? i : nlet - i - 1;
144 if(setjmp(sjbuf)) {
145 print=0;
146 } else {
147 if (print)
148 copylet(j, stdout, ORDINARY);
149 print = 1;
150 }
151 if (flgp) {
152 i++;
153 continue;
154 }
155 setjmp(sjbuf);
156 fprintf(stdout, "? ");
157 fflush(stdout);
158 if (fgets(resp, LSIZE, stdin) == NULL)
159 break;
160 switch (resp[0]) {
161
162 default:
163 fprintf(stderr, "usage\n");
164 case '?':
165 print = 0;
166 fprintf(stderr, "q\tquit\n");
167 fprintf(stderr, "x\texit without changing mail\n");
168 fprintf(stderr, "p\tprint\n");
169 fprintf(stderr, "s[file]\tsave (default mbox)\n");
170 fprintf(stderr, "w[file]\tsame without header\n");
171 fprintf(stderr, "-\tprint previous\n");
172 fprintf(stderr, "d\tdelete\n");
173 fprintf(stderr, "+\tnext (no delete)\n");
174 fprintf(stderr, "m user\tmail to user\n");
175 fprintf(stderr, "! cmd\texecute cmd\n");
176 break;
177
178 case '+':
179 case 'n':
180 case '\n':
181 i++;
182 break;
183 case 'x':
184 changed = 0;
185 case 'q':
186 goto donep;
187 case 'p':
188 break;
189 case '^':
190 case '-':
191 if (--i < 0)
192 i = 0;
193 break;
194 case 'y':
195 case 'w':
196 case 's':
197 flg = 0;
198 if (resp[1] != '\n' && resp[1] != ' ') {
199 printf("illegal\n");
200 flg++;
201 print = 0;
202 continue;
203 }
204 if (resp[1] == '\n' || resp[1] == '\0')
205 cat(resp+1, "mbox", "");
206 for (p = resp+1; (p = getarg(lfil, p)) != NULL; ) {
207 malf = fopen(lfil, "a");
208 if (malf == NULL) {
209 fprintf(stdout, "mail: cannot append to %s\n", lfil);
210 flg++;
211 continue;
212 }
213 copylet(j, malf, resp[0]=='w'? ZAP: ORDINARY);
214 fclose(malf);
215 }
216 if (flg)
217 print = 0;
218 else {
219 let[j].change = 'd';
220 changed++;
221 i++;
222 }
223 break;
224 case 'm':
225 flg = 0;
226 if (resp[1] == '\n' || resp[1] == '\0') {
227 i++;
228 continue;
229 }
230 if (resp[1] != ' ') {
231 printf("invalid command\n");
232 flg++;
233 print = 0;
234 continue;
235 }
236 for (p = resp+1; (p = getarg(lfil, p)) != NULL; )
237 if (!sendrmt(j, lfil)) /* couldn't send it */
238 flg++;
239 if (flg)
240 print = 0;
241 else {
242 let[j].change = 'd';
243 changed++;
244 i++;
245 }
246 break;
247 case '!':
248 system(resp+1);
249 printf("!\n");
250 print = 0;
251 break;
252 case 'd':
253 let[j].change = 'd';
254 changed++;
255 i++;
256 if (resp[1] == 'q')
257 goto donep;
258 break;
259 }
260 }
261 donep:
262 if (changed)
263 copyback();
264}
265
266copyback() /* copy temp or whatever back to /usr/spool/mail */
267{
268 register i, n, c;
269 int new = 0;
270 struct stat stbuf;
271
272 signal(SIGINT, SIG_IGN);
273 signal(SIGHUP, SIG_IGN);
274 signal(SIGQUIT, SIG_IGN);
275 lock(mailfile);
276 stat(mailfile, &stbuf);
277 if (stbuf.st_size != let[nlet].adr) { /* new mail has arrived */
278 malf = fopen(mailfile, "r");
279 if (malf == NULL) {
280 fprintf(stdout, "mail: can't re-read %s\n", mailfile);
281 done();
282 }
283 fseek(malf, let[nlet].adr, 0);
284 fclose(tmpf);
285 tmpf = fopen(lettmp, "a");
286 fseek(tmpf, let[nlet].adr, 0);
287 while ((c = fgetc(malf)) != EOF)
288 fputc(c, tmpf);
289 fclose(malf);
290 fclose(tmpf);
291 tmpf = fopen(lettmp, "r");
292 let[++nlet].adr = stbuf.st_size;
293 new = 1;
294 }
295 malf = fopen(mailfile, "w");
296 if (malf == NULL) {
297 fprintf(stderr, "mail: can't rewrite %s\n", lfil);
298 done();
299 }
300 n = 0;
301 for (i = 0; i < nlet; i++)
302 if (let[i].change != 'd') {
303 copylet(i, malf, ORDINARY);
304 n++;
305 }
306 fclose(malf);
307 if (new)
308 fprintf(stdout, "new mail arrived\n");
309 unlock();
310}
311
312copymt(f1, f2) /* copy mail (f1) to temp (f2) */
313FILE *f1, *f2;
314{
315 long nextadr;
316
317 nlet = nextadr = 0;
318 let[0].adr = 0;
319 while (fgets(line, LSIZE, f1) != NULL) {
320 if (isfrom(line))
321 let[nlet++].adr = nextadr;
322 nextadr += strlen(line);
323 fputs(line, f2);
324 }
325 let[nlet].adr = nextadr; /* last plus 1 */
326}
327
328copylet(n, f, type) FILE *f;
329{ int ch, k;
330 fseek(tmpf, let[n].adr, 0);
331 k = let[n+1].adr - let[n].adr;
332 while(k-- > 1 && (ch=fgetc(tmpf))!='\n')
333 if(type!=ZAP) fputc(ch,f);
334 if(type==REMOTE)
335 fprintf(f, rmtmsg);
336 else if (type==FORWARD)
337 fprintf(f, forwmsg);
338 else if(type==ORDINARY)
339 fputc(ch,f);
340 while(k-->1)
341 fputc(ch=fgetc(tmpf), f);
342 if(type!=ZAP || ch!= '\n')
343 fputc(fgetc(tmpf), f);
344}
345
346isfrom(lp)
347register char *lp;
348{
349 register char *p;
350
351 for (p = from; *p; )
352 if (*lp++ != *p++)
353 return(0);
354 return(1);
355}
356
357sendmail(argc, argv)
358char **argv;
359{
360
361 time(&iop);
362 fprintf(tmpf, "%s%s %s", from, my_name, ctime(&iop));
363 iop = ftell(tmpf);
364 flgf = 1;
365 while (fgets(line, LSIZE, stdin) != NULL) {
366 if (line[0] == '.' && line[1] == '\n')
367 break;
368 if (isfrom(line))
369 fputs(">", tmpf);
370 fputs(line, tmpf);
371 flgf = 0;
372 }
373 fputs("\n", tmpf);
374 nlet = 1;
375 let[0].adr = 0;
376 let[1].adr = ftell(tmpf);
377 fclose(tmpf);
378 if (flgf)
379 return;
380 tmpf = fopen(lettmp, "r");
381 if (tmpf == NULL) {
382 fprintf(stderr, "mail: cannot reopen %s for reading\n", lettmp);
383 return;
384 }
385 while (--argc > 0)
386 if (!send(0, *++argv)) /* couldn't send to him */
387 error++;
388 if (error) {
389 setuid(getuid());
390 malf = fopen(dead, "w");
391 if (malf == NULL) {
392 fprintf(stdout, "mail: cannot open %s\n", dead);
393 fclose(tmpf);
394 return;
395 }
396 copylet(0, malf, ZAP);
397 fclose(malf);
398 fprintf(stdout, "Mail saved in %s\n", dead);
399 }
400 fclose(tmpf);
401}
402
403sendrmt(n, name)
404char *name;
405{
406 FILE *rmf, *popen();
407 register char *p;
408 char rsys[64], cmd[64];
409 register local, pid;
410 int sts;
411
412 local = 0;
413 if (*name=='!')
414 name++;
415 for(p=rsys; *name!='!'; *p++ = *name++)
416 if (*name=='\0') {
417 local++;
418 break;
419 }
420 *p = '\0';
421 if ((!local && *name=='\0') || (local && *rsys=='\0')) {
422 fprintf(stdout, "null name\n");
423 return(0);
424 }
425 if ((pid = fork()) == -1) {
426 fprintf(stderr, "mail: can't create proc for remote\n");
427 return(0);
428 }
429 if (pid) {
430 while (wait(&sts) != pid) {
431 if (wait(&sts)==-1)
432 return(0);
433 }
434 return(!sts);
435 }
436 setuid(getuid());
437 if (local)
438 sprintf(cmd, "mail %s", rsys);
439 else {
440 if (index(name+1, '!'))
441 sprintf(cmd, "uux - %s!rmail \\(%s\\)", rsys, name+1);
442 else
443 sprintf(cmd, "uux - %s!rmail %s", rsys, name+1);
444 }
445 if ((rmf=popen(cmd, "w")) == NULL)
446 exit(1);
447 copylet(n, rmf, local? FORWARD: REMOTE);
448 pclose(rmf);
449 exit(0);
450}
451
452send(n, name) /* send letter n to name */
453int n;
454char *name;
455{
456 char file[50];
457 register char *p;
458 register mask;
459 struct passwd *pw, *getpwnam();
460
461 for(p=name; *p!='!' &&*p!='\0'; p++)
462 ;
463 if (*p == '!')
464 return(sendrmt(n, name));
465 if ((pw = getpwnam(name)) == NULL) {
466 fprintf(stdout, "mail: can't send to %s\n", name);
467 return(0);
468 }
469 cat(file, maildir, name);
470 mask = umask(MAILMODE);
471 malf = fopen(file, "a");
472 umask(mask);
473 if (malf == NULL) {
474 fprintf(stdout, "mail: cannot append to %s\n", file);
475 return(0);
476 }
477 lock(file);
478 chown(file, pw->pw_uid, pw->pw_gid);
479 copylet(n, malf, ORDINARY);
480 fclose(malf);
481 unlock();
482 return(1);
483}
484
485delete(i)
486{
487 setsig(i, delete);
488 fprintf(stderr, "\n");
489 if(delflg)
490 longjmp(sjbuf, 1);
491 done();
492}
493
494done()
495{
496 if(!lockerror)
497 unlock();
498 unlink(lettmp);
499 exit(error+lockerror);
500}
501
502lock(file)
503char *file;
504{
505 struct stat stbuf;
506
507 if (locked || flgf)
508 return;
509 if (stat(file, &stbuf)<0)
510 return;
511 if (stbuf.st_mode&01) { /* user x bit is the lock */
512 if (stbuf.st_ctime+60 >= time((long *)0)) {
513 fprintf(stderr, "%s busy; try again in a minute\n", file);
514 lockerror++;
515 done();
516 }
517 }
518 locked = stbuf.st_mode & ~01;
519 curlock = file;
520 chmod(file, stbuf.st_mode|01);
521}
522
523unlock()
524{
525 if (locked)
526 chmod(curlock, locked);
527 locked = 0;
528}
529
530cat(to, from1, from2)
531char *to, *from1, *from2;
532{
533 int i, j;
534
535 j = 0;
536 for (i=0; from1[i]; i++)
537 to[j++] = from1[i];
538 for (i=0; from2[i]; i++)
539 to[j++] = from2[i];
540 to[j] = 0;
541}
542
543char *getarg(s, p) /* copy p... into s, update p */
544register char *s, *p;
545{
546 while (*p == ' ' || *p == '\t')
547 p++;
548 if (*p == '\n' || *p == '\0')
549 return(NULL);
550 while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
551 *s++ = *p++;
552 *s = '\0';
553 return(p);
554}