1 | |
---|
2 | /* |
---|
3 | * slocal.c -- asynchronously filter and deliver new mail |
---|
4 | * |
---|
5 | * $Id: slocal.c,v 1.3 2000-01-07 04:43:58 rbasch Exp $ |
---|
6 | */ |
---|
7 | |
---|
8 | /* |
---|
9 | * Under sendmail, users should add the line |
---|
10 | * |
---|
11 | * "| /usr/local/nmh/lib/slocal" |
---|
12 | * |
---|
13 | * to their $HOME/.forward file. |
---|
14 | * |
---|
15 | * Under MMDF-I, users should (symbolically) link |
---|
16 | * /usr/local/nmh/lib/slocal to $HOME/bin/rcvmail. |
---|
17 | * |
---|
18 | */ |
---|
19 | |
---|
20 | #include <h/mh.h> |
---|
21 | #include <h/dropsbr.h> |
---|
22 | #include <h/rcvmail.h> |
---|
23 | #include <h/signals.h> |
---|
24 | #include <zotnet/tws/tws.h> |
---|
25 | #include <zotnet/mts/mts.h> |
---|
26 | |
---|
27 | #include <pwd.h> |
---|
28 | #include <signal.h> |
---|
29 | #include <sys/ioctl.h> |
---|
30 | #include <fcntl.h> |
---|
31 | |
---|
32 | #if defined (HAVE_DB_H) && !defined (HAVE_NDBM_H) |
---|
33 | #define DB_DBM_HSEARCH 1 |
---|
34 | #include <db.h> |
---|
35 | #elif defined (HAVE_NDBM_H) |
---|
36 | #include <ndbm.h> |
---|
37 | #else |
---|
38 | #error Cannot find a suitable database header |
---|
39 | #endif |
---|
40 | |
---|
41 | #include <utmp.h> |
---|
42 | |
---|
43 | #ifndef UTMP_FILE |
---|
44 | # ifdef _PATH_UTMP |
---|
45 | # define UTMP_FILE _PATH_UTMP |
---|
46 | # else |
---|
47 | # define UTMP_FILE "/etc/utmp" |
---|
48 | # endif |
---|
49 | #endif |
---|
50 | |
---|
51 | static struct swit switches[] = { |
---|
52 | #define ADDRSW 0 |
---|
53 | { "addr address", 0 }, |
---|
54 | #define USERSW 1 |
---|
55 | { "user name", 0 }, |
---|
56 | #define FILESW 2 |
---|
57 | { "file file", 0 }, |
---|
58 | #define SENDERSW 3 |
---|
59 | { "sender address", 0 }, |
---|
60 | #define MAILBOXSW 4 |
---|
61 | { "mailbox file", 0 }, |
---|
62 | #define HOMESW 5 |
---|
63 | { "home directory", -4 }, |
---|
64 | #define INFOSW 6 |
---|
65 | { "info data", 0 }, |
---|
66 | #define MAILSW 7 |
---|
67 | { "maildelivery file", 0 }, |
---|
68 | #define VERBSW 8 |
---|
69 | { "verbose", 0 }, |
---|
70 | #define NVERBSW 9 |
---|
71 | { "noverbose", 0 }, |
---|
72 | #define SUPPRESSDUP 10 |
---|
73 | { "suppressdup", 0 }, |
---|
74 | #define NSUPPRESSDUP 11 |
---|
75 | { "nosuppressdup", 0 }, |
---|
76 | #define DEBUGSW 12 |
---|
77 | { "debug", 0 }, |
---|
78 | #define VERSIONSW 13 |
---|
79 | { "version", 0 }, |
---|
80 | #define HELPSW 14 |
---|
81 | { "help", 4 }, |
---|
82 | { NULL, 0 } |
---|
83 | }; |
---|
84 | |
---|
85 | static int globbed = 0; /* have we built "vars" table yet? */ |
---|
86 | static int parsed = 0; /* have we built header field table yet */ |
---|
87 | static int utmped = 0; /* have we scanned umtp(x) file yet */ |
---|
88 | static int suppressdup = 0; /* are we suppressing duplicate messages? */ |
---|
89 | |
---|
90 | static int verbose = 0; |
---|
91 | static int debug = 0; |
---|
92 | |
---|
93 | static char *addr = NULL; |
---|
94 | static char *user = NULL; |
---|
95 | static char *info = NULL; |
---|
96 | static char *file = NULL; |
---|
97 | static char *sender = NULL; |
---|
98 | static char *envelope = NULL; /* envelope information ("From " line) */ |
---|
99 | static char *mbox = NULL; |
---|
100 | static char *home = NULL; |
---|
101 | |
---|
102 | static struct passwd *pw; /* passwd file entry */ |
---|
103 | |
---|
104 | static char ddate[BUFSIZ]; /* record the delivery date */ |
---|
105 | struct tws *now; |
---|
106 | |
---|
107 | static jmp_buf myctx; |
---|
108 | |
---|
109 | /* flags for pair->p_flags */ |
---|
110 | #define P_NIL 0x00 |
---|
111 | #define P_ADR 0x01 /* field is address */ |
---|
112 | #define P_HID 0x02 /* special (fake) field */ |
---|
113 | #define P_CHK 0x04 |
---|
114 | |
---|
115 | struct pair { |
---|
116 | char *p_name; |
---|
117 | char *p_value; |
---|
118 | char p_flags; |
---|
119 | }; |
---|
120 | |
---|
121 | #define NVEC 100 |
---|
122 | |
---|
123 | /* |
---|
124 | * Lookup table for matching fields and patterns |
---|
125 | * in messages. The rest of the table is added |
---|
126 | * when the message is parsed. |
---|
127 | */ |
---|
128 | static struct pair hdrs[NVEC + 1] = { |
---|
129 | { "source", NULL, P_HID }, |
---|
130 | { "addr", NULL, P_HID }, |
---|
131 | { "Return-Path", NULL, P_ADR }, |
---|
132 | { "Reply-To", NULL, P_ADR }, |
---|
133 | { "From", NULL, P_ADR }, |
---|
134 | { "Sender", NULL, P_ADR }, |
---|
135 | { "To", NULL, P_ADR }, |
---|
136 | { "cc", NULL, P_ADR }, |
---|
137 | { "Resent-Reply-To", NULL, P_ADR }, |
---|
138 | { "Resent-From", NULL, P_ADR }, |
---|
139 | { "Resent-Sender", NULL, P_ADR }, |
---|
140 | { "Resent-To", NULL, P_ADR }, |
---|
141 | { "Resent-cc", NULL, P_ADR }, |
---|
142 | { NULL, NULL, 0 } |
---|
143 | }; |
---|
144 | |
---|
145 | /* |
---|
146 | * The list of builtin variables to expand in a string |
---|
147 | * before it is executed by the "pipe" or "qpipe" action. |
---|
148 | */ |
---|
149 | static struct pair vars[] = { |
---|
150 | { "sender", NULL, P_NIL }, |
---|
151 | { "address", NULL, P_NIL }, |
---|
152 | { "size", NULL, P_NIL }, |
---|
153 | { "reply-to", NULL, P_CHK }, |
---|
154 | { "info", NULL, P_NIL }, |
---|
155 | { NULL, NULL, 0 } |
---|
156 | }; |
---|
157 | |
---|
158 | extern char **environ; |
---|
159 | |
---|
160 | /* |
---|
161 | * static prototypes |
---|
162 | */ |
---|
163 | static int localmail (int, char *); |
---|
164 | static int usr_delivery (int, char *, int); |
---|
165 | static int split (char *, char **); |
---|
166 | static int parse (int); |
---|
167 | static void expand (char *, char *, int); |
---|
168 | static void glob (int); |
---|
169 | static struct pair *lookup (struct pair *, char *); |
---|
170 | static int logged_in (void); |
---|
171 | static int timely (char *, char *); |
---|
172 | static int usr_file (int, char *, int); |
---|
173 | static int usr_pipe (int, char *, char *, char **, int); |
---|
174 | static int usr_folder (int, char *); |
---|
175 | static RETSIGTYPE alrmser (int); |
---|
176 | static void get_sender (char *, char **); |
---|
177 | static int copy_message (int, char *, int); |
---|
178 | static void verbose_printf (char *fmt, ...); |
---|
179 | static void adorn (char *, char *, ...); |
---|
180 | static void debug_printf (char *fmt, ...); |
---|
181 | static int suppress_duplicates (int, char *); |
---|
182 | static char *trim (char *); |
---|
183 | |
---|
184 | |
---|
185 | int |
---|
186 | main (int argc, char **argv) |
---|
187 | { |
---|
188 | int fd, status; |
---|
189 | FILE *fp = stdin; |
---|
190 | char *cp, *mdlvr = NULL, buf[BUFSIZ]; |
---|
191 | char mailbox[BUFSIZ], tmpfil[BUFSIZ]; |
---|
192 | char **argp, **arguments; |
---|
193 | |
---|
194 | #ifdef LOCALE |
---|
195 | setlocale(LC_ALL, ""); |
---|
196 | #endif |
---|
197 | invo_name = r1bindex (*argv, '/'); |
---|
198 | |
---|
199 | /* foil search of user profile/context */ |
---|
200 | if (context_foil (NULL) == -1) |
---|
201 | done (1); |
---|
202 | |
---|
203 | mts_init (invo_name); |
---|
204 | arguments = getarguments (invo_name, argc, argv, 0); |
---|
205 | argp = arguments; |
---|
206 | |
---|
207 | /* Parse arguments */ |
---|
208 | while ((cp = *argp++)) { |
---|
209 | if (*cp == '-') { |
---|
210 | switch (smatch (++cp, switches)) { |
---|
211 | case AMBIGSW: |
---|
212 | ambigsw (cp, switches); |
---|
213 | done (1); |
---|
214 | case UNKWNSW: |
---|
215 | adios (NULL, "-%s unknown", cp); |
---|
216 | |
---|
217 | case HELPSW: |
---|
218 | snprintf (buf, sizeof(buf), |
---|
219 | "%s [switches] [address info sender]", invo_name); |
---|
220 | print_help (buf, switches, 0); |
---|
221 | done (1); |
---|
222 | case VERSIONSW: |
---|
223 | print_version(invo_name); |
---|
224 | done (1); |
---|
225 | |
---|
226 | case ADDRSW: |
---|
227 | if (!(addr = *argp++))/* allow -xyz arguments */ |
---|
228 | adios (NULL, "missing argument to %s", argp[-2]); |
---|
229 | continue; |
---|
230 | case INFOSW: |
---|
231 | if (!(info = *argp++))/* allow -xyz arguments */ |
---|
232 | adios (NULL, "missing argument to %s", argp[-2]); |
---|
233 | continue; |
---|
234 | case USERSW: |
---|
235 | if (!(user = *argp++))/* allow -xyz arguments */ |
---|
236 | adios (NULL, "missing argument to %s", argp[-2]); |
---|
237 | continue; |
---|
238 | case FILESW: |
---|
239 | if (!(file = *argp++) || *file == '-') |
---|
240 | adios (NULL, "missing argument to %s", argp[-2]); |
---|
241 | continue; |
---|
242 | case SENDERSW: |
---|
243 | if (!(sender = *argp++))/* allow -xyz arguments */ |
---|
244 | adios (NULL, "missing argument to %s", argp[-2]); |
---|
245 | continue; |
---|
246 | case MAILBOXSW: |
---|
247 | if (!(mbox = *argp++) || *mbox == '-') |
---|
248 | adios (NULL, "missing argument to %s", argp[-2]); |
---|
249 | continue; |
---|
250 | case HOMESW: |
---|
251 | if (!(home = *argp++) || *home == '-') |
---|
252 | adios (NULL, "missing argument to %s", argp[-2]); |
---|
253 | continue; |
---|
254 | |
---|
255 | case MAILSW: |
---|
256 | if (!(cp = *argp++) || *cp == '-') |
---|
257 | adios (NULL, "missing argument to %s", argp[-2]); |
---|
258 | if (mdlvr) |
---|
259 | adios (NULL, "only one maildelivery file at a time!"); |
---|
260 | mdlvr = cp; |
---|
261 | continue; |
---|
262 | |
---|
263 | case VERBSW: |
---|
264 | verbose++; |
---|
265 | continue; |
---|
266 | case NVERBSW: |
---|
267 | verbose = 0; |
---|
268 | continue; |
---|
269 | |
---|
270 | case SUPPRESSDUP: |
---|
271 | suppressdup++; |
---|
272 | continue; |
---|
273 | case NSUPPRESSDUP: |
---|
274 | suppressdup = 0; |
---|
275 | continue; |
---|
276 | case DEBUGSW: |
---|
277 | debug++; |
---|
278 | continue; |
---|
279 | } |
---|
280 | } |
---|
281 | |
---|
282 | switch (argp - (argv + 1)) { |
---|
283 | case 1: |
---|
284 | addr = cp; |
---|
285 | break; |
---|
286 | |
---|
287 | case 2: |
---|
288 | info = cp; |
---|
289 | break; |
---|
290 | |
---|
291 | case 3: |
---|
292 | sender = cp; |
---|
293 | break; |
---|
294 | } |
---|
295 | } |
---|
296 | |
---|
297 | if (addr == NULL) |
---|
298 | addr = getusername (); |
---|
299 | if (user == NULL) |
---|
300 | user = (cp = strchr(addr, '.')) ? ++cp : addr; |
---|
301 | if ((pw = getpwnam (user)) == NULL) |
---|
302 | adios (NULL, "no such local user as %s", user); |
---|
303 | |
---|
304 | if (chdir (pw->pw_dir) == -1) |
---|
305 | chdir ("/"); |
---|
306 | umask (0077); |
---|
307 | |
---|
308 | if (geteuid() == 0) { |
---|
309 | setgid (pw->pw_gid); |
---|
310 | initgroups (pw->pw_name, pw->pw_gid); |
---|
311 | setuid (pw->pw_uid); |
---|
312 | } |
---|
313 | |
---|
314 | if (info == NULL) |
---|
315 | info = ""; |
---|
316 | |
---|
317 | setbuf (stdin, NULL); |
---|
318 | |
---|
319 | /* Record the delivery time */ |
---|
320 | if ((now = dlocaltimenow ()) == NULL) |
---|
321 | adios (NULL, "unable to ascertain local time"); |
---|
322 | snprintf (ddate, sizeof(ddate), "Delivery-Date: %s\n", dtimenow (0)); |
---|
323 | |
---|
324 | /* |
---|
325 | * Copy the message to a temporary file |
---|
326 | */ |
---|
327 | if (file) { |
---|
328 | int tempfd; |
---|
329 | |
---|
330 | /* getting message from file */ |
---|
331 | if ((tempfd = open (file, O_RDONLY)) == -1) |
---|
332 | adios (file, "unable to open"); |
---|
333 | if (debug) |
---|
334 | debug_printf ("retrieving message from file \"%s\"\n", file); |
---|
335 | if ((fd = copy_message (tempfd, tmpfil, 1)) == -1) |
---|
336 | adios (NULL, "unable to create temporary file"); |
---|
337 | close (tempfd); |
---|
338 | } else { |
---|
339 | /* getting message from stdin */ |
---|
340 | if (debug) |
---|
341 | debug_printf ("retrieving message from stdin\n"); |
---|
342 | if ((fd = copy_message (fileno (stdin), tmpfil, 1)) == -1) |
---|
343 | adios (NULL, "unable to create temporary file"); |
---|
344 | } |
---|
345 | if (debug) |
---|
346 | debug_printf ("temporary file=\"%s\"\n", tmpfil); |
---|
347 | else |
---|
348 | unlink (tmpfil); |
---|
349 | |
---|
350 | if (!(fp = fdopen (fd, "r+"))) |
---|
351 | adios (NULL, "unable to access temporary file"); |
---|
352 | |
---|
353 | /* |
---|
354 | * If no sender given, extract it |
---|
355 | * from envelope information. |
---|
356 | */ |
---|
357 | if (sender == NULL) |
---|
358 | get_sender (envelope, &sender); |
---|
359 | |
---|
360 | if (mbox == NULL) { |
---|
361 | snprintf (mailbox, sizeof(mailbox), "%s/%s", |
---|
362 | mmdfldir[0] ? mmdfldir : pw->pw_dir, |
---|
363 | mmdflfil[0] ? mmdflfil : pw->pw_name); |
---|
364 | mbox = mailbox; |
---|
365 | } |
---|
366 | if (home == NULL) |
---|
367 | home = pw->pw_dir; |
---|
368 | |
---|
369 | if (debug) { |
---|
370 | debug_printf ("addr=\"%s\"\n", trim(addr)); |
---|
371 | debug_printf ("user=\"%s\"\n", trim(user)); |
---|
372 | debug_printf ("info=\"%s\"\n", trim(info)); |
---|
373 | debug_printf ("sender=\"%s\"\n", trim(sender)); |
---|
374 | debug_printf ("envelope=\"%s\"\n", envelope ? trim(envelope) : ""); |
---|
375 | debug_printf ("mbox=\"%s\"\n", trim(mbox)); |
---|
376 | debug_printf ("home=\"%s\"\n", trim(home)); |
---|
377 | debug_printf ("ddate=\"%s\"\n", trim(ddate)); |
---|
378 | debug_printf ("now=%02d:%02d\n\n", now->tw_hour, now->tw_min); |
---|
379 | } |
---|
380 | |
---|
381 | /* deliver the message */ |
---|
382 | status = localmail (fd, mdlvr); |
---|
383 | |
---|
384 | done (status != -1 ? RCV_MOK : RCV_MBX); |
---|
385 | } |
---|
386 | |
---|
387 | |
---|
388 | /* |
---|
389 | * Main routine for delivering message. |
---|
390 | */ |
---|
391 | |
---|
392 | static int |
---|
393 | localmail (int fd, char *mdlvr) |
---|
394 | { |
---|
395 | /* check if this message is a duplicate */ |
---|
396 | if (suppressdup && |
---|
397 | suppress_duplicates(fd, mdlvr ? mdlvr : ".maildelivery") == DONE) |
---|
398 | return 0; |
---|
399 | |
---|
400 | /* delivery according to personal Maildelivery file */ |
---|
401 | if (usr_delivery (fd, mdlvr ? mdlvr : ".maildelivery", 0) != -1) |
---|
402 | return 0; |
---|
403 | |
---|
404 | /* delivery according to global Maildelivery file */ |
---|
405 | if (usr_delivery (fd, maildelivery, 1) != -1) |
---|
406 | return 0; |
---|
407 | |
---|
408 | if (verbose) |
---|
409 | verbose_printf ("(delivering to standard mail spool)\n"); |
---|
410 | |
---|
411 | /* last resort - deliver to standard mail spool */ |
---|
412 | #ifdef SLOCAL_MBOX |
---|
413 | return usr_file (fd, mbox, MBOX_FORMAT); |
---|
414 | #else |
---|
415 | return usr_file (fd, mbox, MMDF_FORMAT); |
---|
416 | #endif |
---|
417 | } |
---|
418 | |
---|
419 | |
---|
420 | #define matches(a,b) (stringdex (b, a) >= 0) |
---|
421 | |
---|
422 | /* |
---|
423 | * Parse the delivery file, and process incoming message. |
---|
424 | */ |
---|
425 | |
---|
426 | static int |
---|
427 | usr_delivery (int fd, char *delivery, int su) |
---|
428 | { |
---|
429 | int i, accept, status, won, vecp, next; |
---|
430 | char *field, *pattern, *action, *result, *string; |
---|
431 | char buffer[BUFSIZ], tmpbuf[BUFSIZ]; |
---|
432 | char *cp, *vec[NVEC]; |
---|
433 | struct stat st; |
---|
434 | struct pair *p; |
---|
435 | FILE *fp; |
---|
436 | |
---|
437 | /* open the delivery file */ |
---|
438 | if ((fp = fopen (delivery, "r")) == NULL) |
---|
439 | return -1; |
---|
440 | |
---|
441 | /* check if delivery file has bad ownership or permissions */ |
---|
442 | if (fstat (fileno (fp), &st) == -1 |
---|
443 | || (st.st_uid != 0 && (su || st.st_uid != pw->pw_uid)) |
---|
444 | || st.st_mode & (S_IWGRP|S_IWOTH)) { |
---|
445 | if (verbose) { |
---|
446 | verbose_printf ("WARNING: %s has bad ownership/modes (su=%d,uid=%d,owner=%d,mode=0%o)\n", |
---|
447 | delivery, su, (int) pw->pw_uid, (int) st.st_uid, (int) st.st_mode); |
---|
448 | } |
---|
449 | return -1; |
---|
450 | } |
---|
451 | |
---|
452 | won = 0; |
---|
453 | next = 1; |
---|
454 | |
---|
455 | /* read and process delivery file */ |
---|
456 | while (fgets (buffer, sizeof(buffer), fp)) { |
---|
457 | /* skip comments and empty lines */ |
---|
458 | if (*buffer == '#' || *buffer == '\n') |
---|
459 | continue; |
---|
460 | |
---|
461 | /* zap trailing newline */ |
---|
462 | if ((cp = strchr(buffer, '\n'))) |
---|
463 | *cp = 0; |
---|
464 | |
---|
465 | /* split buffer into fields */ |
---|
466 | vecp = split (buffer, vec); |
---|
467 | |
---|
468 | /* check for too few fields */ |
---|
469 | if (vecp < 5) { |
---|
470 | if (debug) |
---|
471 | debug_printf ("WARNING: entry with only %d fields, skipping.\n", vecp); |
---|
472 | continue; |
---|
473 | } |
---|
474 | |
---|
475 | if (debug) { |
---|
476 | for (i = 0; vec[i]; i++) |
---|
477 | debug_printf ("vec[%d]: \"%s\"\n", i, trim(vec[i])); |
---|
478 | } |
---|
479 | |
---|
480 | field = vec[0]; |
---|
481 | pattern = vec[1]; |
---|
482 | action = vec[2]; |
---|
483 | result = vec[3]; |
---|
484 | string = vec[4]; |
---|
485 | |
---|
486 | /* find out how to perform the action */ |
---|
487 | switch (result[0]) { |
---|
488 | case 'N': |
---|
489 | case 'n': |
---|
490 | /* |
---|
491 | * If previous condition failed, don't |
---|
492 | * do this - else fall through |
---|
493 | */ |
---|
494 | if (!next) |
---|
495 | continue; /* else fall */ |
---|
496 | |
---|
497 | case '?': |
---|
498 | /* |
---|
499 | * If already delivered, skip this action. Else |
---|
500 | * consider delivered if action is successful. |
---|
501 | */ |
---|
502 | if (won) |
---|
503 | continue; /* else fall */ |
---|
504 | |
---|
505 | case 'A': |
---|
506 | case 'a': |
---|
507 | /* |
---|
508 | * Take action, and consider delivered if |
---|
509 | * action is successful. |
---|
510 | */ |
---|
511 | accept = 1; |
---|
512 | break; |
---|
513 | |
---|
514 | case 'R': |
---|
515 | case 'r': |
---|
516 | default: |
---|
517 | /* |
---|
518 | * Take action, but don't consider delivered, even |
---|
519 | * if action is successful |
---|
520 | */ |
---|
521 | accept = 0; |
---|
522 | break; |
---|
523 | } |
---|
524 | |
---|
525 | if (vecp > 5) { |
---|
526 | if (!strcasecmp (vec[5], "select")) { |
---|
527 | if (logged_in () != -1) |
---|
528 | continue; |
---|
529 | if (vecp > 7 && timely (vec[6], vec[7]) == -1) |
---|
530 | continue; |
---|
531 | } |
---|
532 | } |
---|
533 | |
---|
534 | /* check if the field matches */ |
---|
535 | switch (*field) { |
---|
536 | case '*': |
---|
537 | /* always matches */ |
---|
538 | break; |
---|
539 | |
---|
540 | case 'd': |
---|
541 | /* |
---|
542 | * "default" matches only if the message hasn't |
---|
543 | * been delivered yet. |
---|
544 | */ |
---|
545 | if (!strcasecmp (field, "default")) { |
---|
546 | if (won) |
---|
547 | continue; |
---|
548 | break; |
---|
549 | } /* else fall */ |
---|
550 | |
---|
551 | default: |
---|
552 | /* parse message and build lookup table */ |
---|
553 | if (!parsed && parse (fd) == -1) { |
---|
554 | fclose (fp); |
---|
555 | return -1; |
---|
556 | } |
---|
557 | /* |
---|
558 | * find header field in lookup table, and |
---|
559 | * see if the pattern matches. |
---|
560 | */ |
---|
561 | if ((p = lookup (hdrs, field)) && (p->p_value != NULL) |
---|
562 | && matches (p->p_value, pattern)) { |
---|
563 | next = 1; |
---|
564 | } else { |
---|
565 | next = 0; |
---|
566 | continue; |
---|
567 | } |
---|
568 | break; |
---|
569 | } |
---|
570 | |
---|
571 | /* find out the action to perform */ |
---|
572 | switch (*action) { |
---|
573 | case 'q': |
---|
574 | /* deliver to quoted pipe */ |
---|
575 | if (strcasecmp (action, "qpipe")) |
---|
576 | continue; /* else fall */ |
---|
577 | case '^': |
---|
578 | expand (tmpbuf, string, fd); |
---|
579 | if (split (tmpbuf, vec) < 1) |
---|
580 | continue; |
---|
581 | status = usr_pipe (fd, tmpbuf, vec[0], vec, 0); |
---|
582 | break; |
---|
583 | |
---|
584 | case 'p': |
---|
585 | /* deliver to pipe */ |
---|
586 | if (strcasecmp (action, "pipe")) |
---|
587 | continue; /* else fall */ |
---|
588 | case '|': |
---|
589 | vec[2] = "sh"; |
---|
590 | vec[3] = "-c"; |
---|
591 | expand (tmpbuf, string, fd); |
---|
592 | vec[4] = tmpbuf; |
---|
593 | vec[5] = NULL; |
---|
594 | status = usr_pipe (fd, tmpbuf, "/bin/sh", vec + 2, 0); |
---|
595 | break; |
---|
596 | |
---|
597 | case 'f': |
---|
598 | /* mbox format */ |
---|
599 | if (!strcasecmp (action, "file")) { |
---|
600 | status = usr_file (fd, string, MBOX_FORMAT); |
---|
601 | break; |
---|
602 | } |
---|
603 | /* deliver to nmh folder */ |
---|
604 | else if (strcasecmp (action, "folder")) |
---|
605 | continue; /* else fall */ |
---|
606 | case '+': |
---|
607 | status = usr_folder (fd, string); |
---|
608 | break; |
---|
609 | |
---|
610 | case 'm': |
---|
611 | /* mmdf format */ |
---|
612 | if (!strcasecmp (action, "mmdf")) { |
---|
613 | status = usr_file (fd, string, MMDF_FORMAT); |
---|
614 | break; |
---|
615 | } |
---|
616 | /* mbox format */ |
---|
617 | else if (strcasecmp (action, "mbox")) |
---|
618 | continue; /* else fall */ |
---|
619 | |
---|
620 | case '>': |
---|
621 | /* mbox format */ |
---|
622 | status = usr_file (fd, string, MBOX_FORMAT); |
---|
623 | break; |
---|
624 | |
---|
625 | case 'd': |
---|
626 | /* ignore message */ |
---|
627 | if (strcasecmp (action, "destroy")) |
---|
628 | continue; |
---|
629 | status = 0; |
---|
630 | break; |
---|
631 | } |
---|
632 | |
---|
633 | if (accept && status == 0) |
---|
634 | won++; |
---|
635 | } |
---|
636 | |
---|
637 | fclose (fp); |
---|
638 | return (won ? 0 : -1); |
---|
639 | } |
---|
640 | |
---|
641 | |
---|
642 | #define QUOTE '\\' |
---|
643 | |
---|
644 | /* |
---|
645 | * Split buffer into fields (delimited by whitespace or |
---|
646 | * comma's). Return the number of fields found. |
---|
647 | */ |
---|
648 | |
---|
649 | static int |
---|
650 | split (char *cp, char **vec) |
---|
651 | { |
---|
652 | int i; |
---|
653 | char *s; |
---|
654 | |
---|
655 | s = cp; |
---|
656 | |
---|
657 | /* split into a maximum of NVEC fields */ |
---|
658 | for (i = 0; i <= NVEC;) { |
---|
659 | vec[i] = NULL; |
---|
660 | |
---|
661 | /* zap any whitespace and comma's */ |
---|
662 | while (isspace (*s) || *s == ',') |
---|
663 | *s++ = 0; |
---|
664 | |
---|
665 | /* end of buffer, time to leave */ |
---|
666 | if (*s == 0) |
---|
667 | break; |
---|
668 | |
---|
669 | /* get double quote text as a single field */ |
---|
670 | if (*s == '"') { |
---|
671 | for (vec[i++] = ++s; *s && *s != '"'; s++) { |
---|
672 | /* |
---|
673 | * Check for escaped double quote. We need |
---|
674 | * to shift the string to remove slash. |
---|
675 | */ |
---|
676 | if (*s == QUOTE) { |
---|
677 | if (*++s == '"') |
---|
678 | strcpy (s - 1, s); |
---|
679 | s--; |
---|
680 | } |
---|
681 | } |
---|
682 | if (*s == '"') /* zap trailing double quote */ |
---|
683 | *s++ = 0; |
---|
684 | continue; |
---|
685 | } |
---|
686 | |
---|
687 | if (*s == QUOTE && *++s != '"') |
---|
688 | s--; |
---|
689 | vec[i++] = s++; |
---|
690 | |
---|
691 | /* move forward to next field delimiter */ |
---|
692 | while (*s && !isspace (*s) && *s != ',') |
---|
693 | s++; |
---|
694 | } |
---|
695 | vec[i] = NULL; |
---|
696 | |
---|
697 | return i; |
---|
698 | } |
---|
699 | |
---|
700 | |
---|
701 | /* |
---|
702 | * Parse the headers of a message, and build the |
---|
703 | * lookup table for matching fields and patterns. |
---|
704 | */ |
---|
705 | |
---|
706 | static int |
---|
707 | parse (int fd) |
---|
708 | { |
---|
709 | int i, state; |
---|
710 | int fd1; |
---|
711 | char *cp, *dp, *lp; |
---|
712 | char name[NAMESZ], field[BUFSIZ]; |
---|
713 | struct pair *p, *q; |
---|
714 | FILE *in; |
---|
715 | |
---|
716 | if (parsed++) |
---|
717 | return 0; |
---|
718 | |
---|
719 | /* get a new FILE pointer to message */ |
---|
720 | if ((fd1 = dup (fd)) == -1) |
---|
721 | return -1; |
---|
722 | if ((in = fdopen (fd1, "r")) == NULL) { |
---|
723 | close (fd1); |
---|
724 | return -1; |
---|
725 | } |
---|
726 | rewind (in); |
---|
727 | |
---|
728 | /* add special entries to lookup table */ |
---|
729 | if ((p = lookup (hdrs, "source"))) |
---|
730 | p->p_value = getcpy (sender); |
---|
731 | if ((p = lookup (hdrs, "addr"))) |
---|
732 | p->p_value = getcpy (addr); |
---|
733 | |
---|
734 | /* |
---|
735 | * Scan the headers of the message and build |
---|
736 | * a lookup table. |
---|
737 | */ |
---|
738 | for (i = 0, state = FLD;;) { |
---|
739 | switch (state = m_getfld (state, name, field, sizeof(field), in)) { |
---|
740 | case FLD: |
---|
741 | case FLDEOF: |
---|
742 | case FLDPLUS: |
---|
743 | lp = add (field, NULL); |
---|
744 | while (state == FLDPLUS) { |
---|
745 | state = m_getfld (state, name, field, sizeof(field), in); |
---|
746 | lp = add (field, lp); |
---|
747 | } |
---|
748 | for (p = hdrs; p->p_name; p++) { |
---|
749 | if (!strcasecmp (p->p_name, name)) { |
---|
750 | if (!(p->p_flags & P_HID)) { |
---|
751 | if ((cp = p->p_value)) |
---|
752 | if (p->p_flags & P_ADR) { |
---|
753 | dp = cp + strlen (cp) - 1; |
---|
754 | if (*dp == '\n') |
---|
755 | *dp = 0; |
---|
756 | cp = add (",\n\t", cp); |
---|
757 | } else { |
---|
758 | cp = add ("\t", cp); |
---|
759 | } |
---|
760 | p->p_value = add (lp, cp); |
---|
761 | } |
---|
762 | free (lp); |
---|
763 | break; |
---|
764 | } |
---|
765 | } |
---|
766 | if (p->p_name == NULL && i < NVEC) { |
---|
767 | p->p_name = getcpy (name); |
---|
768 | p->p_value = lp; |
---|
769 | p->p_flags = P_NIL; |
---|
770 | p++, i++; |
---|
771 | p->p_name = NULL; |
---|
772 | } |
---|
773 | if (state != FLDEOF) |
---|
774 | continue; |
---|
775 | break; |
---|
776 | |
---|
777 | case BODY: |
---|
778 | case BODYEOF: |
---|
779 | case FILEEOF: |
---|
780 | break; |
---|
781 | |
---|
782 | case LENERR: |
---|
783 | case FMTERR: |
---|
784 | advise (NULL, "format error in message"); |
---|
785 | break; |
---|
786 | |
---|
787 | default: |
---|
788 | advise (NULL, "internal error in m_getfld"); |
---|
789 | fclose (in); |
---|
790 | return -1; |
---|
791 | } |
---|
792 | break; |
---|
793 | } |
---|
794 | fclose (in); |
---|
795 | |
---|
796 | if ((p = lookup (vars, "reply-to"))) { |
---|
797 | if ((q = lookup (hdrs, "reply-to")) == NULL || q->p_value == NULL) |
---|
798 | q = lookup (hdrs, "from"); |
---|
799 | p->p_value = getcpy (q ? q->p_value : ""); |
---|
800 | p->p_flags &= ~P_CHK; |
---|
801 | if (debug) |
---|
802 | debug_printf ("vars[%d]: name=\"%s\" value=\"%s\"\n", |
---|
803 | p - vars, p->p_name, trim(p->p_value)); |
---|
804 | } |
---|
805 | if (debug) { |
---|
806 | for (p = hdrs; p->p_name; p++) |
---|
807 | debug_printf ("hdrs[%d]: name=\"%s\" value=\"%s\"\n", |
---|
808 | p - hdrs, p->p_name, p->p_value ? trim(p->p_value) : ""); |
---|
809 | } |
---|
810 | |
---|
811 | return 0; |
---|
812 | } |
---|
813 | |
---|
814 | |
---|
815 | #define LPAREN '(' |
---|
816 | #define RPAREN ')' |
---|
817 | |
---|
818 | /* |
---|
819 | * Expand any builtin variables such as $(sender), |
---|
820 | * $(address), etc., in a string. |
---|
821 | */ |
---|
822 | |
---|
823 | static void |
---|
824 | expand (char *s1, char *s2, int fd) |
---|
825 | { |
---|
826 | char c, *cp; |
---|
827 | struct pair *p; |
---|
828 | |
---|
829 | if (!globbed) |
---|
830 | glob (fd); |
---|
831 | |
---|
832 | while ((c = *s2++)) { |
---|
833 | if (c != '$' || *s2 != LPAREN) { |
---|
834 | *s1++ = c; |
---|
835 | } else { |
---|
836 | for (cp = ++s2; *s2 && *s2 != RPAREN; s2++) |
---|
837 | continue; |
---|
838 | if (*s2 != RPAREN) { |
---|
839 | s2 = --cp; |
---|
840 | continue; |
---|
841 | } |
---|
842 | *s2++ = 0; |
---|
843 | if ((p = lookup (vars, cp))) { |
---|
844 | if (!parsed && (p->p_flags & P_CHK)) |
---|
845 | parse (fd); |
---|
846 | |
---|
847 | strcpy (s1, p->p_value); |
---|
848 | s1 += strlen (s1); |
---|
849 | } |
---|
850 | } |
---|
851 | } |
---|
852 | *s1 = 0; |
---|
853 | } |
---|
854 | |
---|
855 | |
---|
856 | /* |
---|
857 | * Fill in the information missing from the "vars" |
---|
858 | * table, which is necessary to expand any builtin |
---|
859 | * variables in the string for a "pipe" or "qpipe" |
---|
860 | * action. |
---|
861 | */ |
---|
862 | |
---|
863 | static void |
---|
864 | glob (int fd) |
---|
865 | { |
---|
866 | char buffer[BUFSIZ]; |
---|
867 | struct stat st; |
---|
868 | struct pair *p; |
---|
869 | |
---|
870 | if (globbed++) |
---|
871 | return; |
---|
872 | |
---|
873 | if ((p = lookup (vars, "sender"))) |
---|
874 | p->p_value = getcpy (sender); |
---|
875 | if ((p = lookup (vars, "address"))) |
---|
876 | p->p_value = getcpy (addr); |
---|
877 | if ((p = lookup (vars, "size"))) { |
---|
878 | snprintf (buffer, sizeof(buffer), "%d", |
---|
879 | fstat (fd, &st) != -1 ? (int) st.st_size : 0); |
---|
880 | p->p_value = getcpy (buffer); |
---|
881 | } |
---|
882 | if ((p = lookup (vars, "info"))) |
---|
883 | p->p_value = getcpy (info); |
---|
884 | |
---|
885 | if (debug) { |
---|
886 | for (p = vars; p->p_name; p++) |
---|
887 | debug_printf ("vars[%d]: name=\"%s\" value=\"%s\"\n", |
---|
888 | p - vars, p->p_name, trim(p->p_value)); |
---|
889 | } |
---|
890 | } |
---|
891 | |
---|
892 | |
---|
893 | /* |
---|
894 | * Find a matching name in a lookup table. If found, |
---|
895 | * return the "pairs" entry, else return NULL. |
---|
896 | */ |
---|
897 | |
---|
898 | static struct pair * |
---|
899 | lookup (struct pair *pairs, char *key) |
---|
900 | { |
---|
901 | for (; pairs->p_name; pairs++) |
---|
902 | if (!strcasecmp (pairs->p_name, key)) |
---|
903 | return pairs; |
---|
904 | |
---|
905 | return NULL; |
---|
906 | } |
---|
907 | |
---|
908 | |
---|
909 | /* |
---|
910 | * Check utmp(x) file to see if user is currently |
---|
911 | * logged in. |
---|
912 | */ |
---|
913 | |
---|
914 | static int |
---|
915 | logged_in (void) |
---|
916 | { |
---|
917 | struct utmp ut; |
---|
918 | FILE *uf; |
---|
919 | |
---|
920 | if (utmped) |
---|
921 | return utmped; |
---|
922 | |
---|
923 | if ((uf = fopen (UTMP_FILE, "r")) == NULL) |
---|
924 | return NOTOK; |
---|
925 | |
---|
926 | while (fread ((char *) &ut, sizeof(ut), 1, uf) == 1) { |
---|
927 | if (ut.ut_name[0] != 0 |
---|
928 | && strncmp (user, ut.ut_name, sizeof(ut.ut_name)) == 0) { |
---|
929 | if (debug) |
---|
930 | continue; |
---|
931 | fclose (uf); |
---|
932 | return (utmped = DONE); |
---|
933 | } |
---|
934 | } |
---|
935 | |
---|
936 | fclose (uf); |
---|
937 | return (utmped = NOTOK); |
---|
938 | } |
---|
939 | |
---|
940 | |
---|
941 | #define check(t,a,b) if (t < a || t > b) return -1 |
---|
942 | #define cmpar(h1,m1,h2,m2) if (h1 < h2 || (h1 == h2 && m1 < m2)) return 0 |
---|
943 | |
---|
944 | static int |
---|
945 | timely (char *t1, char *t2) |
---|
946 | { |
---|
947 | int t1hours, t1mins, t2hours, t2mins; |
---|
948 | |
---|
949 | if (sscanf (t1, "%d:%d", &t1hours, &t1mins) != 2) |
---|
950 | return -1; |
---|
951 | check (t1hours, 0, 23); |
---|
952 | check (t1mins, 0, 59); |
---|
953 | |
---|
954 | if (sscanf (t2, "%d:%d", &t2hours, &t2mins) != 2) |
---|
955 | return -1; |
---|
956 | check (t2hours, 0, 23); |
---|
957 | check (t2mins, 0, 59); |
---|
958 | |
---|
959 | cmpar (now->tw_hour, now->tw_min, t1hours, t1mins); |
---|
960 | cmpar (t2hours, t2mins, now->tw_hour, now->tw_min); |
---|
961 | |
---|
962 | return -1; |
---|
963 | } |
---|
964 | |
---|
965 | |
---|
966 | /* |
---|
967 | * Deliver message by appending to a file. |
---|
968 | */ |
---|
969 | |
---|
970 | static int |
---|
971 | usr_file (int fd, char *mailbox, int mbx_style) |
---|
972 | { |
---|
973 | int md, mapping; |
---|
974 | |
---|
975 | if (verbose) |
---|
976 | verbose_printf ("delivering to file \"%s\"", mailbox); |
---|
977 | |
---|
978 | if (mbx_style == MBOX_FORMAT) { |
---|
979 | if (verbose) |
---|
980 | verbose_printf (" (mbox style)"); |
---|
981 | mapping = 0; |
---|
982 | } else { |
---|
983 | if (verbose) |
---|
984 | verbose_printf (" (mmdf style)"); |
---|
985 | mapping = 1; |
---|
986 | } |
---|
987 | |
---|
988 | /* open and lock the file */ |
---|
989 | if ((md = mbx_open (mailbox, mbx_style, pw->pw_uid, pw->pw_gid, m_gmprot())) == -1) { |
---|
990 | if (verbose) |
---|
991 | adorn ("", "unable to open:"); |
---|
992 | return -1; |
---|
993 | } |
---|
994 | |
---|
995 | lseek (fd, (off_t) 0, SEEK_SET); |
---|
996 | |
---|
997 | /* append message to file */ |
---|
998 | if (mbx_copy (mailbox, mbx_style, md, fd, mapping, NULL, verbose) == -1) { |
---|
999 | if (verbose) |
---|
1000 | adorn ("", "error writing to:"); |
---|
1001 | return -1; |
---|
1002 | } |
---|
1003 | |
---|
1004 | /* close and unlock file */ |
---|
1005 | if (mbx_close (mailbox, md) == NOTOK) { |
---|
1006 | if (verbose) |
---|
1007 | adorn ("", "error writing to:"); |
---|
1008 | return -1; |
---|
1009 | } |
---|
1010 | |
---|
1011 | if (verbose) |
---|
1012 | verbose_printf (", success.\n"); |
---|
1013 | return 0; |
---|
1014 | } |
---|
1015 | |
---|
1016 | |
---|
1017 | /* |
---|
1018 | * Deliver message to a nmh folder. |
---|
1019 | */ |
---|
1020 | |
---|
1021 | static int |
---|
1022 | usr_folder (int fd, char *string) |
---|
1023 | { |
---|
1024 | int status; |
---|
1025 | char folder[BUFSIZ], *vec[3]; |
---|
1026 | |
---|
1027 | /* get folder name ready */ |
---|
1028 | if (*string == '+') |
---|
1029 | strncpy(folder, string, sizeof(folder)); |
---|
1030 | else |
---|
1031 | snprintf(folder, sizeof(folder), "+%s", string); |
---|
1032 | |
---|
1033 | if (verbose) |
---|
1034 | verbose_printf ("delivering to folder \"%s\"", folder + 1); |
---|
1035 | |
---|
1036 | vec[0] = "rcvstore"; |
---|
1037 | vec[1] = folder; |
---|
1038 | vec[2] = NULL; |
---|
1039 | |
---|
1040 | /* use rcvstore to put message in folder */ |
---|
1041 | status = usr_pipe (fd, "rcvstore", rcvstoreproc, vec, 1); |
---|
1042 | |
---|
1043 | #if 0 |
---|
1044 | /* |
---|
1045 | * Currently, verbose status messages are handled by usr_pipe(). |
---|
1046 | */ |
---|
1047 | if (verbose) { |
---|
1048 | if (status == 0) |
---|
1049 | verbose_printf (", success.\n"); |
---|
1050 | else |
---|
1051 | verbose_printf (", failed.\n"); |
---|
1052 | } |
---|
1053 | #endif |
---|
1054 | |
---|
1055 | return status; |
---|
1056 | } |
---|
1057 | |
---|
1058 | /* |
---|
1059 | * Deliver message to a process. |
---|
1060 | */ |
---|
1061 | |
---|
1062 | static int |
---|
1063 | usr_pipe (int fd, char *cmd, char *pgm, char **vec, int suppress) |
---|
1064 | { |
---|
1065 | pid_t child_id; |
---|
1066 | int i, bytes, seconds, status; |
---|
1067 | struct stat st; |
---|
1068 | |
---|
1069 | if (verbose && !suppress) |
---|
1070 | verbose_printf ("delivering to pipe \"%s\"", cmd); |
---|
1071 | |
---|
1072 | lseek (fd, (off_t) 0, SEEK_SET); |
---|
1073 | |
---|
1074 | for (i = 0; (child_id = fork()) == -1 && i < 5; i++) |
---|
1075 | sleep (5); |
---|
1076 | |
---|
1077 | switch (child_id) { |
---|
1078 | case -1: |
---|
1079 | /* fork error */ |
---|
1080 | if (verbose) |
---|
1081 | adorn ("fork", "unable to"); |
---|
1082 | return -1; |
---|
1083 | |
---|
1084 | case 0: |
---|
1085 | /* child process */ |
---|
1086 | if (fd != 0) |
---|
1087 | dup2 (fd, 0); |
---|
1088 | freopen ("/dev/null", "w", stdout); |
---|
1089 | freopen ("/dev/null", "w", stderr); |
---|
1090 | if (fd != 3) |
---|
1091 | dup2 (fd, 3); |
---|
1092 | closefds (4); |
---|
1093 | |
---|
1094 | #ifdef TIOCNOTTY |
---|
1095 | if ((fd = open ("/dev/tty", O_RDWR)) != -1) { |
---|
1096 | ioctl (fd, TIOCNOTTY, NULL); |
---|
1097 | close (fd); |
---|
1098 | } |
---|
1099 | #endif /* TIOCNOTTY */ |
---|
1100 | |
---|
1101 | setpgid ((pid_t) 0, getpid ()); /* put in own process group */ |
---|
1102 | |
---|
1103 | *environ = NULL; |
---|
1104 | m_putenv ("USER", pw->pw_name); |
---|
1105 | m_putenv ("HOME", pw->pw_dir); |
---|
1106 | m_putenv ("SHELL", pw->pw_shell); |
---|
1107 | |
---|
1108 | execvp (pgm, vec); |
---|
1109 | _exit (-1); |
---|
1110 | |
---|
1111 | default: |
---|
1112 | /* parent process */ |
---|
1113 | if (!setjmp (myctx)) { |
---|
1114 | SIGNAL (SIGALRM, alrmser); |
---|
1115 | bytes = fstat (fd, &st) != -1 ? (int) st.st_size : 100; |
---|
1116 | |
---|
1117 | /* amount of time to wait depends on message size */ |
---|
1118 | if (bytes <= 100) { |
---|
1119 | /* give at least 5 minutes */ |
---|
1120 | seconds = 300; |
---|
1121 | } else if (bytes >= 90000) { |
---|
1122 | /* a half hour is long enough */ |
---|
1123 | seconds = 1800; |
---|
1124 | } else { |
---|
1125 | seconds = (bytes / 60) + 300; |
---|
1126 | } |
---|
1127 | alarm ((unsigned int) seconds); |
---|
1128 | status = pidwait (child_id, 0); |
---|
1129 | alarm (0); |
---|
1130 | |
---|
1131 | #ifdef MMDFI |
---|
1132 | if (status == RP_MOK || status == RP_OK) |
---|
1133 | status = 0; |
---|
1134 | #endif |
---|
1135 | if (verbose) { |
---|
1136 | if (status == 0) |
---|
1137 | verbose_printf (", success.\n"); |
---|
1138 | else |
---|
1139 | if ((status & 0xff00) == 0xff00) |
---|
1140 | verbose_printf (", system error\n"); |
---|
1141 | else |
---|
1142 | pidstatus (status, stdout, ", failed"); |
---|
1143 | } |
---|
1144 | return (status == 0 ? 0 : -1); |
---|
1145 | } else { |
---|
1146 | /* |
---|
1147 | * Ruthlessly kill the child and anything |
---|
1148 | * else in its process group. |
---|
1149 | */ |
---|
1150 | KILLPG(child_id, SIGKILL); |
---|
1151 | if (verbose) |
---|
1152 | verbose_printf (", timed-out; terminated\n"); |
---|
1153 | return -1; |
---|
1154 | } |
---|
1155 | } |
---|
1156 | } |
---|
1157 | |
---|
1158 | |
---|
1159 | static RETSIGTYPE |
---|
1160 | alrmser (int i) |
---|
1161 | { |
---|
1162 | #ifndef RELIABLE_SIGNALS |
---|
1163 | SIGNAL (SIGALRM, alrmser); |
---|
1164 | #endif |
---|
1165 | |
---|
1166 | longjmp (myctx, DONE); |
---|
1167 | } |
---|
1168 | |
---|
1169 | |
---|
1170 | /* |
---|
1171 | * Get the `sender' from the envelope |
---|
1172 | * information ("From " line). |
---|
1173 | */ |
---|
1174 | |
---|
1175 | static void |
---|
1176 | get_sender (char *envelope, char **sender) |
---|
1177 | { |
---|
1178 | int i; |
---|
1179 | char *cp; |
---|
1180 | char buffer[BUFSIZ]; |
---|
1181 | |
---|
1182 | if (envelope == NULL) { |
---|
1183 | *sender = getcpy (""); |
---|
1184 | return; |
---|
1185 | } |
---|
1186 | |
---|
1187 | i = strlen ("From "); |
---|
1188 | strncpy (buffer, envelope + i, sizeof(buffer)); |
---|
1189 | if ((cp = strchr(buffer, '\n'))) { |
---|
1190 | *cp = 0; |
---|
1191 | cp -= 24; |
---|
1192 | if (cp < buffer) |
---|
1193 | cp = buffer; |
---|
1194 | } else { |
---|
1195 | cp = buffer; |
---|
1196 | } |
---|
1197 | *cp = 0; |
---|
1198 | |
---|
1199 | for (cp = buffer + strlen (buffer) - 1; cp >= buffer; cp--) |
---|
1200 | if (isspace (*cp)) |
---|
1201 | *cp = 0; |
---|
1202 | else |
---|
1203 | break; |
---|
1204 | *sender = getcpy (buffer); |
---|
1205 | } |
---|
1206 | |
---|
1207 | |
---|
1208 | /* |
---|
1209 | * Copy message into a temporary file. |
---|
1210 | * While copying, it will do some header processing |
---|
1211 | * including the extraction of the envelope information. |
---|
1212 | */ |
---|
1213 | |
---|
1214 | static int |
---|
1215 | copy_message (int qd, char *tmpfil, int fold) |
---|
1216 | { |
---|
1217 | int i, first = 1, fd1, fd2; |
---|
1218 | char buffer[BUFSIZ]; |
---|
1219 | FILE *qfp, *ffp; |
---|
1220 | |
---|
1221 | strcpy (tmpfil, m_tmpfil (invo_name)); |
---|
1222 | |
---|
1223 | /* open temporary file to put message in */ |
---|
1224 | if ((fd1 = open (tmpfil, O_RDWR | O_CREAT | O_TRUNC, 0600)) == -1) |
---|
1225 | return -1; |
---|
1226 | |
---|
1227 | if (!fold) { |
---|
1228 | while ((i = read (qd, buffer, sizeof(buffer))) > 0) |
---|
1229 | if (write (fd1, buffer, i) != i) { |
---|
1230 | you_lose: |
---|
1231 | close (fd1); |
---|
1232 | unlink (tmpfil); |
---|
1233 | return -1; |
---|
1234 | } |
---|
1235 | if (i == -1) |
---|
1236 | goto you_lose; |
---|
1237 | lseek (fd1, (off_t) 0, SEEK_SET); |
---|
1238 | return fd1; |
---|
1239 | } |
---|
1240 | |
---|
1241 | /* dup the fd for incoming message */ |
---|
1242 | if ((fd2 = dup (qd)) == -1) { |
---|
1243 | close (fd1); |
---|
1244 | return -1; |
---|
1245 | } |
---|
1246 | |
---|
1247 | /* now create a FILE pointer for it */ |
---|
1248 | if ((qfp = fdopen (fd2, "r")) == NULL) { |
---|
1249 | close (fd1); |
---|
1250 | close (fd2); |
---|
1251 | return -1; |
---|
1252 | } |
---|
1253 | |
---|
1254 | /* dup the fd for temporary file */ |
---|
1255 | if ((fd2 = dup (fd1)) == -1) { |
---|
1256 | close (fd1); |
---|
1257 | fclose (qfp); |
---|
1258 | return -1; |
---|
1259 | } |
---|
1260 | |
---|
1261 | /* now create a FILE pointer for it */ |
---|
1262 | if ((ffp = fdopen (fd2, "r+")) == NULL) { |
---|
1263 | close (fd1); |
---|
1264 | close (fd2); |
---|
1265 | fclose (qfp); |
---|
1266 | return -1; |
---|
1267 | } |
---|
1268 | |
---|
1269 | /* |
---|
1270 | * copy message into temporary file |
---|
1271 | * and massage the headers. Save |
---|
1272 | * a copy of the "From " line for later. |
---|
1273 | */ |
---|
1274 | i = strlen ("From "); |
---|
1275 | while (fgets (buffer, sizeof(buffer), qfp)) { |
---|
1276 | if (first) { |
---|
1277 | first = 0; |
---|
1278 | if (!strncmp (buffer, "From ", i)) { |
---|
1279 | #ifdef RPATHS |
---|
1280 | char *fp, *cp, *hp, *ep; |
---|
1281 | #endif |
---|
1282 | /* get copy of envelope information ("From " line) */ |
---|
1283 | envelope = getcpy (buffer); |
---|
1284 | |
---|
1285 | #if 0 |
---|
1286 | /* First go ahead and put "From " line in message */ |
---|
1287 | fputs (buffer, ffp); |
---|
1288 | if (ferror (ffp)) |
---|
1289 | goto fputs_error; |
---|
1290 | #endif |
---|
1291 | |
---|
1292 | #ifdef RPATHS |
---|
1293 | /* |
---|
1294 | * Now create a "Return-Path:" line |
---|
1295 | * from the "From " line. |
---|
1296 | */ |
---|
1297 | hp = cp = strchr(fp = envelope + i, ' '); |
---|
1298 | while ((hp = strchr(++hp, 'r'))) |
---|
1299 | if (uprf (hp, "remote from")) { |
---|
1300 | hp = strrchr(hp, ' '); |
---|
1301 | break; |
---|
1302 | } |
---|
1303 | if (hp) { |
---|
1304 | /* return path for UUCP style addressing */ |
---|
1305 | ep = strchr(++hp, '\n'); |
---|
1306 | snprintf (buffer, sizeof(buffer), "Return-Path: %.*s!%.*s\n", |
---|
1307 | ep - hp, hp, cp - fp, fp); |
---|
1308 | } else { |
---|
1309 | /* return path for standard domain addressing */ |
---|
1310 | snprintf (buffer, sizeof(buffer), "Return-Path: %.*s\n", |
---|
1311 | cp - fp, fp); |
---|
1312 | } |
---|
1313 | |
---|
1314 | /* Add Return-Path header to message */ |
---|
1315 | fputs (buffer, ffp); |
---|
1316 | if (ferror (ffp)) |
---|
1317 | goto fputs_error; |
---|
1318 | #endif |
---|
1319 | /* Put the delivery date in message */ |
---|
1320 | fputs (ddate, ffp); |
---|
1321 | if (ferror (ffp)) |
---|
1322 | goto fputs_error; |
---|
1323 | |
---|
1324 | continue; |
---|
1325 | } |
---|
1326 | } |
---|
1327 | |
---|
1328 | fputs (buffer, ffp); |
---|
1329 | if (ferror (ffp)) |
---|
1330 | goto fputs_error; |
---|
1331 | } |
---|
1332 | |
---|
1333 | fclose (ffp); |
---|
1334 | if (ferror (qfp)) { |
---|
1335 | close (fd1); |
---|
1336 | fclose (qfp); |
---|
1337 | return -1; |
---|
1338 | } |
---|
1339 | fclose (qfp); |
---|
1340 | lseek (fd1, (off_t) 0, SEEK_SET); |
---|
1341 | return fd1; |
---|
1342 | |
---|
1343 | |
---|
1344 | fputs_error: |
---|
1345 | close (fd1); |
---|
1346 | fclose (ffp); |
---|
1347 | fclose (qfp); |
---|
1348 | return -1; |
---|
1349 | } |
---|
1350 | |
---|
1351 | /* |
---|
1352 | * Trim strings for pretty printing of debugging output |
---|
1353 | */ |
---|
1354 | |
---|
1355 | static char * |
---|
1356 | trim (char *cp) |
---|
1357 | { |
---|
1358 | char buffer[BUFSIZ*4]; |
---|
1359 | char *bp, *sp; |
---|
1360 | |
---|
1361 | if (cp == NULL) |
---|
1362 | return NULL; |
---|
1363 | |
---|
1364 | /* copy string into temp buffer */ |
---|
1365 | strncpy (buffer, cp, sizeof(buffer)); |
---|
1366 | bp = buffer; |
---|
1367 | |
---|
1368 | /* skip over leading whitespace */ |
---|
1369 | while (isspace(*bp)) |
---|
1370 | bp++; |
---|
1371 | |
---|
1372 | /* start at the end and zap trailing whitespace */ |
---|
1373 | for (sp = bp + strlen(bp) - 1; sp >= bp; sp--) { |
---|
1374 | if (isspace(*sp)) |
---|
1375 | *sp = 0; |
---|
1376 | else |
---|
1377 | break; |
---|
1378 | } |
---|
1379 | |
---|
1380 | /* replace remaining whitespace with spaces */ |
---|
1381 | for (sp = bp; *sp; sp++) |
---|
1382 | if (isspace(*sp)) |
---|
1383 | *sp = ' '; |
---|
1384 | |
---|
1385 | /* now return a copy */ |
---|
1386 | return getcpy(bp); |
---|
1387 | } |
---|
1388 | |
---|
1389 | /* |
---|
1390 | * Function for printing `verbose' messages. |
---|
1391 | */ |
---|
1392 | |
---|
1393 | static void |
---|
1394 | verbose_printf (char *fmt, ...) |
---|
1395 | { |
---|
1396 | va_list ap; |
---|
1397 | |
---|
1398 | va_start(ap, fmt); |
---|
1399 | vfprintf (stdout, fmt, ap); |
---|
1400 | va_end(ap); |
---|
1401 | |
---|
1402 | fflush (stdout); /* now flush output */ |
---|
1403 | } |
---|
1404 | |
---|
1405 | |
---|
1406 | /* |
---|
1407 | * Function for printing `verbose' delivery |
---|
1408 | * error messages. |
---|
1409 | */ |
---|
1410 | |
---|
1411 | static void |
---|
1412 | adorn (char *what, char *fmt, ...) |
---|
1413 | { |
---|
1414 | va_list ap; |
---|
1415 | int eindex; |
---|
1416 | char *s; |
---|
1417 | |
---|
1418 | eindex = errno; /* save the errno */ |
---|
1419 | fprintf (stdout, ", "); |
---|
1420 | |
---|
1421 | va_start(ap, fmt); |
---|
1422 | vfprintf (stdout, fmt, ap); |
---|
1423 | va_end(ap); |
---|
1424 | |
---|
1425 | if (what) { |
---|
1426 | if (*what) |
---|
1427 | fprintf (stdout, " %s: ", what); |
---|
1428 | if ((s = strerror (eindex))) |
---|
1429 | fprintf (stdout, "%s", s); |
---|
1430 | else |
---|
1431 | fprintf (stdout, "Error %d", eindex); |
---|
1432 | } |
---|
1433 | |
---|
1434 | fputc ('\n', stdout); |
---|
1435 | fflush (stdout); |
---|
1436 | } |
---|
1437 | |
---|
1438 | |
---|
1439 | /* |
---|
1440 | * Function for printing `debug' messages. |
---|
1441 | */ |
---|
1442 | |
---|
1443 | static void |
---|
1444 | debug_printf (char *fmt, ...) |
---|
1445 | { |
---|
1446 | va_list ap; |
---|
1447 | |
---|
1448 | va_start(ap, fmt); |
---|
1449 | vfprintf (stderr, fmt, ap); |
---|
1450 | va_end(ap); |
---|
1451 | } |
---|
1452 | |
---|
1453 | |
---|
1454 | /* |
---|
1455 | * Check ndbm/db file(s) to see if the Message-Id of this |
---|
1456 | * message matches the Message-Id of a previous message, |
---|
1457 | * so we can discard it. If it doesn't match, we add the |
---|
1458 | * Message-Id of this message to the ndbm/db file. |
---|
1459 | */ |
---|
1460 | static int |
---|
1461 | suppress_duplicates (int fd, char *file) |
---|
1462 | { |
---|
1463 | int fd1, lockfd, state, result; |
---|
1464 | char *cp, buf[BUFSIZ], name[NAMESZ]; |
---|
1465 | datum key, value; |
---|
1466 | DBM *db; |
---|
1467 | FILE *in; |
---|
1468 | |
---|
1469 | if ((fd1 = dup (fd)) == -1) |
---|
1470 | return -1; |
---|
1471 | if (!(in = fdopen (fd1, "r"))) { |
---|
1472 | close (fd1); |
---|
1473 | return -1; |
---|
1474 | } |
---|
1475 | rewind (in); |
---|
1476 | |
---|
1477 | for (state = FLD;;) { |
---|
1478 | state = m_getfld (state, name, buf, sizeof(buf), in); |
---|
1479 | switch (state) { |
---|
1480 | case FLD: |
---|
1481 | case FLDPLUS: |
---|
1482 | case FLDEOF: |
---|
1483 | /* Search for the message ID */ |
---|
1484 | if (strcasecmp (name, "Message-ID")) { |
---|
1485 | while (state == FLDPLUS) |
---|
1486 | state = m_getfld (state, name, buf, sizeof(buf), in); |
---|
1487 | continue; |
---|
1488 | } |
---|
1489 | |
---|
1490 | cp = add (buf, NULL); |
---|
1491 | while (state == FLDPLUS) { |
---|
1492 | state = m_getfld (state, name, buf, sizeof(buf), in); |
---|
1493 | cp = add (buf, cp); |
---|
1494 | } |
---|
1495 | key.dptr = trimcpy (cp); |
---|
1496 | key.dsize = strlen (key.dptr) + 1; |
---|
1497 | free (cp); |
---|
1498 | cp = key.dptr; |
---|
1499 | |
---|
1500 | if (!(db = dbm_open (file, O_RDWR | O_CREAT, 0600))) { |
---|
1501 | advise (file, "unable to perform dbm_open on"); |
---|
1502 | free (cp); |
---|
1503 | fclose (in); |
---|
1504 | return -1; |
---|
1505 | } |
---|
1506 | /* |
---|
1507 | * Since it is difficult to portable lock a ndbm file, |
---|
1508 | * we will open and lock the Maildelivery file instead. |
---|
1509 | * This will fail if your Maildelivery file doesn't |
---|
1510 | * exist. |
---|
1511 | */ |
---|
1512 | if ((lockfd = lkopen(file, O_RDWR, 0)) == -1) { |
---|
1513 | advise (file, "unable to perform file locking on"); |
---|
1514 | free (cp); |
---|
1515 | fclose (in); |
---|
1516 | return -1; |
---|
1517 | } |
---|
1518 | value = dbm_fetch (db, key); |
---|
1519 | if (value.dptr) { |
---|
1520 | if (verbose) |
---|
1521 | verbose_printf ("Message-ID: %s\n already received on %s", |
---|
1522 | cp, value.dptr); |
---|
1523 | result = DONE; |
---|
1524 | } else { |
---|
1525 | value.dptr = ddate + sizeof("Delivery-Date:"); |
---|
1526 | value.dsize = strlen(value.dptr) + 1; |
---|
1527 | if (dbm_store (db, key, value, DBM_INSERT)) |
---|
1528 | advise (file, "possibly corrupt file"); |
---|
1529 | result = 0; |
---|
1530 | } |
---|
1531 | |
---|
1532 | dbm_close (db); |
---|
1533 | lkclose(lockfd, file); |
---|
1534 | free (cp); |
---|
1535 | fclose (in); |
---|
1536 | return result; |
---|
1537 | break; |
---|
1538 | |
---|
1539 | case BODY: |
---|
1540 | case BODYEOF: |
---|
1541 | case FILEEOF: |
---|
1542 | break; |
---|
1543 | |
---|
1544 | case LENERR: |
---|
1545 | case FMTERR: |
---|
1546 | default: |
---|
1547 | break; |
---|
1548 | } |
---|
1549 | |
---|
1550 | break; |
---|
1551 | } |
---|
1552 | |
---|
1553 | fclose (in); |
---|
1554 | return 0; |
---|
1555 | } |
---|