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