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