Fix missing va_end call in uip/mhmisc.c
[mmh] / uip / whom.c
1 /*
2 ** whom.c -- list recipients of message
3 **
4 ** This code is Copyright (c) 2012, by the authors of mmh.  See the
5 ** COPYRIGHT file in the root directory of the nmh distribution for
6 ** complete copyright information.
7 */
8
9 #include <h/mh.h>
10 #include <h/addrsbr.h>
11 #include <h/fmt_scan.h>
12 #include <h/utils.h>
13 #include <locale.h>
14 #include <sysexits.h>
15
16 static struct swit switches[] = {
17 #define VERSIONSW 0
18         { "Version", 0 },
19 #define HELPSW 1
20         { "help", 0 },
21 #define TOCCSW 2
22         { "tocc", 0 },
23 #define NTOCCSW 3
24         { "notocc", 2 },
25 #define DCCSW 4
26         { "dcc", 0 },
27 #define NDCCSW 5
28         { "nodcc", 2 },
29 #define BCCSW 6
30         { "bcc", 0 },
31 #define NBCCSW 7
32         { "nobcc", 2 },
33 #define ALISW 8
34         { "alias", 0 },
35 #define NALISW 9
36         { "noalias", 2 },
37         { NULL, 0 }
38 };
39
40
41 #define NFILES 32
42
43 #define HRESENT (1<<1)
44 #define HTO (1<<2)
45 #define HCC (1<<3)
46 #define HDCC (1<<4)
47 #define HBCC (1<<5)
48
49 struct mailname head = {0};
50 struct mailname *mp = &head;
51
52 static char *cmd;
53
54 static int resent = 0;  /* consider normal or resent headers */
55 static int toccsw = 1;  /* list sighted recipients */
56 static int dccsw = 1;  /* list hidden dcc recipients */
57 static int bccsw = 1;  /* list hidden bcc recipients */
58 static int alisw = 1;  /* expand aliases on rcpt addrs */
59
60 static char *dccsep = "\t==DCC==";
61 static char *bccsep = "\t==BCC==";
62
63
64 /*
65 ** static prototypes
66 */
67 static int process(char *);
68 static void proc_hdr(char *, char *);
69 static int print(void);
70 static int printdcc(void);
71 static int printbcc(void);
72 static void printone(struct mailname *);
73
74
75 int
76 main(int argc, char **argv)
77 {
78         int filep=0, naddrs=0, n;
79         char *cp;
80         char buf[BUFSIZ], **argp;
81         char **arguments, *files[NFILES];
82         FILE *in;
83
84         setlocale(LC_ALL, "");
85         invo_name = mhbasename(argv[0]);
86
87         /* read user profile/context */
88         context_read();
89
90         arguments = getarguments(invo_name, argc, argv, 1);
91         argp = arguments;
92
93         while ((cp = *argp++)) {
94                 if (*cp == '-') {
95                         switch (smatch(++cp, switches)) {
96                         case AMBIGSW:
97                                 ambigsw(cp, switches);
98                                 exit(EX_USAGE);
99
100                         case UNKWNSW:
101                                 adios(EX_USAGE, NULL, "-%s unknown", cp);
102
103                         case HELPSW:
104                                 snprintf(buf, sizeof(buf),
105                                                 "%s [switches] file ...",
106                                                 invo_name);
107                                 print_help(buf, switches, 1);
108                                 exit(argc == 2 ? EX_OK : EX_USAGE);
109                         case VERSIONSW:
110                                 print_version(invo_name);
111                                 exit(argc == 2 ? EX_OK : EX_USAGE);
112
113                         case TOCCSW:
114                                 toccsw = 1;
115                                 continue;
116                         case NTOCCSW:
117                                 toccsw = 0;
118                                 continue;
119
120                         case DCCSW:
121                                 dccsw = 1;
122                                 continue;
123                         case NDCCSW:
124                                 dccsw = 0;
125                                 continue;
126
127                         case BCCSW:
128                                 bccsw = 1;
129                                 continue;
130                         case NBCCSW:
131                                 bccsw = 0;
132                                 continue;
133
134                         case ALISW:
135                                 alisw = 1;
136                                 continue;
137                         case NALISW:
138                                 alisw = 0;
139                                 continue;
140                         }
141                 }
142                 if (filep > NFILES) {
143                         adios(EX_USAGE, NULL, "too many files (more than %d)",
144                                         NFILES);
145                 } else {
146                         files[filep++] = cp;
147                 }
148         }
149         files[filep] = NULL;
150         if (!filep) {
151                 adios(EX_USAGE, NULL, "usage: %s [switches] file ...", invo_name);
152         }
153         if (!toccsw && !dccsw && !bccsw) {
154                 adios(EX_USAGE, NULL, "use at least one of: -tocc -dcc -bcc");
155         }
156         for (filep=0; files[filep]; filep++) {
157                 process(files[filep]);
158         }
159
160         cmd = add("ali -list", NULL);
161         if ((n=print()) && alisw) {
162                 if (!(in = popen(cmd, "r"))) {
163                         adios(EX_IOERR, "popen", "unable to");
164                 }
165                 while (fgets(buf, sizeof buf, in)) {
166                         fputs(buf, stdout);
167                 }
168                 pclose(in);
169         }
170         mh_free0(&cmd);
171         naddrs += n;
172
173         cmd = add("ali -list", NULL);
174         if ((n=printdcc()) && alisw) {
175                 if (!(in = popen(cmd, "r"))) {
176                         adios(EX_IOERR, "popen", "unable to");
177                 }
178                 while (fgets(buf, sizeof buf, in)) {
179                         fputs(buf, stdout);
180                 }
181                 pclose(in);
182         }
183         mh_free0(&cmd);
184         naddrs += n;
185
186         cmd = add("ali -list", NULL);
187         if ((n=printbcc()) && alisw) {
188                 if (!(in = popen(cmd, "r"))) {
189                         adios(EX_IOERR, "popen", "unable to");
190                 }
191                 while (fgets(buf, sizeof buf, in)) {
192                         fputs(buf, stdout);
193                 }
194                 pclose(in);
195         }
196         mh_free0(&cmd);
197         naddrs += n;
198
199         return naddrs ? 0 : 1;
200 }
201
202
203 static int
204 process(char *file)
205 {
206         enum state state;
207         struct field f = {{0}};
208         int compnum;
209         FILE *in;
210
211
212         if ((in = fopen(file, "r")) == NULL) {
213                 adios(EX_IOERR, file, "unable to open");
214         }
215
216         for (compnum=1, state=FLD2;; compnum++) {
217                 switch (state = m_getfld2(state, &f, in)) {
218                 case LENERR2:
219                         state = FLD2;
220                         /* FALL */
221                 case FLD2:
222                         proc_hdr(f.name, f.value);
223                         continue;
224
225                 case BODY2:
226                 case FILEEOF2:
227                         break;
228
229                 case FMTERR2:
230                         advise(NULL, "message format error in component #%d", compnum);
231                         continue;
232
233                 case IOERR2:
234                         adios(EX_DATAERR, NULL, "message format error in component #%d",
235                                         compnum);
236
237                 default:
238                         adios(EX_SOFTWARE, NULL, "getfld() returned %d", state);
239                 }
240                 break;
241         }
242         fclose(in);
243         return 0;
244 }
245
246
247 /*
248 ** Check if the header contains addresses we're interested in.
249 ** If so, extract the addresses and add them to the global list.
250 */
251 static void
252 proc_hdr(char *name, char *val)
253 {
254         char *cp;
255         int type = 0;
256
257         while (*val==' ' || *val=='\t' || *val=='\n') {
258                 val++;
259         }
260         if (strncasecmp(name, "resent-", 7)==0) {
261                 resent = HRESENT;
262         }
263         if (mh_strcasecmp(name, "to")==0) {
264                 type = HTO;
265         } else if (mh_strcasecmp(name, "cc")==0) {
266                 type = HCC;
267         } else if (mh_strcasecmp(name, "dcc")==0) {
268                 type = HDCC;
269         } else if (mh_strcasecmp(name, "bcc")==0) {
270                 type = HBCC;
271         } else if (mh_strcasecmp(name, "resent-to")==0) {
272                 type = (HRESENT | HTO);
273         } else if (mh_strcasecmp(name, "resent-cc")==0) {
274                 type = (HRESENT | HCC);
275         } else if (mh_strcasecmp(name, "resent-dcc")==0) {
276                 type = (HRESENT | HDCC);
277         } else if (mh_strcasecmp(name, "resent-bcc")==0) {
278                 type = (HRESENT | HBCC);
279         }
280         /* ignore non-recpient headers */
281         if (!type) {
282                 return;
283         }
284         /* ignore recipient headers we are not interested in */ 
285         if ((type&HTO || type&HCC) && !toccsw) {
286                 return;
287         }
288         if ((type&HDCC) && !dccsw) {
289                 return;
290         }
291         if ((type&HBCC) && !bccsw) {
292                 return;
293         }
294
295         while ((cp = getname(val))) {
296                 if (!(mp->m_next = getm(cp, NULL, 0, AD_NAME, NULL))) {
297                         adios(EX_DATAERR, NULL, "illegal address: %s", cp);
298                 }
299                 mp = mp->m_next;
300                 if (mp->m_type == BADHOST) {
301                         admonish(NULL, "bad address `%s'", mp->m_text);
302                         continue;
303                 }
304                 mp->m_type = type;
305         }
306 }
307
308
309 /*
310 ** Walk through the list of addresses and print the right ones.
311 */
312 static int
313 print(void)
314 {
315         int naddrs = 0;
316
317         for (mp=head.m_next; mp; mp=mp->m_next) {
318                 /* skip unless both are resent or neither one is */
319                 if (resent != (mp->m_type&HRESENT)) {
320                         continue;
321                 }
322                 if (mp->m_type & (HTO|HCC)) {
323                         naddrs++;
324                         printone(mp);
325                 }
326         }
327         return naddrs;
328 }
329
330 /*
331 ** Walk through the list of addresses and print the right ones.
332 */
333 static int
334 printdcc(void)
335 {
336         int naddrs = 0;
337
338         for (mp=head.m_next; mp; mp=mp->m_next) {
339                 /* skip unless both are resent or neither one is */
340                 if (resent != (mp->m_type&HRESENT)) {
341                         continue;
342                 }
343                 if (mp->m_type & HDCC) {
344                         if (!naddrs && (toccsw || bccsw)) {
345                                 puts(dccsep);
346                         }
347                         naddrs++;
348                         printone(mp);
349                 }
350         }
351         return naddrs;
352 }
353
354 /*
355 ** Walk through the list of addresses and print the right ones.
356 */
357 static int
358 printbcc(void)
359 {
360         int naddrs = 0;
361
362         for (mp=head.m_next; mp; mp=mp->m_next) {
363                 /* skip unless both are resent or neither one is */
364                 if (resent != (mp->m_type&HRESENT)) {
365                         continue;
366                 }
367                 if (mp->m_type & HBCC) {
368                         if (!naddrs && (toccsw || dccsw)) {
369                                 puts(bccsep);
370                         }
371                         naddrs++;
372                         printone(mp);
373                 }
374         }
375         return naddrs;
376 }
377
378
379 /*
380 ** Print one single address in appropriate form.
381 */
382 static void
383 printone(struct mailname *mp)
384 {
385         char buf[BUFSIZ];
386
387         if (mp->m_host) {
388                 snprintf(buf, sizeof buf, " %s@%s", mp->m_mbox, mp->m_host);
389         } else {
390                 snprintf(buf, sizeof buf, " %s", mp->m_mbox);
391         }
392         if (alisw) {
393                 cmd = add(buf, cmd);
394         } else {
395                 puts(buf+1);
396         }
397 }