]> diplodocus.org Git - nmh/blob - uip/msgchk.c
82541ec5beeaf0203a8d19096d7002ec02e309ad
[nmh] / uip / msgchk.c
1 /* msgchk.c -- check for mail
2 *
3 * This code is Copyright (c) 2002, by the authors of nmh. See the
4 * COPYRIGHT file in the root directory of the nmh distribution for
5 * complete copyright information.
6 */
7
8 #include "h/mh.h"
9 #include "sbr/dtime.h"
10 #include "sbr/getarguments.h"
11 #include "sbr/smatch.h"
12 #include "sbr/ambigsw.h"
13 #include "sbr/print_version.h"
14 #include "sbr/print_help.h"
15 #include "sbr/error.h"
16 #include "h/mts.h"
17 #include "h/tws.h"
18 #include "h/done.h"
19 #include "h/utils.h"
20 #include <pwd.h>
21
22 #include "popsbr.h"
23
24 #ifndef CYRUS_SASL
25 # define SASLminc(a) (a)
26 #else
27 # define SASLminc(a) 0
28 #endif
29
30 #ifndef TLS_SUPPORT
31 # define TLSminc(a) (a)
32 #else
33 # define TLSminc(a) 0
34 #endif
35
36 #define MSGCHK_SWITCHES \
37 X("date", 0, DATESW) \
38 X("nodate", 0, NDATESW) \
39 X("notify type", 0, NOTESW) \
40 X("nonotify type", 0, NNOTESW) \
41 X("host hostname", 0, HOSTSW) \
42 X("user username", 0, USERSW) \
43 X("port name/number", 0, PORTSW) \
44 X("version", 0, VERSIONSW) \
45 X("help", 0, HELPSW) \
46 X("snoop", 0, SNOOPSW) \
47 X("sasl", SASLminc(4), SASLSW) \
48 X("nosasl", SASLminc(6), NOSASLSW) \
49 X("saslmech", SASLminc(5), SASLMECHSW) \
50 X("authservice", SASLminc(0), AUTHSERVICESW) \
51 X("tls", TLSminc(-3), TLSSW) \
52 X("initialtls", TLSminc(-10), INITTLSSW) \
53 X("notls", TLSminc(-5), NOTLSSW) \
54 X("certverify", TLSminc(-10), CERTVERSW) \
55 X("nocertverify", TLSminc(-12), NOCERTVERSW) \
56 X("proxy command", 0, PROXYSW) \
57
58 #define X(sw, minchars, id) id,
59 DEFINE_SWITCH_ENUM(MSGCHK);
60 #undef X
61
62 #define X(sw, minchars, id) { sw, minchars, id },
63 DEFINE_SWITCH_ARRAY(MSGCHK, switches);
64 #undef X
65
66 /*
67 * Maximum numbers of users we can check (plus
68 * one for the NULL vector at the end).
69 */
70 #define MAXVEC 51
71
72 #define NT_MAIL 0x1
73 #define NT_NMAI 0x2
74 #define NT_ALL (NT_MAIL | NT_NMAI)
75
76
77 /*
78 * static prototypes
79 */
80 static int donote (char *, int) PURE;
81 static int checkmail (char *, char *, int, int, int);
82 static int remotemail (char *, char *, char *, char *, int, int, int, int,
83 char *, int, const char *);
84
85
86 int
87 main (int argc, char **argv)
88 {
89 bool datesw = true;
90 int notifysw = NT_ALL;
91 int status = 0;
92 bool sasl = false;
93 int tls = 0;
94 bool noverify = false;
95 bool snoop = false;
96 int vecp = 0;
97 char *cp, *host = NULL, *port = NULL, *user = NULL, *proxy = NULL;
98 char buf[BUFSIZ], *saslmech = NULL, *auth_svc = NULL;
99 char **argp, **arguments, *vec[MAXVEC];
100 struct passwd *pw;
101
102 if (nmh_init(argv[0], true, true)) { return 1; }
103
104 mts_init ();
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 die("-%s unknown", cp);
117
118 case HELPSW:
119 snprintf (buf, sizeof(buf), "%s [switches] [users ...]",
120 invo_name);
121 print_help (buf, switches, 1);
122 done (0);
123 case VERSIONSW:
124 print_version(invo_name);
125 done (0);
126
127 case DATESW:
128 datesw = true;
129 continue;
130 case NDATESW:
131 datesw = false;
132 continue;
133
134 case NOTESW:
135 if (!(cp = *argp++) || *cp == '-')
136 die("missing argument to %s", argp[-2]);
137 notifysw |= donote (cp, 1);
138 continue;
139 case NNOTESW:
140 if (!(cp = *argp++) || *cp == '-')
141 die("missing argument to %s", argp[-2]);
142 notifysw &= ~donote (cp, 0);
143 continue;
144
145 case HOSTSW:
146 if (!(host = *argp++) || *host == '-')
147 die("missing argument to %s", argp[-2]);
148 continue;
149
150 case PORTSW:
151 if (!(port = *argp++) || *port == '-')
152 die("missing argument to %s", argp[-2]);
153 continue;
154
155 case USERSW:
156 if (!(cp = *argp++) || *cp == '-')
157 die("missing argument to %s", argp[-2]);
158 if (vecp >= MAXVEC-1)
159 die("you can only check %d users at a time", MAXVEC-1);
160 user = vec[vecp++] = cp;
161 continue;
162
163 case SNOOPSW:
164 snoop = true;
165 continue;
166
167 case SASLSW:
168 sasl = true;
169 continue;
170
171 case NOSASLSW:
172 sasl = false;
173 continue;
174
175 case SASLMECHSW:
176 if (!(saslmech = *argp++) || *saslmech == '-')
177 die("missing argument to %s", argp[-2]);
178 continue;
179
180 case TLSSW:
181 tls = 1;
182 continue;
183
184 case INITTLSSW:
185 tls = 2;
186 continue;
187
188 case NOTLSSW:
189 tls = 0;
190 continue;
191
192 case CERTVERSW:
193 noverify = false;
194 continue;
195
196 case NOCERTVERSW:
197 noverify = true;
198 continue;
199
200 case AUTHSERVICESW:
201 #ifdef OAUTH_SUPPORT
202 if (!(auth_svc = *argp++) || *auth_svc == '-')
203 die("missing argument to %s", argp[-2]);
204 #else
205 die("not built with OAuth support");
206 #endif
207 continue;
208
209 case PROXYSW:
210 if (!(proxy = *argp++) || *proxy == '-')
211 die("missing argument to %s", argp[-2]);
212 continue;
213 }
214 }
215 if (vecp >= MAXVEC-1)
216 die("you can only check %d users at a time", MAXVEC-1);
217 vec[vecp++] = cp;
218 }
219
220 /*
221 * If -host is not specified by user
222 */
223 if (!host || !*host) {
224 /*
225 * If "pophost" is specified in mts.conf,
226 * use it as default value.
227 */
228 if (pophost && *pophost)
229 host = pophost;
230 }
231 if (!host || !*host)
232 host = NULL;
233
234 if (vecp != 0)
235 vec[vecp] = NULL;
236
237 if (host) {
238 int tlsflag = 0;
239
240 if (tls == 1)
241 tlsflag = P_STARTTLS;
242 else if (tls == 2)
243 tlsflag = P_INITTLS;
244
245 if (noverify)
246 tlsflag |= P_NOVERIFY;
247
248 if (vecp == 0) {
249 status = remotemail (host, port, user, proxy, notifysw, 1,
250 snoop, sasl, saslmech, tlsflag, auth_svc);
251 } else {
252 for (vecp = 0; vec[vecp]; vecp++)
253 status += remotemail (host, port, vec[vecp], proxy, notifysw, 0,
254 snoop, sasl, saslmech, tlsflag, auth_svc);
255 }
256 } else {
257 if (user == NULL) user = getusername ();
258 if (vecp == 0) {
259 char *home;
260
261 /* Not sure this check makes sense... */
262 if (!geteuid() || NULL == (home = getenv("HOME"))) {
263 pw = getpwnam (user);
264 if (pw == NULL)
265 die("unable to get information about user");
266 home = pw->pw_dir;
267 }
268 status = checkmail (user, home, datesw, notifysw, 1);
269 } else {
270 for (vecp = 0; vec[vecp]; vecp++) {
271 if ((pw = getpwnam (vec[vecp])))
272 status += checkmail (pw->pw_name, pw->pw_dir, datesw, notifysw, 0);
273 else
274 inform("no such user as %s", vec[vecp]);
275 }
276 }
277 } /* host == NULL */
278
279 done (status);
280 return 1;
281 }
282
283
284 #define NOTE_SWITCHES \
285 X("all", 0, NALLSW) \
286 X("mail", 0, NMAISW) \
287 X("nomail", 0, NNMAISW) \
288
289 #define X(sw, minchars, id) id,
290 DEFINE_SWITCH_ENUM(NOTE);
291 #undef X
292
293 #define X(sw, minchars, id) { sw, minchars, id },
294 DEFINE_SWITCH_ARRAY(NOTE, ntswitches);
295 #undef X
296
297
298 static int
299 donote (char *cp, int ntflag)
300 {
301 switch (smatch (cp, ntswitches)) {
302 case AMBIGSW:
303 ambigsw (cp, ntswitches);
304 done (1);
305 case UNKWNSW:
306 die("-%snotify %s unknown", ntflag ? "" : "no", cp);
307
308 case NALLSW:
309 return NT_ALL;
310 case NMAISW:
311 return NT_MAIL;
312 case NNMAISW:
313 return NT_NMAI;
314 }
315
316 return 0; /* Before 1999-07-15, garbage was returned if control got here. */
317 }
318
319
320 static int
321 checkmail (char *user, char *home, int datesw, int notifysw, int personal)
322 {
323 char buffer[BUFSIZ];
324 snprintf(buffer, sizeof buffer, "%s/%s", *mmdfldir ? mmdfldir : home,
325 *mmdflfil ? mmdflfil : user);
326
327 struct stat st;
328 bool statok = stat(buffer, &st) != -1;
329 bool empty = !statok || st.st_size == 0;
330
331 if ((empty && !(notifysw & NT_NMAI)) ||
332 (!empty && !(notifysw & NT_MAIL))) {
333 return empty;
334 }
335
336 if (empty) {
337 if (personal) {
338 fputs("You don't have any mail waiting", stdout);
339 } else {
340 printf("%s doesn't have any mail waiting", user);
341 }
342 } else {
343 char *kind = st.st_mtime < st.st_atime ? "old" : "new";
344 if (personal) {
345 printf("You have %s mail waiting", kind);
346 } else {
347 printf("%s has %s mail waiting", user, kind);
348 }
349 }
350
351 if (datesw && statok && st.st_atime)
352 printf("; last read on %s", dtime(&st.st_atime, 1));
353 putchar('\n');
354
355 return empty;
356 }
357
358
359 extern char response[];
360
361 static int
362 remotemail (char *host, char *port, char *user, char *proxy, int notifysw,
363 int personal, int snoop, int sasl, char *saslmech, int tls,
364 const char *auth_svc)
365 {
366 int nmsgs, nbytes;
367 bool status;
368
369 if (auth_svc == NULL) {
370 if (saslmech && ! strcasecmp(saslmech, "xoauth2")) {
371 die("must specify -authservice with -saslmech xoauth2");
372 }
373 } else {
374 if (user == NULL) {
375 die("must specify -user with -saslmech xoauth2");
376 }
377 }
378
379 /* open the POP connection */
380 if (pop_init (host, port, user, proxy, snoop, sasl, saslmech, tls,
381 auth_svc) == NOTOK
382 || pop_stat (&nmsgs, &nbytes) == NOTOK /* check for messages */
383 || pop_quit () == NOTOK) { /* quit POP connection */
384 inform("%s", response);
385 return 1;
386 }
387
388 if (nmsgs) {
389 if (notifysw & NT_MAIL) {
390 if (personal)
391 fputs("You have ", stdout);
392 else
393 printf ("%s has ", user);
394
395 printf ("%d message%s (%d bytes)",
396 nmsgs, PLURALS(nmsgs), nbytes);
397 }
398 else
399 notifysw = 0;
400
401 status = false;
402 } else {
403 if (notifysw & NT_NMAI)
404 printf (personal ? "You don't %s%s" : "%s doesn't %s",
405 personal ? "" : user, "have any mail waiting");
406 else
407 notifysw = 0;
408 status = true;
409 }
410 if (notifysw)
411 printf (" on %s\n", host);
412
413 return status;
414 }