BSD 4_3_Tahoe development
[unix-history] / usr / src / undoc / v6mail.c
CommitLineData
6baee9a1
C
1/*
2 * v6mail
3 */
4#include <sysexits.h>
5
6#include <sys/param.h>
7#include <sys/stat.h>
8#include <sys/dir.h>
9#include <sys/times.h>
10#include <ctype.h>
11#include <errno.h>
12#include <pwd.h>
13#include <signal.h>
14
15char *ctime(), *index(), *rindex(), *ctime(), *strcpy(), *getlogin();
16char *mktemp();
17struct passwd *getpwnam(), *getpwuid();
18time_t time();
19struct utmp *getutmp();
20char *getdate();
21int errno;
22
23#include <stdio.h>
24
25#define MAILMODE 0644
26#define MSGSCMD "/usr/ucb/msgs"
27#define MAILDIR "/usr/spool/mail"
28
29char lettmp[] = "/tmp/MaXXXXX"; /* keep letter before sending it */
30char preptmp[] = "/tmp/mbXXXXX"; /* if prepending msg, use this file */
31int chew; /* if true, strip extra from lines */
32int dflag; /* if true, don't call sendmail */
33char shopcnt[30] = "0"; /* hop count parameter for rmt mail */
34int errs; /* no of errs in sending */
35char deleteonly; /* if true, just delete mailbox */
36char remname[50]; /* if non-empty, from line extra */
37
38main(argc, argv)
39 int argc;
40 char **argv;
41{
42 register int myuid;
43 int delexit();
44 char namebuf[128], *sn = NULL, logindir[60];
45 struct passwd *pwd;
46
47 (void) mktemp(lettmp);
48 (void) mktemp(preptmp);
49 (void) unlink(lettmp);
50 (void) unlink(preptmp);
51 myuid = getuid();
52 logindir[0] = 0;
53 sn = getlogin();
54 if (sn == NULL || *sn == 0 || *sn == ' ') {
55 pwd = getpwuid(myuid); /* will read passwd file */
56 if (pwd != NULL){
57 sn = pwd->pw_name;
58 (void) strcpy(logindir, pwd->pw_dir);
59 }
60 if (sn == NULL) {
61 fprintf(stderr, "Who are you?\n");
62 delexit(EX_OSFILE);
63 }
64 }
65 (void) strcpy(namebuf, sn);
66 if (argc < 2)
67 goto hitit;
68 for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++)
69 switch (argv[0][1]) {
70
71 case 'y':
72 case 'n':
73 argc++, argv--;
74hitit:
75 printmail(argc, argv, namebuf, logindir);
76 delexit(EX_OK);
77
78 case 'r': /* one-arg -r-- -r addr */
79 if (argc < 2)
80 continue;
81 /* ignore -r if not network or root */
82 if (strcmp("network", namebuf) == 0 || myuid == 0 ||
83/*###86 [lint] index arg. 1 used inconsistently v6mail.c(86) :: v6mail.c(244)%%%*/
84/*###86 [lint] index arg. 2 used inconsistently v6mail.c(86) :: v6mail.c(244)%%%*/
85 strcmp("uucp", namebuf) == 0 || index('!', argv[1])) {
86 (void) strcpy(namebuf, argv[1]);
87 chew++; /* eat From lines */
88 }
89 else
90 (void) strcpy(remname, argv[1]);
91 argc--, argv++;
92 continue;
93
94 case 'h': /* hop count - used by network */
95 if (argc < 2)
96 continue;
97 (void) strcpy(shopcnt, argv[1]);
98 argc--, argv++;
99 continue;
100
101 case 'd': /* really deliver this message */
102 dflag++;
103 continue;
104
105 case 'D': /* only delete the invokers mailbox */
106 deleteonly++;
107 goto hitit; /* delete mail box, thats all */
108 }
109 /* if we are already ignoring signals, catch sigint */
110 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
111 (void) signal(SIGINT, delexit);
112 argc++, argv--;
113 bulkmail(argc, argv, namebuf);
114 delexit(EX_OK);
115}
116
117printmail(argc, argv, name, logindir)
118 int argc;
119 char **argv;
120 char *name, *logindir;
121{
122 register int c;
123 FILE *fdin;
124 char sfnmail[60], mbox[120];
125 struct stat stb;
126
127 (void) sprintf(sfnmail, "%s/%s", MAILDIR, name);
128 if (deleteonly) {
129 remove(sfnmail);
130 return;
131 }
132 fdin = fopen(sfnmail, "r");
133 if (fdin < 0 || fstat(fileno(fdin), &stb) < 0 || stb.st_size == 0) {
134 printf("No mail.\n");
135 return;
136 }
137 if (stb.st_nlink > 1) {
138 printf("%s: Too many links.\n", sfnmail);
139 return;
140 }
141 (void) getput(fdin, stdout);
142 (void) fclose(fdin);
143 (void) fflush(stdout);
144 c = 'y';
145 if (argc < 2) {
146 if (isatty(0)) {
147 printf("Save (y or n) ?"); (void) fflush(stdout);
148 c = getchar();
149 }
150 } else
151 c = argv[1][1];
152 switch (c) {
153
154 default:
155 delexit(EX_OK);
156 /*NOTREACHED*/
157
158 case 'x':
159 return;
160
161 case 'y':
162 (void) sprintf(mbox, "%s/mbox", logindir);
163 if (writeable(mbox)) {
164 perror(mbox);
165 return;
166 }
167 printf("Saving mail in %s.\n", mbox);
168 if (append(sfnmail, mbox, getuid(), getgid()) == 0)
169 return;
170 /* fall into... */
171
172 case 'n':
173 remove(sfnmail);
174 return;
175 }
176}
177
178bulkmail(argc, argv, from)
179 char **argv, *from;
180{
181 char linebuf[BUFSIZ];
182 FILE *fdout;
183
184 if (dflag == 0) {
185 argv[0] = "sendmail";
186 argv[argc] = 0;
187 execv("/usr/lib/sendmail", argv);
188 perror("/usr/lib/sendmail");
189 _exit(1);
190 }
191 fdout = fopen(lettmp, "w");
192 if (fdout == NULL) {
193 perror(lettmp);
194 delexit(EX_OSFILE);
195 }
196
197 /*
198 * If delivering mail from the network via mail -r,
199 * Strip the leading line and throw it away, as long
200 * as it begins with "From ..." (and preserve the date if poss.)
201 */
202 if (chew) {
203 if (fgets(linebuf, BUFSIZ, stdin) == 0)
204 goto skip;
205 if (!strncmp(linebuf, "From ", 5) != 0)
206 printfromline(fdout, getdate(linebuf), from);
207 else {
208 printfromline(fdout, (char *)0, from);
209 fprintf(fdout, "%s", linebuf);
210 }
211 } else
212 printfromline(fdout, (char *)0, from);
213skip:
214 if (remname[0])
215 fprintf(fdout, "(from %s)\n", remname);
216 if (getput(stdin, fdout) == 0)
217 delexit(EX_OSERR);
218 putc('\n', fdout);
219 (void) fclose(fdout);
220 while (--argc > 0)
221 sendto(*++argv);
222 delexit(errs);
223}
224
225printfromline(fdout, date, from)
226 FILE *fdout;
227 char *date, *from;
228{
229 time_t t;
230
231 if (date == NULL) {
232 t = time((time_t *)0);
233 date = ctime(&t);
234 }
235 fprintf(fdout, "From %s %s", from, date);
236}
237
238/* look over linebuf and return ptr to date, NULL if error */
239char *
240getdate(linebuf)
241 char *linebuf;
242{
243 register char *s = linebuf;
244
245/*###244 [lint] index arg. 2 used inconsistently v6mail.c(86) :: v6mail.c(244)%%%*/
246/*###244 [lint] index arg. 1 used inconsistently v6mail.c(86) :: v6mail.c(244)%%%*/
247 while (s = index(' ', s))
248 if (!strncmp(s, " Sun ", 5) ||
249 !strncmp(s, " Mon ", 5) ||
250 !strncmp(s, " Tue ", 5) ||
251 !strncmp(s, " Wed ", 5) ||
252 !strncmp(s, " Thu ", 5) ||
253 !strncmp(s, " Fri ", 5) ||
254 !strncmp(s, " Sat ", 5))
255 return (s + 1);
256 return (0);
257}
258
259int saved = 0;
260
261sendto(person)
262 char *person;
263{
264 char mailboxname[BUFSIZ];
265 struct passwd *pwd;
266
267 if (index('/', person)) {
268 if (!writeable(person)) {
269 perror(person);
270 return;
271 }
272 lock(person);
273 (void) append(lettmp, person, -1, -1);
274 unlock();
275 return;
276 }
277 pwd = getpwnam(person);
278 if (pwd) {
279 (void) sprintf(mailboxname, "%s/%s", MAILDIR, person);
280 lock(mailboxname);
281 (void) append(lettmp, mailboxname, pwd->pw_uid, pwd->pw_gid);
282 unlock();
283 return;
284 }
285 fprintf(stderr, "Can't send to %s.\n", person);
286 errs++;
287 if (!isatty(0) || saved)
288 return;
289 saved++;
290 if (!writeable("dead.letter")) {
291 perror("dead.letter");
292 return;
293 }
294 printf("Letter saved in 'dead.letter'\n");
295 (void) append(lettmp, "dead.letter", getuid(), getgid());
296}
297
298#include <sys/socket.h>
299#include <net/in.h>
300
301struct sockaddr_in biffaddr = { AF_INET, IPPORT_BIFFUDP };
302
303append(from, to, uid, gid)
304 char *from, *to;
305 int uid, gid;
306{
307 register FILE *fdin, *fdout;
308 int ret;
309 struct stat stb;
310 char *cp, buf[100]; int f;
311
312 if (stat(to, &stb) >= 0 && (stb.st_mode&S_IFMT) != S_IFREG) {
313 fprintf(stderr, "Not a plain file: %s\n", to);
314 goto fail;
315 }
316 fdout = fopen(to, "a");
317 if (fdout == NULL) {
318 perror(to);
319 goto fail;
320 }
321 if (uid != -1) {
322 (void) chown(to, uid, gid);
323 (void) chmod(to, MAILMODE);
324 }
325 if ((fdin = fopen(from, "r")) == NULL) {
326 perror(from);
327 return (0);
328 }
329 cp = rindex(to, '/');
330 if (cp) {
331 char *host = "localhost";
332 biffaddr.sin_addr.s_addr = rhost(&host);
333#if vax || pdp11
334 biffaddr.sin_port =
335 (biffaddr.sin_port<<8) | ((biffaddr.sin_port>>8) & 0xff);
336#endif
337 f = socket(SOCK_DGRAM, 0, 0, 0);
338 (void) sprintf(buf, "%s@%d\n", cp+1, ftell(fdout));
339 }
340 ret = getput(fdin, fdout);
341 (void) fclose(fdin);
342 (void) fclose(fdout);
343 if (cp && f >= 0) {
344 send(f, &biffaddr, buf, strlen(buf)+1);
345 (void) close(f);
346 }
347 return (ret);
348fail:
349 errs++;
350 return (0);
351}
352
353delexit(status)
354 int status;
355{
356
357 (void) unlink(lettmp);
358 (void) unlink(preptmp);
359 exit(status);
360}
361
362getput(fdin, fdout)
363 register FILE *fdin, *fdout;
364{
365 register int c;
366
367 while ((c = getc(fdin)) != EOF) {
368 errno = 0;
369 putc(c, fdout);
370 if (errno) {
371 perror("mail");
372 return (0);
373 }
374 }
375 return (1);
376}
377
378writeable(name)
379 char *name;
380{
381 struct stat stb;
382 char *cp;
383 int ok;
384
385 if (stat(name, &stb) < 0) {
386 cp = rindex(name, '/');
387 if (cp)
388 *cp = 0;
389 ok = access(cp ? "." : name, 2) == 0;
390 if (cp)
391 *cp = '/';
392 return (ok);
393 }
394 return (access(name, 2) == 0);
395}
396
397char locktmp[30]; /* Usable lock temporary */
398char curlock[50]; /* Last used name of lock */
399int locked; /* To note that we locked it */
400
401/*
402 * Lock the specified mail file by setting the file mailfile.lock.
403 * We must, of course, be careful to unlink the lock file by a call
404 * to unlock before we stop. The algorithm used here is to see if
405 * the lock exists, and if it does, to check its modify time. If it
406 * is older than 30 seconds, we assume error and set our own file.
407 * Otherwise, we wait for 5 seconds and try again.
408 */
409lock(file)
410 char *file;
411{
412 register int f;
413 struct stat statbuf;
414 time_t curtime;
415
416 if (locked)
417 return;
418 (void) sprintf(curlock, "%s%s", file, ".lock");
419 (void) sprintf(locktmp, "%s/tmXXXXXX", MAILDIR);
420 (void) mktemp(locktmp);
421 (void) unlink(locktmp);
422 for (;;) {
423 f = lock1(locktmp, curlock);
424 if (f == 0) {
425 locked = 1;
426 return;
427 }
428 if (stat(curlock, &statbuf) < 0)
429 return;
430 (void) time(&curtime);
431 if (curtime < statbuf.st_mtime + 30) {
432 sleep(5);
433 continue;
434 }
435 (void) unlink(curlock);
436 }
437}
438
439unlock()
440{
441
442 if (locked)
443 (void) unlink(curlock);
444 locked = 0;
445}
446
447/*
448 * Attempt to set the lock by creating the temporary file,
449 * then doing a link/unlink. If it fails, return -1 else 0
450 */
451lock1(tempfile, name)
452 char tempfile[], name[];
453{
454 int fno;
455
456 fno = creat(tempfile, 0400);
457 if (fno < 0)
458 return (-1);
459 (void) close(fno);
460 if (link(tempfile, name) < 0) {
461 (void) unlink(tempfile);
462 return (-1);
463 }
464 (void) unlink(tempfile);
465 return (0);
466}
467
468remove(sfn)
469 char *sfn;
470{
471 int i;
472
473 if (unlink(sfn) < 0) {
474 i = creat(sfn, MAILMODE);
475 if (i >= 0)
476 (void) close(i);
477 }
478}