Added all of the MH sources, including RCS files, in
[mmh] / docs / historical / mh-6.8.5 / uip / umhook.c
1 /* umhook.c - one attempt at a rcvmail hook for UUCP mail */
2 #ifndef lint
3 static char ident[] = "@(#)$Id: umhook.c,v 1.4 1993/08/25 17:29:36 jromine Exp $";
4 #endif  lint
5
6 /* I don't comment my code heavily, so read this...
7
8     You run this program from your .login file.  The invocation is simply
9     "umhook".  The program "detaches" itself and runs unattended until you
10     logout.  Whenever you get UUCP mail (or upto a minute afterwards),
11     umhook will filter your UUCP mail drop to a temporary file.  The mail
12     drop is *NOT* touched beyond this (even the access time remains the
13     same).  For each message that was new in the mail drop, umhook will
14     fork a process to interpret your .maildelivery file.
15
16     The umhook program uses the -ljobs control facility to do two things:
17         - determine when the controlling tty has gone away
18         - kill a child that's run away (the child sets up a process group)
19  */
20
21 #include "../h/mh.h"
22 #include "../zotnet/mf.h"
23 #include <stdio.h>
24 #include "../zotnet/mts.h"
25 #include <pwd.h>
26 #include <signal.h>
27 #include <sys/ioctl.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #ifdef LOCALE
31 #include        <locale.h>
32 #endif
33
34 /* \f */
35
36 static struct swit switches[] = {
37 #define SLEEPSW 0
38     "sleep seconds", 0,
39
40 #define HELPSW  1
41     "help", 4,
42
43     NULL, NULL
44 };
45
46 /* \f */
47
48 static int  snooze = 60;
49
50 static int  uucp = NOTOK;
51
52 extern char *environ;
53
54 static char myhome[BUFSIZ] = "";
55 static char mymail[BUFSIZ] = "";
56 static char myaddr[BUFSIZ] = "";
57 static char mystat[BUFSIZ] = "";
58 static char myuser[BUFSIZ] = "";
59
60 int     sigser ();
61
62 off_t    lseek ();
63 #ifdef  SYS5
64 #ifndef __STDC__
65 struct passwd  *getpwuid ();
66 #endif /* !__STDC__ */
67 #endif  SYS5
68
69 /* \f */
70
71 /* ARGSUSED */
72
73 main (argc, argv)
74 int     argc;
75 char  **argv;
76 {
77     char   *cp,
78           **ap,
79           **argp,
80             buf[100],
81            *arguments[MAXARGS];
82     struct passwd  *pw;
83
84 #ifdef LOCALE
85         setlocale(LC_ALL, "");
86 #endif
87     invo_name = r1bindex (argv[0], '/');
88     mts_init (invo_name);
89     if ((cp = m_find (invo_name)) != NULL) {
90         ap = brkstring (cp = getcpy (cp), " ", "\n");
91         ap = copyip (ap, arguments);
92     }
93     else
94         ap = arguments;
95     (void) copyip (argv + 1, ap);
96     argp = arguments;
97
98 /* \f */
99
100     while (cp = *argp++) {
101         if (*cp == '-')
102             switch (smatch (++cp, switches)) {
103                 case AMBIGSW: 
104                     ambigsw (cp, switches);
105                     done (1);
106                 case UNKWNSW: 
107                     adios (NULLCP, "-%s unknown", cp);
108                 case HELPSW: 
109                     (void) sprintf (buf, "%s [switches]", invo_name);
110                     help (buf, switches);
111                     done (1);
112
113                 case SLEEPSW: 
114                     if (!(cp = *argp++) || *cp == '-')
115                         adios (NULLCP, "missing argument to %s", argp[-2]);
116                     if ((snooze = atoi (cp)) < 0)
117                         adios (NULLCP, "bad argument %s %s", argp[-2], cp);
118                     continue;
119             }
120         adios (NULLCP, "usage: %s [switches]", invo_name);
121     }
122
123 /* \f */
124
125     if ((pw = getpwuid (getuid ())) == NULL)
126         adios (NULLCP, "you lose big");
127
128     *environ = NULL;
129     (void) m_putenv ("USER", pw -> pw_name);
130     (void) m_putenv ("HOME", pw -> pw_dir);
131     (void) m_putenv ("SHELL", pw -> pw_shell);
132     if (chdir (pw -> pw_dir) == NOTOK)
133         (void) chdir ("/");
134     (void) umask (0077);
135
136     if (geteuid () == 0) {
137 #ifdef  BSD41A
138         (void) inigrp (pw -> pw_name, pw -> pw_gid);
139 #endif  BSD41A
140         (void) setgid (pw -> pw_gid);
141 #ifdef  BSD42
142         (void) initgroups (pw -> pw_name, pw -> pw_gid);
143 #endif  BSD42
144         (void) setuid (pw -> pw_uid);
145     }
146
147     (void) sprintf (mymail, "%s/%s",
148             uucpldir[0] ? uucpldir : pw -> pw_dir,
149             uucplfil[0] ? uucplfil : pw -> pw_name);
150     (void) strcpy (myuser, pw -> pw_name);
151     (void) sprintf (myaddr, "%s@%s", pw -> pw_name, LocalName ());
152     (void) strcpy (myhome, pw -> pw_dir);
153     (void) sprintf (mystat, ".%s_%d", invo_name, pw -> pw_uid);
154
155     if (access (slocalproc, 1) == NOTOK)
156         adios (slocalproc, "unable to execute");
157
158     closefds (fileno (stderr) + 1);
159
160     (void) signal (SIGINT, SIG_IGN);
161     (void) signal (SIGHUP, sigser);
162     (void) signal (SIGQUIT, SIG_IGN);
163     (void) signal (SIGTERM, sigser);
164
165     switch (fork ()) {
166         case NOTOK: 
167         case OK: 
168             umhook ();
169             break;
170
171         default: 
172             break;
173     }
174
175     exit (0);
176 }
177
178 /* \f */
179
180 #ifndef TIOCGPGRP
181 #define pgrp_ok(pg)     1
182 #else   TIOCGPGRP
183 #define pgrp_ok(pg)     (ioctl (2, TIOCGPGRP, (char *) &pg) != NOTOK)
184 #endif  TIOCGPGRP
185
186 static  umhook () {
187     int     pg;
188     struct stat st1,
189                 st2;
190
191     st_init (&st1);
192
193     for (; pgrp_ok (pg);) {
194         if (stat (mymail, &st2) == NOTOK) {
195             st2.st_ino = (ino_t) 0;
196             st2.st_size = (off_t) 0;
197             st2.st_mtime = (time_t) 0;
198         }
199         else
200             if (st1.st_mtime != st2.st_mtime)
201                 if (st1.st_ino != st2.st_ino)
202                     process ((off_t) 0, &st2);
203                 else
204                     if (st1.st_size < st2.st_size)
205                         process (st1.st_size, &st2);
206
207         st1.st_ino = st2.st_ino;
208         st1.st_size = st2.st_size;
209         st1.st_mtime = st2.st_mtime;
210
211         sleep ((unsigned) snooze);
212     }
213 }
214
215 /* \f */
216
217 static  process (offset, st)
218 off_t offset;
219 struct stat *st;
220 {
221     int     td1,
222             td2;
223     time_t timep[2];
224     char    tmpfil[BUFSIZ];
225     register FILE *fp;
226
227     if ((uucp = lkopen (mymail, 0)) == NOTOK)
228         adios (NULLCP, "unable to lock and open %s", mymail);
229     if (lseek (uucp, (off_t) offset, 0) == (off_t) NOTOK)
230         adios (mymail, "unable to position to %ld offset on", (long) offset);
231
232     (void) strcpy (tmpfil, m_tmpfil (invo_name));
233     if ((td1 = creat (tmpfil, TMPMODE)) == NOTOK)
234         adios (tmpfil, "unable to create");
235     (void) close (td1);
236
237     if ((td1 = open (tmpfil, 2)) == NOTOK)
238         adios (tmpfil, "unable to open");
239     (void) unlink (tmpfil);
240     if ((td2 = dup (td1)) == NOTOK)
241         adios ("file descriptor", "unable to dup");
242
243     switch (uucp2mmdf (uucp, td1, FALSE)) {
244         case MFPRM: 
245             adios (NULLCP, "internal error while filtering UUCP mail");
246
247         case MFSIO: 
248             adios (NULLCP, "no free file pointers");
249
250         case MFERR: 
251             adios ("UUCP mail", "i/o error while filtering");
252
253         case MFOK: 
254         case MFROM: 
255         case MFHDR: 
256         case MFTXT: 
257             timep[0] = st -> st_atime;
258             timep[1] = st -> st_mtime;
259             utime (mymail, timep);
260             st_update (st);
261             break;
262     }
263     (void) lkclose (uucp, mymail), uucp = NOTOK;
264
265 /* \f */
266
267     (void) close (td1);
268
269     (void) lseek (td2, (off_t)0, 0);
270     if ((fp = fdopen (td2, "r")) == NULL)
271         adios (NULLCP, "no free file pointers");
272
273     while (hook (fp))
274         continue;
275     (void) fclose (fp);
276 }
277
278 /* \f */
279
280 static int  hook (in)
281 register FILE *in;
282 {
283     int     child_id,
284             done,
285             fd1,
286             fd2,
287             i;
288     char    buffer[BUFSIZ],
289             mysndr[BUFSIZ],
290             myfile[BUFSIZ];
291     register FILE *out;
292
293     if (fgets (buffer, sizeof buffer, in) == NULL)
294         return FALSE;
295
296 /* should insist on isdlm1 (buffer) here... */
297
298     (void) strcpy (myfile, m_tmpfil (invo_name));
299     if ((fd1 = creat (myfile, TMPMODE)) == NOTOK)
300         adios (myfile, "unable to create");
301     (void) close (fd1);
302
303     if ((fd1 = open (myfile, 2)) == NOTOK)
304         adios (myfile, "unable to open");
305     (void) unlink (myfile);
306     if ((fd2 = dup (fd1)) == NOTOK)
307         adios ("file descriptor", "unable to dup");
308
309     if ((out = fdopen (fd1, "w")) == NULL)
310         adios (NULLCP, "no free file pointers");
311
312     for (done = TRUE;;) {
313         if (fgets (buffer, sizeof buffer, in) == NULL)
314             break;              /* should be error */
315         if (done && isdlm2 (buffer))
316             break;
317         done = buffer[strlen (buffer) - 1] == '\n';
318         fputs (buffer, out);
319     }
320     (void) fclose (out);
321
322     (void) lseek (fd2, (off_t)0, 0);
323     seeksndr (fd2, mysndr);
324
325 /* \f */
326
327     switch (child_id = fork ()) {
328         case NOTOK: 
329             adios ("fork", "unable to");/* NOTREACHED */
330
331         case OK: 
332             (void) lseek (fd2, (off_t)0, 0);
333             if (fd2 != 0)
334                 (void) dup2 (fd2, 0);
335             (void) freopen ("/dev/null", "w", stdout);
336             (void) freopen ("/dev/null", "w", stderr);
337             if (fd2 != 3)
338                 (void) dup2 (fd2, 3);
339             closefds (4);
340 #ifdef  TIOCNOTTY
341             if ((i = open ("/dev/tty", 2)) != NOTOK) {
342                 (void) ioctl (i, TIOCNOTTY, NULLCP);
343                 (void) close (i);
344             }
345 #endif  TIOCNOTTY
346 #ifdef  BSD42
347             (void) setpgrp (0, getpid ());
348 #endif  BSD42
349
350             execlp (slocalproc, r1bindex (slocalproc, '/'),
351                     "-file", myfile, "-mailbox", mymail,
352                     "-home", myhome, "-addr", myaddr,
353                     "-user", myuser, "-sender", mysndr, NULLCP);
354             adios (slocalproc, "unable to exec");/* NOTREACHED */
355
356         default: 
357             (void) close (fd2);
358             (void) pidwait (child_id, OK);
359             return TRUE;
360     }
361 }
362
363 /* \f */
364
365 static  seeksndr (fd1, mysndr)
366 int     fd1;
367 char   *mysndr;
368 {
369     int     fd2;
370     char   *bp,
371            *hp,
372             from[BUFSIZ],
373             sender[BUFSIZ];
374     register FILE *in;
375
376     if ((fd2 = dup (fd1)) == NOTOK)
377         adios ("file descriptor", "unable to dup");
378     if ((in = fdopen (fd2, "r")) == NULL)
379         adios (NULLCP, "no free file pointers");
380
381     for (from[0] = sender[0] = NULL; mfgets (in, &hp) != DONE;)
382         if ((bp = index (hp, ':')) != NULL) {
383             *bp++ = NULL;
384             if (lequal (hp, "From"))
385                 seekaddr (from, bp);
386             else
387                 if (lequal (hp, "Sender"))
388                     seekaddr (sender, bp);
389         }
390     (void) fclose (in);
391
392     (void) strcpy (mysndr, sender[0] ? sender : from[0] ? from : myaddr);
393 }
394
395 /* \f */
396
397 static  seekaddr (addr, bp)
398 char   *addr,
399        *bp;
400 {
401     struct adrx *adrxp;
402
403     if ((adrxp = seekadrx (bp)) == NULL)
404         return;
405     if (adrxp -> err || !adrxp -> mbox)
406         return;
407
408     if (adrxp -> host)
409         (void) sprintf (addr, "%s@%s", adrxp -> mbox, adrxp -> host);
410     else
411         (void) strcpy (addr, adrxp -> mbox);
412
413     while (seekadrx (NULLCP))
414         continue;
415 }
416
417 /* \f */
418
419 static st_init(st)
420 struct stat *st;
421 {
422     int     fd;
423
424     if ((fd = open (mystat, 0)) == NOTOK
425             || read (fd, (char *) st, sizeof *st) != (sizeof *st)) {
426         st -> st_ino = (ino_t) 0;
427         st -> st_size = (off_t) 0;
428         st -> st_mtime = (time_t) 0;
429     }
430     if (fd != NOTOK)
431         (void) close (fd);
432 }
433
434
435 static st_update(st)
436 struct stat *st;
437 {
438     static int  fd = NOTOK;
439
440     if (fd == NOTOK
441             && (fd = creat (mystat, TMPMODE)) == NOTOK)
442         adios (mystat, "unable to write");
443
444     (void) lseek (fd, (off_t)0, 0);
445     if (write (fd, (char *) st, sizeof *st) != (sizeof *st))
446         adios (mystat, "error writing");
447 }
448
449 /* \f */
450
451 #ifdef  BSD42
452 /* ARGSUSED */
453 #endif  BSD42
454
455 static int  sigser (sig)
456 int     sig;
457 {
458 #ifndef BSD42
459     (void) signal (sig, SIG_IGN);
460 #endif  BSD42
461
462     done (1);
463 }
464
465 /* \f */
466
467 void    done (status)
468 int     status;
469 {
470     (void) lkclose (uucp, mymail), uucp = NOTOK;
471     exit (status);
472 }