9489ef0d716ccbc86dba511dcb6c8451f0d3bd05
[mmh] / uip / msgchk.c
1
2 /*
3  * msgchk.c -- check for mail
4  *
5  * $Id$
6  */
7
8 #include <h/mh.h>
9 #include <mts/generic/mts.h>
10 #include <h/tws.h>
11 #include <pwd.h>
12
13 #ifdef POP
14 # include <h/popsbr.h>
15 #endif
16
17 #ifdef HESIOD
18 # include <hesiod.h>
19 #endif
20
21 #ifndef POP
22 # define POPminc(a) (a)
23 #else
24 # define POPminc(a)  0
25 #endif
26
27 #ifndef RPOP
28 # define RPOPminc(a) (a)
29 #else
30 # define RPOPminc(a)  0
31 #endif
32
33 #ifndef APOP
34 # define APOPminc(a) (a)
35 #else
36 # define APOPminc(a)  0
37 #endif
38
39 #ifndef KPOP
40 # define KPOPminc(a) (a)
41 #else
42 # define KPOPminc(a)  0
43 #endif
44
45 #ifndef CYRUS_SASL
46 # define SASLminc(a) (a)
47 #else
48 # define SASLminc(a)  0
49 #endif
50
51 static struct swit switches[] = {
52 #define DATESW                   0
53     { "date", 0 },
54 #define NDATESW                  1
55     { "nodate", 0 },
56 #define NOTESW                   2
57     { "notify type", 0 },
58 #define NNOTESW                  3
59     { "nonotify type", 0 },
60 #define HOSTSW                   4
61     { "host hostname", POPminc (-4) },
62 #define USERSW                   5
63     { "user username", POPminc (-4) },
64 #define APOPSW                   6
65     { "apop", APOPminc (-4) },
66 #define NAPOPSW                  7
67     { "noapop", APOPminc (-6) },
68 #define RPOPSW                   8
69     { "rpop", RPOPminc (-4) },
70 #define NRPOPSW                  9
71     { "norpop", RPOPminc (-6) },
72 #define VERSIONSW               10
73     { "version", 0 },
74 #define HELPSW                  11
75     { "help", 0 },
76 #define SNOOPSW                 12
77     { "snoop", -5 },
78 #define KPOPSW                  13
79     { "kpop", KPOPminc (-4) },
80 #define SASLSW                  14
81     { "sasl", SASLminc(-4) },
82 #define SASLMECHSW              15
83     { "saslmech", SASLminc(-5) },
84     { NULL, 0 }
85 };
86
87 /*
88  * Maximum numbers of users we can check (plus
89  * one for the NULL vector at the end).
90  */
91 #define MAXVEC  51
92
93 #define NT_NONE 0x0
94 #define NT_MAIL 0x1
95 #define NT_NMAI 0x2
96 #define NT_ALL  (NT_MAIL | NT_NMAI)
97
98 #define NONEOK  0x0
99 #define UUCPOLD 0x1
100 #define UUCPNEW 0x2
101 #define UUCPOK  (UUCPOLD | UUCPNEW)
102 #define MMDFOLD 0x4
103 #define MMDFNEW 0x8
104 #define MMDFOK  (MMDFOLD | MMDFNEW)
105
106
107 /*
108  * static prototypes
109  */
110 static int donote (char *, int);
111 static int checkmail (char *, char *, int, int, int);
112
113 #ifdef POP
114 static int remotemail (char *, char *, int, int, int, int, int, int, char *);
115 #endif
116
117
118 int
119 main (int argc, char **argv)
120 {
121     int datesw = 1, notifysw = NT_ALL;
122     int rpop, status = 0;
123     int kpop = 0, sasl = 0;
124     int snoop = 0, vecp = 0;
125     uid_t uid;
126     char *cp, *host = NULL, *user, buf[BUFSIZ], *saslmech = NULL; 
127     char **argp, **arguments, *vec[MAXVEC];
128     struct passwd *pw;
129
130 #ifdef HESIOD
131     struct hes_postoffice *po;
132     char *tmphost;
133 #endif
134
135 #ifdef LOCALE
136     setlocale(LC_ALL, "");
137 #endif
138     invo_name = r1bindex (argv[0], '/');
139
140     /* read user profile/context */
141     context_read();
142
143     mts_init (invo_name);
144     uid = getuid ();
145     user = getusername();
146
147     arguments = getarguments (invo_name, argc, argv, 1);
148     argp = arguments;
149
150 #ifdef POP
151     if ((cp = getenv ("MHPOPDEBUG")) && *cp)
152         snoop++;
153 #endif
154
155     rpop = 0;
156
157     while ((cp = *argp++)) {
158         if (*cp == '-') {
159             switch (smatch (++cp, switches)) {
160                 case AMBIGSW: 
161                     ambigsw (cp, switches);
162                     done (1);
163                 case UNKWNSW: 
164                     adios (NULL, "-%s unknown", cp);
165
166                 case HELPSW: 
167                     snprintf (buf, sizeof(buf), "%s [switches] [users ...]",
168                         invo_name);
169                     print_help (buf, switches, 1);
170                     done (1);
171                 case VERSIONSW:
172                     print_version(invo_name);
173                     done (1);
174
175                 case DATESW:
176                     datesw++;
177                     continue;
178                 case NDATESW:
179                     datesw = 0;
180                     continue;
181
182                 case NOTESW:
183                     if (!(cp = *argp++) || *cp == '-')
184                         adios (NULL, "missing argument to %s", argp[-2]);
185                     notifysw |= donote (cp, 1);
186                     continue;
187                 case NNOTESW:
188                     if (!(cp = *argp++) || *cp == '-')
189                         adios (NULL, "missing argument to %s", argp[-2]);
190                     notifysw &= ~donote (cp, 0);
191                     continue;
192
193                 case HOSTSW: 
194                     if (!(host = *argp++) || *host == '-')
195                         adios (NULL, "missing argument to %s", argp[-2]);
196                     continue;
197                 case USERSW: 
198                     if (!(cp = *argp++) || *cp == '-')
199                         adios (NULL, "missing argument to %s", argp[-2]);
200                     if (vecp >= MAXVEC-1)
201                         adios (NULL, "you can only check %d users at a time", MAXVEC-1);
202                     else
203                         vec[vecp++] = cp;
204                     continue;
205
206                 case APOPSW: 
207                     rpop = -1;
208                     continue;
209                 case NAPOPSW:
210                     rpop = 0;
211                     continue;
212
213                 case RPOPSW: 
214                     rpop = 1;
215                     continue;
216                 case NRPOPSW: 
217                     rpop = 0;
218                     continue;
219
220                 case KPOPSW:
221                     kpop = 1;
222                     continue;
223
224                 case SNOOPSW:
225                     snoop++;
226                     continue;
227
228                 case SASLSW:
229                     sasl++;
230                     continue;
231                 
232                 case SASLMECHSW:
233                     if (!(saslmech = *argp++) || *saslmech == '-')
234                         adios (NULL, "missing argument to %s", argp[-2]);
235                     continue;
236             }
237         }
238         if (vecp >= MAXVEC-1)
239             adios (NULL, "you can only check %d users at a time", MAXVEC-1);
240         else
241             vec[vecp++] = cp;
242     }
243
244 #ifdef POP
245     /*
246      * If -host is not specified by user
247      */
248     if (!host || !*host) {
249 # ifdef HESIOD
250         /*
251          * Scheme is:
252          *        use MAILHOST environment variable if present,
253          *  else try Hesiod.
254          *  If that fails, use the default (if any)
255          *  provided by mts.conf in mts_init()
256          */
257         if ((tmphost = getenv("MAILHOST")) != NULL)
258             pophost = tmphost;
259         else if ((po = hes_getmailhost(vecp ? vec[0] : user)) != NULL &&
260                 strcmp(po->po_type, "POP") == 0)
261             pophost = po->po_host;
262 # endif /* HESIOD */
263         /*
264          * If "pophost" is specified in mts.conf,
265          * use it as default value.
266          */
267         if (pophost && *pophost)
268             host = pophost;
269     }
270     if (!host || !*host)
271         host = NULL;
272     if (!host || rpop <= 0)
273         setuid (uid);
274 #endif /* POP */
275
276     if (vecp != 0)
277         vec[vecp] = NULL;
278
279 #ifdef POP
280     if (host) {
281         if ( strcmp( POPSERVICE, "kpop" ) == 0 ) {
282             kpop = 1;
283         }
284         if (vecp == 0) {
285             status = remotemail (host, user, rpop, kpop, notifysw, 1, snoop,
286                                  sasl, saslmech);
287         } else {
288             for (vecp = 0; vec[vecp]; vecp++)
289                 status += remotemail (host, vec[vecp], rpop, kpop, notifysw, 0,
290                                       snoop, sasl, saslmech);
291         }
292     } else {
293 #endif /* POP */
294
295     if (vecp == 0) {
296         char *home;
297
298         home = (uid = geteuid()) ? home = getenv ("HOME") : NULL;
299         if (home == NULL) {
300             pw = getpwnam (user);
301             if (pw == NULL)
302                 adios (NULL, "unable to get information about user");
303             if (home == NULL)
304                 home = pw->pw_dir;
305         }
306         status = checkmail (user, home, datesw, notifysw, 1);
307     } else {
308         for (vecp = 0; vec[vecp]; vecp++) {
309             if ((pw = getpwnam (vec[vecp])))
310                 status += checkmail (pw->pw_name, pw->pw_dir, datesw, notifysw, 0);
311             else
312                 advise (NULL, "no such user as %s", vec[vecp]);
313         }
314     }
315 #ifdef POP
316     }           /* host == NULL */
317 #endif
318
319     return done (status);
320 }
321
322
323 static struct swit ntswitches[] = {
324 #define NALLSW     0
325     { "all", 0 },
326 #define NMAISW     1
327     { "mail", 0 },
328 #define NNMAISW    2
329     { "nomail", 0 },
330     { NULL, 0 }
331 };
332
333
334 static int
335 donote (char *cp, int ntflag)
336 {
337     switch (smatch (cp, ntswitches)) {
338         case AMBIGSW: 
339             ambigsw (cp, ntswitches);
340             done (1);
341         case UNKWNSW: 
342             adios (NULL, "-%snotify %s unknown", ntflag ? "" : "no", cp);
343
344         case NALLSW: 
345             return NT_ALL;
346         case NMAISW: 
347             return NT_MAIL;
348         case NNMAISW: 
349             return NT_NMAI;
350     }
351
352     return 0; /* Before 1999-07-15, garbage was returned if control got here. */
353 }
354
355
356 static int
357 checkmail (char *user, char *home, int datesw, int notifysw, int personal)
358 {
359     int mf, status;
360     char buffer[BUFSIZ];
361     struct stat st;
362
363     snprintf (buffer, sizeof(buffer), "%s/%s", mmdfldir[0] ? mmdfldir : home, mmdflfil[0] ? mmdflfil : user);
364     if (datesw) {
365         st.st_size = 0;
366         st.st_atime = st.st_mtime = 0;
367     }
368     mf = (stat (buffer, &st) == NOTOK || st.st_size == 0) ? NONEOK
369         : st.st_atime <= st.st_mtime ? MMDFNEW : MMDFOLD;
370
371     if ((mf & UUCPOK) || (mf & MMDFOK)) {
372         if (notifysw & NT_MAIL) {
373             printf (personal ? "You have " : "%s has ", user);
374             if (mf & UUCPOK)
375                 printf ("%s old-style bell", mf & UUCPOLD ? "old" : "new");
376             if ((mf & UUCPOK) && (mf & MMDFOK))
377                 printf (" and ");
378             if (mf & MMDFOK)
379                 printf ("%s%s", mf & MMDFOLD ? "old" : "new",
380                         mf & UUCPOK ? " Internet" : "");
381             printf (" mail waiting");
382         } else {
383             notifysw = 0;
384         }
385         status = 0;
386     }
387     else {
388         if (notifysw & NT_NMAI)
389             printf (personal ? "You don't %s%s" : "%s doesn't %s",
390                     personal ? "" : user, "have any mail waiting");
391         else
392             notifysw = 0;
393
394         status = 1;
395     }
396
397     if (notifysw)
398         if (datesw && st.st_atime)
399             printf ("; last read on %s", dtime (&st.st_atime, 1));
400     if (notifysw)
401         printf ("\n");
402
403     return status;
404 }
405
406
407 #ifdef POP
408 extern char response[];
409
410 static int
411 remotemail (char *host, char *user, int rpop, int kpop, int notifysw, int personal, int snoop, int sasl, char *saslmech)
412 {
413     int nmsgs, nbytes, status;
414     char *pass = NULL;
415
416     if (user == NULL)
417         user = getusername ();
418     if (kpop || sasl || (rpop > 0))
419         pass = getusername ();
420     else
421         ruserpass (host, &user, &pass);
422
423     /* open the POP connection */
424     if (pop_init (host, user, pass, snoop, kpop ? 1 : rpop, kpop,
425                   sasl, saslmech) == NOTOK
426             || pop_stat (&nmsgs, &nbytes) == NOTOK      /* check for messages  */
427             || pop_quit () == NOTOK) {                  /* quit POP connection */
428         advise (NULL, "%s", response);
429         return 1;
430     }
431
432     if (nmsgs) {
433         if (notifysw & NT_MAIL) {
434             printf (personal ? "You have " : "%s has ", user);
435             printf ("%d message%s (%d bytes)",
436                     nmsgs, nmsgs != 1 ? "s" : "", nbytes);
437         }
438         else
439             notifysw = 0;
440
441         status = 0;
442     } else {
443         if (notifysw & NT_NMAI)
444             printf (personal ? "You don't %s%s" : "%s doesn't %s",
445                     personal ? "" : user, "have any mail waiting");
446         else
447             notifysw = 0;
448         status = 1;
449     }
450     if (notifysw)
451         printf (" on %s\n", host);
452
453     return status;
454 }
455 #endif /* POP */