Commit | Line | Data |
---|---|---|
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 | ||
15 | char *ctime(), *index(), *rindex(), *ctime(), *strcpy(), *getlogin(); | |
16 | char *mktemp(); | |
17 | struct passwd *getpwnam(), *getpwuid(); | |
18 | time_t time(); | |
19 | struct utmp *getutmp(); | |
20 | char *getdate(); | |
21 | int errno; | |
22 | ||
23 | #include <stdio.h> | |
24 | ||
25 | #define MAILMODE 0644 | |
26 | #define MSGSCMD "/usr/ucb/msgs" | |
27 | #define MAILDIR "/usr/spool/mail" | |
28 | ||
29 | char lettmp[] = "/tmp/MaXXXXX"; /* keep letter before sending it */ | |
30 | char preptmp[] = "/tmp/mbXXXXX"; /* if prepending msg, use this file */ | |
31 | int chew; /* if true, strip extra from lines */ | |
32 | int dflag; /* if true, don't call sendmail */ | |
33 | char shopcnt[30] = "0"; /* hop count parameter for rmt mail */ | |
34 | int errs; /* no of errs in sending */ | |
35 | char deleteonly; /* if true, just delete mailbox */ | |
36 | char remname[50]; /* if non-empty, from line extra */ | |
37 | ||
38 | main(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--; | |
74 | hitit: | |
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 | ||
117 | printmail(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 | ||
178 | bulkmail(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); | |
213 | skip: | |
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 | ||
225 | printfromline(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 */ | |
239 | char * | |
240 | getdate(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 | ||
259 | int saved = 0; | |
260 | ||
261 | sendto(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 | ||
301 | struct sockaddr_in biffaddr = { AF_INET, IPPORT_BIFFUDP }; | |
302 | ||
303 | append(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); | |
348 | fail: | |
349 | errs++; | |
350 | return (0); | |
351 | } | |
352 | ||
353 | delexit(status) | |
354 | int status; | |
355 | { | |
356 | ||
357 | (void) unlink(lettmp); | |
358 | (void) unlink(preptmp); | |
359 | exit(status); | |
360 | } | |
361 | ||
362 | getput(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 | ||
378 | writeable(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 | ||
397 | char locktmp[30]; /* Usable lock temporary */ | |
398 | char curlock[50]; /* Last used name of lock */ | |
399 | int 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 | */ | |
409 | lock(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 | ||
439 | unlock() | |
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 | */ | |
451 | lock1(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 | ||
468 | remove(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 | } |