Added all of the MH sources, including RCS files, in
[mmh] / docs / historical / mh-6.8.5 / uip / slocal.c
1 /* slocal.c - MH style mailer to write to a local user's mailbox */
2 #ifndef lint
3 static char ident[] = "@(#)$Id: slocal.c,v 1.25 1995/12/07 00:01:25 jromine Exp shettich $";
4 #endif  /* lint */
5
6 /* This program implements mail delivery in the MH/MMDF style.
7
8    Under SendMail, users should add the line
9
10         "| /usr/local/lib/mh/slocal"
11
12    to their $HOME/.forward file.
13
14    Under MMDF-I, users should (symbolically) link /usr/local/lib/mh/slocal
15    to $HOME/bin/rcvmail.
16
17    Under stand-alone MH, post will automatically run this during local
18    delivery.
19
20    This program should be used ONLY if you have "mts sendmail" or "mts mh"
21    or "mts mmdf1" set in your MH configuration.
22  */
23
24 /* \f */
25
26 #include "../h/mh.h"
27 #include "../h/dropsbr.h"
28 #include "../h/rcvmail.h"
29 #include "../zotnet/tws.h"
30 #include "../zotnet/mts.h"
31 #include <pwd.h>
32 #include <signal.h>
33 #ifndef V7
34 #ifndef NOIOCTLH
35 #include <sys/ioctl.h>
36 #endif  /* NOIOCTLH */
37 #endif  /* not V7 */
38 #include <sys/stat.h>
39 #include <utmp.h>
40 #ifdef LOCALE
41 #include        <locale.h>
42 #endif
43
44
45 #ifdef  MSGID
46
47 #undef  DBM             /* used by ndbm.h */
48 #include <ndbm.h>
49 #include <stdio.h>
50 #include <sys/types.h>
51 #include <sys/file.h>
52 #ifdef  SYS5
53 #include <fcntl.h>
54 #endif
55 #ifdef UNISTD
56 #include <unistd.h>
57 #endif
58 #if defined(LOCKF) && !defined(F_ULOCK)
59 #include <sys/fcntl.h>
60 #endif  /* LOCKF */
61
62 #endif
63
64
65 #define NVEC    100
66
67 /* \f */
68
69 static struct swit switches[] = {
70 #define ADDRSW  0
71     "addr address", 0,
72 #define USERSW  1
73     "user name", 0,
74 #define FILESW  2
75     "file file", 0,
76 #define SENDSW  3
77     "sender address", 0,
78 #define MBOXSW  4
79     "mailbox file", 0,
80 #define HOMESW  5
81     "home directory", -4,
82 #define INFOSW  6
83     "info data", 0,
84
85 #define MAILSW  7
86     "maildelivery file", 0,
87
88 #define VERBSW  8
89     "verbose", 0,
90 #define NVERBSW 9
91     "noverbose", 0,
92
93 #define DEBUGSW 10
94     "debug", 0,
95
96 #define HELPSW  11
97     "help", 4,
98
99     NULL, 0
100 };
101
102 /* \f */
103
104 static int  debug = 0;
105 static int  globbed = 0;
106 static int  parsed = 0;
107 static int  utmped = 0;
108 static int  verbose = 0;
109
110 static char *addr = NULLCP;
111 static char *user = NULLCP;
112 static char *info = NULLCP;
113 static char *file = NULLCP;
114 static char *sender = NULLCP;
115 static char *unixfrom = NULLCP;
116 static char *mbox = NULLCP;
117 static char *home = NULLCP;
118
119
120 static struct passwd *pw;
121
122
123 static char ddate[BUFSIZ];
124
125 struct tws *now;
126
127
128 static jmp_buf myctx;
129
130 /* \f */
131
132 struct pair {
133     char   *p_name;
134     char   *p_value;
135
136     char    p_flags;
137 #define P_NIL   0x00
138 #define P_ADR   0x01
139 #define P_HID   0x02
140 #define P_CHK   0x04
141 };
142
143 static struct pair *lookup ();
144
145
146 static struct pair  hdrs[NVEC + 1] = {
147     "source", NULL, P_HID,
148     "addr", NULL, P_HID,
149
150     "Return-Path", NULL, P_ADR,
151     "Reply-To", NULL, P_ADR,
152     "From", NULL, P_ADR,
153     "Sender", NULL, P_ADR,
154     "To", NULL, P_ADR,
155     "cc", NULL, P_ADR,
156     "Resent-Reply-To", NULL, P_ADR,
157     "Resent-From", NULL, P_ADR,
158     "Resent-Sender", NULL, P_ADR,
159     "Resent-To", NULL, P_ADR,
160     "Resent-cc", NULL, P_ADR,
161
162     NULL
163 };
164
165
166 static struct pair  vars[] = {
167     "sender", NULL, P_NIL,
168     "address", NULL, P_NIL,
169     "size", NULL, P_NIL,
170     "reply-to", NULL, P_CHK,
171     "info", NULL, P_NIL,
172
173     NULL
174 };
175
176 /* \f */
177
178 extern char **environ;
179
180 static void     adorn ();
181 static TYPESIG  alrmser ();
182
183
184 off_t    lseek ();
185 #ifndef __STDC__
186 #ifdef  SYS5
187 struct passwd *getpwnam ();
188 #endif  /* SYS5 */
189 #endif
190 static int      localmail(), usr_delivery(), split(), parse(), logged_in();
191 static int      timely(), usr_file(), usr_pipe(), copyfile();
192 static expand(), glob(), copyinfo();
193
194 /* \f */
195
196 /* ARGSUSED */
197
198 main (argc, argv, envp)
199 int     argc;
200 char  **argv,
201       **envp;
202 {
203     int     fd;
204     FILE   *fp    = stdin;
205     char   *cp,
206            *mdlvr = NULL,
207             buf[100],
208             from[BUFSIZ],
209             mailbox[BUFSIZ],
210             tmpfil[BUFSIZ],
211           **argp = argv + 1;
212
213 #ifdef LOCALE
214         setlocale(LC_ALL, "");
215 #endif
216     invo_name = r1bindex (*argv, '/');
217     m_foil (NULLCP);
218     mts_init (invo_name);
219
220 /* \f */
221
222     while (cp = *argp++) {
223         if (*cp == '-')
224             switch (smatch (++cp, switches)) {
225                 case AMBIGSW: 
226                     ambigsw (cp, switches);
227                     done (1);
228                 case UNKWNSW: 
229                     adios (NULLCP, "-%s unknown", cp);
230                 case HELPSW: 
231                     (void) sprintf (buf, "%s [switches] [address info sender]",
232                             invo_name);
233                     help (buf, switches);
234                     done (1);
235
236                 case ADDRSW: 
237                     if (!(addr = *argp++))/* allow -xyz arguments */
238                         adios (NULLCP, "missing argument to %s", argp[-2]);
239                     continue;
240                 case INFOSW: 
241                     if (!(info = *argp++))/* allow -xyz arguments */
242                         adios (NULLCP, "missing argument to %s", argp[-2]);
243                     continue;
244                 case USERSW: 
245                     if (!(user = *argp++))/* allow -xyz arguments */
246                         adios (NULLCP, "missing argument to %s", argp[-2]);
247                     continue;
248                 case FILESW: 
249                     if (!(file = *argp++) || *file == '-')
250                         adios (NULLCP, "missing argument to %s", argp[-2]);
251                     continue;
252                 case SENDSW: 
253                     if (!(sender = *argp++))/* allow -xyz arguments */
254                         adios (NULLCP, "missing argument to %s", argp[-2]);
255                     continue;
256                 case MBOXSW: 
257                     if (!(mbox = *argp++) || *mbox == '-')
258                         adios (NULLCP, "missing argument to %s", argp[-2]);
259                     continue;
260                 case HOMESW: 
261                     if (!(home = *argp++) || *home == '-')
262                         adios (NULLCP, "missing argument to %s", argp[-2]);
263                     continue;
264
265                 case MAILSW: 
266                     if (!(cp = *argp++) || *cp == '-')
267                         adios (NULLCP, "missing argument to %s", argp[-2]);
268                     if (mdlvr)
269                         adios (NULLCP, "only one maildelivery file at a time!");
270                     mdlvr = cp;
271                     continue;
272
273                 case VERBSW: 
274                     verbose++;
275                     continue;
276                 case NVERBSW: 
277                     verbose = 0;
278                     continue;
279
280                 case DEBUGSW: 
281                     debug++;
282                     continue;
283             }
284
285         switch (argp - (argv + 1)) {
286             case 1: 
287                 addr = cp;
288                 break;
289
290             case 2: 
291                 info = cp;
292                 break;
293
294             case 3: 
295                 sender = cp;
296                 break;
297         }
298     }
299
300 /* \f */
301
302     if (addr == NULL)
303         addr = getusr ();
304     if (user == NULL)
305         user = (cp = index (addr, '.')) ? ++cp : addr;
306     if ((pw = getpwnam (user)) == NULL)
307         adios (NULLCP, "no such local user as %s", user);
308
309     if (chdir (pw -> pw_dir) == NOTOK)
310         (void) chdir ("/");
311     (void) umask (0077);
312
313     if (geteuid () == 0) {
314 #ifdef  BSD41A
315         (void) inigrp (pw -> pw_name, pw -> pw_gid);
316 #endif  /* BSD41A */
317         (void) setgid (pw -> pw_gid);
318 #ifdef  BSD42
319         (void) initgroups (pw -> pw_name, pw -> pw_gid);
320 #endif  /* BSD42 */
321         (void) setuid (pw -> pw_uid);
322     }
323     
324     if (info == NULL)
325         info = "";
326
327     setbuf (stdin, NULLCP);
328
329     if (file == NULL) {
330         if ((fd = copyfile (fileno (stdin), file = tmpfil, 1)) == NOTOK)
331             adios (NULLCP, "unable to create temporary file");
332         if (debug)
333             fprintf (stderr, "temporary file \"%s\" selected\n", tmpfil);
334         else
335             (void) unlink (tmpfil);
336         if ((fp = fdopen (fd, "r+")) == NULL)
337             adios (NULLCP, "unable to access temporary file");
338     }
339     else
340         fd = fileno (stdin);
341
342     from[0] = 0;
343     if (sender == NULL)
344         copyinfo (fp, from);
345
346
347     if (mbox == NULL) {
348         (void) sprintf (mailbox, "%s/%s",
349                 mmdfldir[0] ? mmdfldir : pw -> pw_dir,
350                 mmdflfil[0] ? mmdflfil : pw -> pw_name);
351         mbox = mailbox;
352     }
353     if (home == NULL)
354         home = pw -> pw_dir;
355
356     if ((now = dtwstime ()) == NULL)
357         adios (NULLCP, "unable to ascertain local time");
358     (void) sprintf (ddate, "Delivery-Date: %s\n", dtimenow ());
359
360     if (debug) {
361         fprintf (stderr, "addr=\"%s\" user=\"%s\" info=\"%s\" file=\"%s\"\n",
362                 addr, user, info, file);
363         fprintf (stderr, "sender=\"%s\" mbox=\"%s\" home=\"%s\" from=\"%s\"\n",
364                 sender ? sender : "", mbox, home, from);
365         fprintf (stderr, "ddate=\"%s\" now=%02d:%02d\n",
366                 ddate, now -> tw_hour, now -> tw_min);
367     }
368
369     done (localmail (fd, from, mdlvr) != NOTOK ? RCV_MOK : RCV_MBX);
370 }
371
372 /* \f */
373
374 static int  localmail (fd, from, mdlvr)
375 int     fd;
376 char   *from,
377        *mdlvr;
378 {
379 #ifdef  MSGID
380     struct stat st;
381
382     if (stat (".maildelivery.pag", &st) != NOTOK
383             && check_msgid (fd, ".maildelivery") == DONE)
384         return OK;
385 #endif
386
387     if (usr_delivery (fd, mdlvr ? mdlvr : ".maildelivery", 0, from) != NOTOK)
388         return OK;
389
390     if (usr_delivery (fd, maildelivery, 1, from) != NOTOK)
391         return OK;
392
393 #ifdef  notdef
394     if (verbose)
395         printf ("(invoking hook)\n");
396     if (usr_hook (fd, mbox) != NOTOK)
397         return OK;
398 #endif  /* notdef */
399
400     if (verbose)
401         printf ("(trying normal delivery)\n");
402     return usr_file (fd, mbox, from);
403 }
404
405 /* \f */
406
407 #define matches(a,b)    (stringdex (b, a) >= 0)
408
409 static int  usr_delivery (fd, delivery, su, from)
410 int     fd,
411         su;
412 char   *delivery,
413        *from;
414 {
415     int     i,
416             accept,
417             status,
418             won,
419             vecp,
420             next;
421     register char  *cp,
422                    *action,
423                    *field,
424                    *pattern,
425                    *string;
426     char    buffer[BUFSIZ],
427             tmpbuf[BUFSIZ],
428            *vec[NVEC];
429     struct stat st;
430     register struct pair   *p;
431     register FILE  *fp;
432
433     if ((fp = fopen (delivery, "r")) == NULL)
434         return NOTOK;
435     if (fstat (fileno (fp), &st) == NOTOK
436             || (st.st_uid != 0 && (su || st.st_uid != pw -> pw_uid))
437             || st.st_mode & 0022) {
438         if (verbose) {
439             printf ("%s: ownership/modes bad (%d, %d,%d,0%o)\n",
440                     delivery, su, pw -> pw_uid, st.st_uid, st.st_mode);
441             (void) fflush (stdout);
442         }
443         return NOTOK;
444     }
445
446     won = 0;
447     next = 1;
448     while (fgets (buffer, sizeof buffer, fp) != NULL) {
449         if (*buffer == '#')
450             continue;
451         if (cp = index (buffer, '\n'))
452             *cp = 0;
453         if ((vecp = split (buffer, vec)) < 5)
454             continue;
455         if (debug)
456             for (i = 0; vec[i]; i++)
457                 fprintf (stderr, "vec[%d]: \"%s\"\n", i, vec[i]);
458
459         field = vec[0];
460         pattern = vec[1];
461         action = vec[2];
462
463         switch (vec[3][0]) {
464             case 'N':
465             case 'n':
466                 if (! next)
467                         continue;  /* if previous condition failed, don't
468                                       do this - else fall through */
469
470             case '?': 
471                 if (won)
472                     continue;   /* else fall */
473             case 'A': 
474             case 'a': 
475                 accept = 1;
476                 break;
477
478             case 'R': 
479             case 'r': 
480             default: 
481                 accept = 0;
482                 break;
483         }
484
485         string = vec[4];
486
487         if (vecp > 5) {
488             if (uleq (vec[5], "select")) {
489                 if (logged_in () != NOTOK)
490                     continue;
491                 if (vecp > 7 && timely (vec[6], vec[7]) == NOTOK)
492                     continue;
493             }
494         }
495
496         switch (*field) {
497             case '*': 
498                 break;
499
500             case 'd': 
501                 if (uleq (field, "default")) {
502                     if (won)
503                         continue;
504                     break;
505                 }               /* else fall */
506
507             default: 
508                 if (!parsed && parse (fd) == NOTOK) {
509                     (void) fclose (fp);
510                     return NOTOK;
511                 }
512                 if ((p = lookup (hdrs, field)) == NULL
513                         || (p->p_value == NULL)                 /* XXX */
514                         || !matches (p -> p_value, pattern)) {
515                         next = 0;
516                         continue;
517                 }
518                 else
519                     next = 1;
520                 break;
521         }
522
523         switch (*action) {
524             case 'q':
525                 if (!uleq (action, "qpipe"))
526                     continue;   /* else fall */
527             case '^':
528                 expand (tmpbuf, string, fd);
529                 if (split (tmpbuf, vec) < 1)
530                     continue;
531                 status = usr_pipe (fd, tmpbuf, vec[0], vec);
532                 break;
533
534             case 'p': 
535                 if (!uleq (action, "pipe"))
536                     continue;   /* else fall */
537             case '|': 
538                 vec[2] = "sh";
539                 vec[3] = "-c";
540                 expand (tmpbuf, string, fd);
541                 vec[4] = tmpbuf;
542                 vec[5] = NULL;
543                 status = usr_pipe (fd, tmpbuf, "/bin/sh", vec + 2);
544                 break;
545
546             case 'f': 
547                 if (!uleq (action, "file"))
548                     continue;   /* else fall */
549             case '>': 
550 #ifdef  RPATHS
551                 status = usr_file (fd, string,  from);  /* UUCP format? */
552 #else
553                 status = usr_file (fd, string,  NULLCP);
554 #endif
555                 break;
556
557             case 'm':
558                 if (!uleq (action, "mbox"))
559                     continue;
560                 status = usr_file (fd, string,  NULLCP);
561                 break;
562
563             case 'd': 
564                 if (!uleq (action, "destroy"))
565                     continue;
566                 status = OK;
567                 break;
568         }
569
570         if (accept && status == OK)
571             won++;
572     }
573
574     (void) fclose (fp);
575     return (won ? OK : NOTOK);
576 }
577
578 /* \f */
579
580 #define QUOTE   '\\'
581
582 static int  split (cp, vec)
583 char   *cp,
584       **vec;
585 {
586     register int    i;
587     register char  *s;
588
589     for (i = 0, s = cp; i <= NVEC;) {
590         vec[i] = NULL;
591         while (isspace (*s) || *s == ',')
592             *s++ = 0;
593         if (*s == 0)
594             break;
595
596         if (*s == '"') {
597             for (vec[i++] = ++s; *s != 0 && *s != '"'; s++)
598                 if (*s == QUOTE) {
599                     if (*++s == '"')
600                         (void) strcpy (s - 1, s);
601                     s--;
602                 }
603             if (*s == '"')
604                 *s++ = 0;
605             continue;
606         }
607         if (*s == QUOTE && *++s != '"')
608             s--;
609         vec[i++] = s++;
610
611         while (*s != 0 && !isspace (*s) && *s != ',')
612             s++;
613     }
614     vec[i] = NULL;
615
616     return i;
617 }
618
619 /* \f */
620
621 static int  parse (fd)
622 register int    fd;
623 {
624     register int    i,
625                     state;
626     int     fd1;
627     register char  *cp,
628                    *dp,
629                    *lp;
630     char    name[NAMESZ],
631             field[BUFSIZ];
632     register struct pair   *p,
633                            *q;
634     register FILE  *in;
635
636     if (parsed++)
637         return OK;
638
639     if ((fd1 = dup (fd)) == NOTOK)
640         return NOTOK;
641     if ((in = fdopen (fd1, "r")) == NULL) {
642         (void) close (fd1);
643         return NOTOK;
644     }
645     rewind (in);
646
647     if (p = lookup (hdrs, "source"))
648         p -> p_value = getcpy (sender);
649     if (p = lookup (hdrs, "addr"))
650         p -> p_value = getcpy (addr);
651
652     for (i = 0, state = FLD;;) {
653         switch (state = m_getfld (state, name, field, sizeof field, in)) {
654             case FLD: 
655             case FLDEOF: 
656             case FLDPLUS: 
657                 lp = add (field, NULLCP);
658                 while (state == FLDPLUS) {
659                     state = m_getfld (state, name, field, sizeof field, in);
660                     lp = add (field, lp);
661                 }
662                 for (p = hdrs; p -> p_name; p++)
663                     if (uleq (p -> p_name, name)) {
664                         if (!(p -> p_flags & P_HID)) {
665                             if (cp = p -> p_value)
666                                 if (p -> p_flags & P_ADR) {
667                                     dp = cp + strlen (cp) - 1;
668                                     if (*dp == '\n')
669                                         *dp = 0;
670                                     cp = add (",\n\t", cp);
671                                 }
672                                 else
673                                     cp = add ("\t", cp);
674                             p -> p_value = add (lp, cp);
675                         }
676                         free (lp);
677                         break;
678                     }
679                 if (p -> p_name == NULL && i < NVEC) {
680                     p -> p_name = getcpy (name);
681                     p -> p_value = lp;
682                     p -> p_flags = P_NIL;
683                     p++, i++;
684                     p -> p_name = NULL;
685                 }
686                 if (state != FLDEOF)
687                     continue;
688                 break;
689
690             case BODY: 
691             case BODYEOF: 
692             case FILEEOF: 
693                 break;
694
695             case LENERR: 
696             case FMTERR: 
697                 advise (NULLCP, "format error in message");
698                 break;
699
700             default: 
701                 advise (NULLCP, "internal error");
702                 (void) fclose (in);
703                 return NOTOK;
704         }
705         break;
706     }
707     (void) fclose (in);
708
709     if (p = lookup (vars, "reply-to")) {
710         if ((q = lookup (hdrs, "reply-to")) == NULL || q -> p_value == NULL)
711             q = lookup (hdrs, "from");
712         p -> p_value = getcpy (q ? q -> p_value : "");
713         p -> p_flags &= ~P_CHK;
714         if (debug)
715             fprintf (stderr, "vars[%d]: name=\"%s\" value=\"%s\"\n",
716                     p - vars, p -> p_name, p -> p_value);
717     }
718 #define empty(s)        ((s) ? (s) : "")
719     if (debug)
720         for (p = hdrs; p -> p_name; p++)
721             fprintf (stderr, "hdrs[%d]: name=\"%s\" value=\"%s\"\n",
722                 p - hdrs, p -> p_name, empty(p -> p_value));
723 #undef  empty
724
725     return OK;
726 }
727
728 /* \f */
729
730 #define LPAREN  '('
731 #define RPAREN  ')'
732
733 static  expand (s1, s2, fd)
734 register char  *s1,
735                *s2;
736 int     fd;
737 {
738     register char   c,
739                    *cp;
740     register struct pair   *p;
741
742     if (!globbed)
743         glob (fd);
744
745     while (c = *s2++)
746         if (c != '$' || *s2 != LPAREN)
747             *s1++ = c;
748         else {
749             for (cp = ++s2; *s2 && *s2 != RPAREN; s2++)
750                 continue;
751             if (*s2 != RPAREN) {
752                 s2 = --cp;
753                 continue;
754             }
755             *s2++ = 0;
756             if (p = lookup (vars, cp)) {
757                 if (!parsed && (p -> p_flags & P_CHK))
758                     (void) parse (fd);
759
760                 (void) strcpy (s1, p -> p_value);
761                 s1 += strlen (s1);
762             }
763         }
764     *s1 = 0;
765 }
766
767 /* \f */
768
769 static  glob (fd)
770 register int  fd;
771 {
772     char buffer[BUFSIZ];
773     struct stat st;
774     register struct pair   *p;
775
776     if (globbed++)
777         return;
778
779     if (p = lookup (vars, "sender"))
780         p -> p_value = getcpy (sender);
781     if (p = lookup (vars, "address"))
782         p -> p_value = getcpy (addr);
783     if (p = lookup (vars, "size")) {
784         (void) sprintf (buffer, "%d",
785                 fstat (fd, &st) != NOTOK ? (int) st.st_size : 0);
786         p -> p_value = getcpy (buffer);
787     }
788     if (p = lookup (vars, "info"))
789         p -> p_value = getcpy (info);
790
791     if (debug)
792         for (p = vars; p -> p_name; p++)
793             fprintf (stderr, "vars[%d]: name=\"%s\" value=\"%s\"\n",
794                     p - vars, p -> p_name, p -> p_value);
795 }
796
797 /* \f */
798
799 static struct pair *lookup (pairs, key)
800 register struct pair   *pairs;
801 register char  *key;
802 {
803     register char  *cp;
804
805     for (; cp = pairs -> p_name; pairs++)
806         if (uleq (cp, key))
807             return pairs;
808
809     return NULL;
810 }
811
812 /* \f */
813
814 static int  logged_in () {
815     struct utmp ut;
816     register FILE  *uf;
817
818     if (utmped)
819         return utmped;
820
821     if ((uf = fopen ("/etc/utmp", "r")) == NULL)
822         return NOTOK;
823
824     while (fread ((char *) &ut, sizeof ut, 1, uf) == 1)
825         if (ut.ut_name[0] != 0
826                 && strncmp (user, ut.ut_name, sizeof ut.ut_name) == 0) {
827             if (debug)
828                 continue;
829             (void) fclose (uf);
830             return (utmped = DONE);
831         }
832
833     (void) fclose (uf);
834     return (utmped = NOTOK);
835 }
836
837
838 static int  timely (t1, t2)
839 char   *t1,
840        *t2;
841 {
842 #define check(t,a,b)            if (t < a || t > b) return NOTOK
843 #define cmpar(h1,m1,h2,m2)      if (h1 < h2 || (h1 == h2 && m1 < m2)) return OK
844
845     int     t1hours,
846             t1mins,
847             t2hours,
848             t2mins;
849
850     if (sscanf (t1, "%d:%d", &t1hours, &t1mins) != 2)
851         return NOTOK;
852     check (t1hours, 0, 23);
853     check (t1mins, 0, 59);
854
855     if (sscanf (t2, "%d:%d", &t2hours, &t2mins) != 2)
856         return NOTOK;
857     check (t2hours, 0, 23);
858     check (t2mins, 0, 59);
859
860     cmpar (now -> tw_hour, now -> tw_min, t1hours, t1mins);
861     cmpar (t2hours, t2mins, now -> tw_hour, now -> tw_min);
862
863     return NOTOK;
864 }
865
866 /* \f */
867
868 static int  usr_file (fd, mailbox, from)
869 int     fd;
870 char   *mailbox,
871        *from;
872 {
873     int     md,
874             mapping;
875     register char  *bp;
876     char    buffer[BUFSIZ];
877
878     if (verbose)
879         printf ("\tdelivering to file \"%s\"", mailbox);
880     if (from && *from) {
881         (void) mbx_uucp ();
882         if (verbose)
883             printf (" (uucp style)");
884         (void) sprintf (buffer, "%s%s", from, ddate);
885         bp = buffer;
886         mapping = 0;
887     }
888     else {
889         bp = ddate;
890         mapping = 1;
891     }
892     if (verbose)
893         (void) fflush (stdout);
894
895     if ((md = mbx_open (mailbox, pw -> pw_uid, pw -> pw_gid, m_gmprot ()))
896             == NOTOK) {
897         adorn ("", "unable to open:");
898         return NOTOK;
899     }
900
901     (void) lseek (fd, (off_t)0, 0);
902     if (mbx_copy (mailbox, md, fd, mapping, bp, verbose) == NOTOK) {
903         adorn ("", "error writing to:");
904         return NOTOK;
905     }
906
907     (void) mbx_close (mailbox, md);
908     if (verbose) {
909         printf (", done.\n");
910         (void) fflush (stdout);
911     }
912     return OK;
913 }
914
915 /* \f */
916
917 #ifdef  notdef
918 static int  usr_hook (fd, mailbox)
919 int     fd;
920 char   *mailbox;
921 {
922     int     i,
923             vecp;
924     char    receive[BUFSIZ],
925             tmpfil[BUFSIZ],
926            *vec[NVEC];
927
928     if ((fd = copyfile (fd, tmpfil, 0)) == NOTOK) {
929         if (verbose)
930             adorn ("unable to copy message; skipping hook\n");
931         return NOTOK;
932     }
933     (void) chown (tmpfil, pw -> pw_uid, pw -> pw_gid);
934
935     vecp = 1;
936     (void) sprintf (receive, "%s/.mh_receive", pw -> pw_dir);
937     switch (access (receive, 01)) {
938         case NOTOK: 
939             (void) sprintf (receive, "%s/bin/rcvmail", pw -> pw_dir);
940             if (access (receive, 01) == NOTOK) {
941                 (void) unlink (tmpfil);
942                 if (verbose) {
943                     printf ("\tnot present\n");
944                     (void) fflush (stdout);
945                 }
946                 return NOTOK;
947             }
948             vec[vecp++] = addr;
949             vec[vecp++] = tmpfil;
950             vec[vecp++] = sender;
951             break;
952
953         default: 
954             vec[vecp++] = tmpfil;
955             vec[vecp++] = mailbox;
956             vec[vecp++] = home;
957             vec[vecp++] = addr;
958             vec[vecp++] = sender;
959             break;
960     }
961     vec[0] = r1bindex (receive, '/');
962     vec[vecp] = NULL;
963
964     i = usr_pipe (fd, "rcvmail", receive, vec);
965     (void) unlink (tmpfil);
966
967     return i;
968 }
969 #endif  /* notdef */
970
971 /* \f */
972
973 static int  usr_pipe (fd, cmd, pgm, vec)
974 int     fd;
975 char   *cmd,
976        *pgm,
977       **vec;
978 {
979     int     bytes,
980             i,
981             child_id,
982             status;
983     struct stat st;
984
985     if (verbose) {
986         printf ("\tdelivering to pipe \"%s\"", cmd);
987         (void) fflush (stdout);
988     }
989     (void) lseek (fd, (off_t)0, 0);
990
991     for (i = 0; (child_id = fork ()) == NOTOK && i < 5; i++)
992         sleep (5);
993     switch (child_id) {
994         case NOTOK: 
995             adorn ("fork", "unable to");
996             return NOTOK;
997
998         case OK: 
999             if (fd != 0)
1000                 (void) dup2 (fd, 0);
1001             (void) freopen ("/dev/null", "w", stdout);
1002             (void) freopen ("/dev/null", "w", stderr);
1003             if (fd != 3)
1004                 (void) dup2 (fd, 3);
1005             closefds (4);
1006 #ifdef  TIOCNOTTY
1007             if ((fd = open ("/dev/tty", 2)) != NOTOK) {
1008                 (void) ioctl (fd, TIOCNOTTY, NULLCP);
1009                 (void) close (fd);
1010             }
1011 #endif  /* TIOCNOTTY */
1012 #ifdef  BSD42
1013             (void) setpgrp (0, getpid ());
1014 #endif  /* BSD42 */
1015
1016             *environ = NULL;
1017             (void) m_putenv ("USER", pw -> pw_name);
1018             (void) m_putenv ("HOME", pw -> pw_dir);
1019             (void) m_putenv ("SHELL", pw -> pw_shell);
1020
1021             execvp (pgm, vec);
1022             _exit (-1);
1023
1024         default: 
1025             switch (setjmp (myctx)) {
1026                 case OK: 
1027                     (void) signal (SIGALRM, alrmser);
1028                     bytes = fstat (fd, &st) != NOTOK ? (int) st.st_size : 100;
1029                     if (bytes <= 0)
1030                         bytes = 100;
1031                     (void) alarm ((unsigned) (bytes * 60 + 300));
1032
1033                     status = pidwait (child_id, OK);
1034
1035                     (void) alarm (0);
1036 #ifdef  MMDFI
1037                     if (status == RP_MOK || status == RP_OK)
1038                         status = 0;
1039 #endif  /* MMDFI */
1040                     if (verbose) {
1041                         if (status == 0)
1042                             printf (", wins.\n");
1043                         else
1044                             if ((status & 0xff00) == 0xff00)
1045                                 printf (", system error\n");
1046                             else
1047                                 (void) pidstatus (status, stdout, ", loses");
1048                         (void) fflush (stdout);
1049                     }
1050                     return (status == 0 ? OK : NOTOK);
1051
1052                 default: 
1053 #ifndef BSD42
1054                     (void) kill (child_id, SIGKILL);
1055 #else   /* BSD42 */
1056                     (void) killpg (child_id, SIGKILL);
1057 #endif  /* BSD42 */
1058                     if (verbose) {
1059                         printf (", timed-out; terminated\n");
1060                         (void) fflush (stdout);
1061                     }
1062                     return NOTOK;
1063             }
1064     }
1065 }
1066
1067 /* \f */
1068
1069 /* ARGSUSED */
1070
1071 static  TYPESIG alrmser (i)
1072 int     i;
1073 {
1074     longjmp (myctx, DONE);
1075 }
1076
1077 /* \f */
1078
1079 static  copyinfo (fp, from)
1080 register FILE   *fp;
1081 char    *from;
1082 {
1083     int     i;
1084     register char  *cp;
1085     static char buffer[BUFSIZ];
1086
1087     if (unixfrom)       /* interface from copyfile */
1088         strcpy (from, unixfrom);
1089     else if (fgets (from, BUFSIZ, fp) == NULL)
1090         adios (NULLCP, "no message");
1091
1092     if (strncmp (from, "From ", i = strlen ("From "))) {
1093         rewind (fp);
1094         *from = 0;
1095         return;
1096     }
1097
1098     (void) strcpy (buffer, from + i);
1099     if (cp = index (buffer, '\n')) {
1100         *cp = 0;
1101         cp -= 24;
1102         if (cp < buffer)
1103             cp = buffer;
1104     }
1105     else
1106         cp = buffer;
1107     *cp = 0;
1108
1109     for (cp = buffer + strlen (buffer) - 1; cp >= buffer; cp--)
1110         if (isspace (*cp))
1111             *cp = 0;
1112         else
1113             break;
1114     sender = buffer;
1115     rewind (fp);
1116 }
1117
1118 /* \f */
1119
1120 static int  copyfile (qd, tmpfil, fold)
1121 int     qd,
1122         fold;
1123 register char   *tmpfil;
1124 {
1125     register int    i,
1126                     first = 0,
1127                     fd1,
1128                     fd2;
1129     char    buffer[BUFSIZ];
1130     register FILE  *qfp,
1131                    *ffp;
1132
1133     (void) strcpy (tmpfil, m_tmpfil (invo_name));
1134     if ((fd1 = creat (tmpfil, 0600)) == NOTOK)
1135         return NOTOK;
1136     (void) close (fd1);
1137     if ((fd1 = open (tmpfil, 2)) == NOTOK)
1138         return NOTOK;
1139
1140     if (!fold) {
1141         while ((i = read (qd, buffer, sizeof buffer)) > 0)
1142             if (write (fd1, buffer, i) != i) {
1143 you_lose: ;
1144                 (void) close (fd1);
1145                 (void) unlink (tmpfil);
1146                 return NOTOK;
1147             }
1148         if (i == NOTOK)
1149             goto you_lose;
1150         (void) lseek (fd1, (off_t)0, 0);
1151         return fd1;
1152     }
1153
1154     if ((fd2 = dup (qd)) == NOTOK) {
1155         (void) close (fd1);
1156         return NOTOK;
1157     }
1158     if ((qfp = fdopen (fd2, "r")) == NULL) {
1159         (void) close (fd1);
1160         (void) close (fd2);
1161         return NOTOK;
1162     }
1163
1164     if ((fd2 = dup (fd1)) == NOTOK) {
1165         (void) close (fd1);
1166         (void) fclose (qfp);
1167         return NOTOK;
1168     }
1169     if ((ffp = fdopen (fd2, "r+")) == NULL) {
1170         (void) close (fd1);
1171         (void) close (fd2);
1172         (void) fclose (qfp);
1173         return NOTOK;
1174     }
1175
1176     i = strlen ("From ");
1177     while (fgets (buffer, sizeof buffer, qfp)) {
1178         if (!strncmp (buffer, "From ", i))
1179             if (first == 0) {
1180 #ifdef  RPATHS
1181                 register char *fp, *cp, *hp, *ep;
1182 #endif
1183                 unixfrom = getcpy (buffer);     /* save for later */
1184 #ifndef RPATHS
1185                 continue;                       /* but don't put in file */
1186 #else
1187                 hp = cp = index (fp = unixfrom + i, ' ');
1188                 while (hp = index (++hp, 'r'))
1189                     if (uprf (hp, "remote from")) {
1190                         hp = rindex (hp, ' ');
1191                         break;
1192                     }
1193                 if (hp) {
1194                     ep = rindex (++hp, '\n');
1195                     sprintf (buffer, "Return-Path: %.*s!%.*s\n",
1196                             ep - hp, hp,
1197                             cp - fp, fp);
1198                 }
1199                 else
1200                     sprintf (buffer, "Return-Path: %.*s\n",
1201                             cp - fp, fp);
1202 #endif
1203             }
1204 #ifdef  notdef          /* mbx_copy does this */
1205             else
1206                 putc ('>', ffp);
1207 #endif  /* notdef */
1208         first++;
1209         fputs (buffer, ffp);
1210         if (ferror (ffp)) {
1211             (void) close (fd1);
1212             (void) fclose (ffp);
1213             (void) fclose (qfp);
1214             return NOTOK;
1215         }
1216     }
1217
1218     (void) fclose (ffp);
1219     if (ferror (qfp)) {
1220         (void) close (fd1);
1221         (void) fclose (qfp);
1222         return NOTOK;
1223     }
1224     (void) fclose (qfp);
1225
1226     (void) lseek (fd1, (off_t)0, 0);
1227
1228     return fd1;
1229 }
1230
1231 /* \f */
1232
1233 /* VARARGS2 */
1234
1235 static void  adorn (what, fmt, a, b, c, d, e, f)
1236 char   *what,
1237        *fmt,
1238        *a,
1239        *b,
1240        *c,
1241        *d,
1242        *e,
1243        *f;
1244 {
1245     char   *cp = invo_name;
1246
1247     if (!verbose)
1248         return;
1249     printf (", ");
1250
1251     invo_name = NULL;
1252     advise (what, fmt, a, b, c, d, e, f);
1253     invo_name = cp;
1254 }
1255
1256 /* \f */
1257
1258 #ifdef  MSGID
1259
1260 static int  check_msgid (fd, file)
1261 int     fd;
1262 char   *file;
1263 {
1264     int     fd1,
1265             state;
1266     char   *cp,
1267             buf[BUFSIZ],
1268             name[NAMESZ];
1269     datum   key,
1270             value;
1271     DBM    *db;
1272     FILE   *in;
1273
1274     if ((fd1 = dup (fd)) == NOTOK)
1275         return NOTOK;
1276     if ((in = fdopen (fd1, "r")) == NULL) {
1277         (void) close (fd1);
1278         return NOTOK;
1279     }
1280     rewind (in);
1281
1282     for (state = FLD;;) {
1283         switch (state = m_getfld (state, name, buf, sizeof buf, in)) {
1284             case FLD:
1285             case FLDPLUS:
1286             case FLDEOF:
1287                 if (!uleq (name, "Message-ID")) {
1288                     while (state == FLDPLUS)
1289                         state = m_getfld (state, name, buf, sizeof buf, in);
1290                     continue;
1291                 }
1292
1293                 cp = add (buf, NULLCP);
1294                 while (state == FLDPLUS) {
1295                     state = m_getfld (state, name, buf, sizeof buf, in);
1296                     cp = add (buf, cp);
1297                 }
1298                 key.dsize = strlen (key.dptr = trimcpy (cp)) + 1;
1299                 free (cp);
1300                 cp = key.dptr;
1301
1302                 if ((db = dbm_open (file, O_RDWR | O_CREAT, 0600)) == NULL) {
1303                     advise (file, "unable to perform dbm_open on");
1304 out: ;
1305                     free (cp);
1306                     (void) fclose (in);
1307                     return NOTOK;
1308                 }
1309 #ifdef FCNTL
1310                 {
1311                     struct flock fl;
1312
1313                     fl.l_type = F_WRLCK;
1314                     fl.l_whence = 0;
1315                     fl.l_start = 0;
1316                     fl.l_len = 0;
1317                     if (fcntl (dbm_pagfno (db), F_SETLK, &fl) == -1) {
1318                         advise (file, "unable to perform flock on");
1319                         goto out;
1320                     }
1321                 }
1322 #else
1323 #ifdef LOCKF
1324                 if (lockf (dbm_pagfno (db), F_LOCK) == NOTOK) {
1325                     advise (file, "unable to perform lockf on");
1326                     goto out;
1327                 }
1328 #else
1329                 if (flock (dbm_pagfno (db), LOCK_EX) == NOTOK) {
1330                     advise (file, "unable to perform flock on");
1331                     goto out;
1332                 }
1333 #endif
1334 #endif
1335
1336                 value = dbm_fetch (db, key);
1337                 if (value.dptr != NULL) {
1338                     if (debug)
1339                         advise (NULLCP,
1340                                 "Message-ID: %s already received on\n\tDate:       %s",
1341                                 cp, value.dptr);
1342                     free (cp);
1343                     (void) fclose (in);
1344                     return DONE;
1345                 }
1346                            
1347                 value.dsize = strlen (value.dptr =
1348                                       ddate + sizeof "Delivery-Date:") + 1;
1349
1350                 if (dbm_store (db, key, value, DBM_INSERT))
1351                     advise (file, "possibly corrupt file");
1352
1353                 dbm_close (db);
1354
1355                 free (cp);
1356                 break;
1357
1358            case BODY:
1359            case BODYEOF:
1360            case FILEEOF:
1361                 break;
1362
1363            case LENERR:
1364            case FMTERR:
1365            default:
1366                 break;
1367         }
1368
1369         break;
1370     }
1371
1372     (void) fclose (in);
1373     return OK;
1374 }
1375 #endif