Finally, some support for mh-format when using forw(1). Still needs some
[mmh] / uip / rcvtty.c
1
2 /*
3  * rcvtty.c -- a rcvmail program (a lot like rcvalert) handling IPC ttys
4  *
5  * This code is Copyright (c) 2002, by the authors of nmh.  See the
6  * COPYRIGHT file in the root directory of the nmh distribution for
7  * complete copyright information.
8  */
9
10 /* Changed to use getutent() and friends.  Assumes that when getutent() exists,
11  * a number of other things also exist.  Please check.
12  * Ruud de Rooij <ruud@ruud.org>  Sun, 28 May 2000 17:28:55 +0200
13  */
14
15 #include <h/mh.h>
16 #include <h/signals.h>
17 #include <h/m_setjmp.h>
18 #include <h/rcvmail.h>
19 #include <h/scansbr.h>
20 #include <h/tws.h>
21 #include <h/mts.h>
22 #include <signal.h>
23 #include <fcntl.h>
24
25 #include <utmpx.h>
26
27 #define SCANFMT \
28 "%2(hour{dtimenow}):%02(min{dtimenow}): %<(size)%5(size) %>%<{encrypted}E%>\
29 %<(mymbox{from})%<{to}To:%14(friendly{to})%>%>%<(zero)%17(friendly{from})%>  \
30 %{subject}%<{body}<<%{body}>>%>"
31
32 static struct swit switches[] = {
33 #define BIFFSW  0
34     { "biff", 0 },
35 #define FORMSW  1
36     { "form formatfile", 0 },
37 #define FMTSW   2
38     { "format string", 5 },
39 #define WIDTHSW 3
40     { "width columns", 0 },
41 #define NLSW    4
42     { "newline", 0 },
43 #define NNLSW   5
44     { "nonewline", 0 },
45 #define BELSW   6
46     { "bell", 0 },
47 #define NBELSW  7
48     { "nobell", 0 },
49 #define VERSIONSW 8
50     { "version", 0 },
51 #define HELPSW  9
52     { "help", 0 },
53     { NULL, 0 }
54 };
55
56 static jmp_buf myctx;
57 static int bell = 1;
58 static int newline = 1;
59 static int biff = 0;
60 static int width = 0;
61 static char *form = NULL;
62 static char *format = NULL;
63
64 /*
65  * external prototypes
66  */
67 char *getusername(void);
68
69 /*
70  * static prototypes
71  */
72 static void alrmser (int);
73 static int message_fd (char **);
74 static int header_fd (void);
75 static void alert (char *, int);
76
77
78 int
79 main (int argc, char **argv)
80 {
81     int md, vecp = 0;
82     char *cp, *user, buf[BUFSIZ], tty[BUFSIZ];
83     char **argp, **arguments, *vec[MAXARGS];
84     struct utmpx *utp;
85 #ifdef LOCALE
86     setlocale(LC_ALL, "");
87 #endif
88     invo_name = r1bindex (argv[0], '/');
89
90     /* read user profile/context */
91     context_read();
92
93     mts_init (invo_name);
94     arguments = getarguments (invo_name, argc, argv, 1);
95     argp = arguments;
96
97     while ((cp = *argp++)) {
98         if (*cp == '-') {
99             switch (smatch (++cp, switches)) {
100                 case AMBIGSW: 
101                     ambigsw (cp, switches);
102                     done (1);
103                 case UNKWNSW: 
104                     vec[vecp++] = --cp;
105                     continue;
106
107                 case HELPSW: 
108                     snprintf (buf, sizeof(buf), "%s [command ...]", invo_name);
109                     print_help (buf, switches, 1);
110                     done (1);
111                 case VERSIONSW:
112                     print_version(invo_name);
113                     done (1);
114
115                 case BIFFSW:
116                     biff = 1;
117                     continue;
118
119                 case FORMSW: 
120                     if (!(form = *argp++) || *form == '-')
121                         adios (NULL, "missing argument to %s", argp[-2]);
122                     format = NULL;
123                     continue;
124                 case FMTSW: 
125                     if (!(format = *argp++) || *format == '-')
126                         adios (NULL, "missing argument to %s", argp[-2]);
127                     form = NULL;
128                     continue;
129
130                 case WIDTHSW:
131                     if (!(cp = *argp++) || *cp == '-')
132                         adios(NULL, "missing argument to %s", argp[-2]);
133                     width = atoi(cp);
134                     continue;
135                 case NLSW:
136                     newline = 1;
137                     continue;
138                 case NNLSW:
139                     newline = 0;
140                     continue;
141                 case BELSW:
142                     bell = 1;
143                     continue;
144                 case NBELSW:
145                     bell = 0;
146                     continue;
147
148             }
149         }
150         vec[vecp++] = cp;
151     }
152     vec[vecp] = 0;
153
154     if ((md = vecp ? message_fd (vec) : header_fd ()) == NOTOK)
155         exit (RCV_MBX);
156
157     user = getusername();
158
159     setutxent();
160     while ((utp = getutxent()) != NULL) {
161         if (utp->ut_type == USER_PROCESS && utp->ut_user[0] != 0
162                && utp->ut_line[0] != 0
163                && strncmp (user, utp->ut_user, sizeof(utp->ut_user)) == 0) {
164             strncpy (tty, utp->ut_line, sizeof(utp->ut_line));
165             alert (tty, md);
166         }
167     }
168     endutxent();
169
170     exit (RCV_MOK);
171     return 0;  /* dead code to satisfy the compiler */
172 }
173
174
175 static void
176 alrmser (int i)
177 {
178     NMH_UNUSED (i);
179
180     longjmp (myctx, 1);
181 }
182
183
184 static int
185 message_fd (char **vec)
186 {
187     pid_t child_id;
188     int bytes, fd, seconds;
189     char tmpfil[BUFSIZ];
190     struct stat st;
191
192     fd = mkstemp (strncpy (tmpfil, "/tmp/rcvttyXXXXX", sizeof(tmpfil)));
193     unlink (tmpfil);
194
195     if ((child_id = vfork()) == NOTOK) {
196         /* fork error */
197         close (fd);
198         return header_fd ();
199     } else if (child_id) {
200         /* parent process */
201         if (!m_setjmp (myctx)) {
202             SIGNAL (SIGALRM, alrmser);
203             bytes = fstat(fileno (stdin), &st) != NOTOK ? (int) st.st_size : 100;
204
205             /* amount of time to wait depends on message size */
206             if (bytes <= 100) {
207                 /* give at least 5 minutes */
208                 seconds = 300;
209             } else if (bytes >= 90000) {
210                 /* but 30 minutes should be long enough */
211                 seconds = 1800;
212             } else {
213                 seconds = (bytes / 60) + 300;
214             }
215             alarm ((unsigned int) seconds);
216             pidwait(child_id, OK);
217             alarm (0);
218
219             if (fstat (fd, &st) != NOTOK && st.st_size > (off_t) 0)
220                 return fd;
221         } else {
222             /*
223              * Ruthlessly kill the child and anything
224              * else in its process group.
225              */
226             killpg(child_id, SIGKILL);
227         }
228         close (fd);
229         return header_fd ();
230     }
231
232     /* child process */
233     rewind (stdin);
234     if (dup2 (fd, 1) == NOTOK || dup2 (fd, 2) == NOTOK)
235         _exit (-1);
236     closefds (3);
237     setpgid ((pid_t) 0, getpid ());     /* put in own process group */
238     execvp (vec[0], vec);
239     _exit (-1);
240     return 1;  /* dead code to satisfy compiler */
241 }
242
243
244 static int
245 header_fd (void)
246 {
247     int fd;
248     char *nfs;
249     char *tfile = NULL;
250
251     tfile = m_mktemp2(NULL, invo_name, &fd, NULL);
252     if (tfile == NULL) return NOTOK;
253     unlink (tfile);
254
255     rewind (stdin);
256
257     /* get new format string */
258     nfs = new_fs (form, format, SCANFMT);
259     scan (stdin, 0, 0, nfs, width, 0, 0, NULL, 0L, 0);
260     if (newline)
261         write (fd, "\n\r", 2);
262     write (fd, scanl, strlen (scanl));
263     if (bell)
264         write (fd, "\007", 1);
265
266     return fd;
267 }
268
269
270 static void
271 alert (char *tty, int md)
272 {
273     int i, td, mask;
274     char buffer[BUFSIZ], ttyspec[BUFSIZ];
275     struct stat st;
276
277     snprintf (ttyspec, sizeof(ttyspec), "/dev/%s", tty);
278
279     /*
280      * The mask depends on whether we are checking for
281      * write permission based on `biff' or `mesg'.
282      */
283     mask = biff ? S_IEXEC : (S_IWRITE >> 3);
284     if (stat (ttyspec, &st) == NOTOK || (st.st_mode & mask) == 0)
285         return;
286
287     if (!m_setjmp (myctx)) {
288         SIGNAL (SIGALRM, alrmser);
289         alarm (2);
290         td = open (ttyspec, O_WRONLY);
291         alarm (0);
292         if (td == NOTOK)
293             return;
294     } else {
295         alarm (0);
296         return;
297     }
298
299     lseek (md, (off_t) 0, SEEK_SET);
300
301     while ((i = read (md, buffer, sizeof(buffer))) > 0)
302         if (write (td, buffer, i) != i)
303             break;
304
305     close (td);
306 }
307