d313f05656097d0621b3e609404131abc029e75a
[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         free(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         free(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         free(cmd);
197         naddrs += n;
198
199         return naddrs ? 0 : 1;
200 }
201
202
203 static int
204 process(char *file)
205 {
206         int state, compnum;
207         char *cp = NULL;
208         char buf[BUFSIZ], name[NAMESZ];
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 = FLD;;) {
217                 switch (state = m_getfld(state, name, buf, sizeof(buf), in)) {
218                 case FLD:
219                         compnum++;
220                         proc_hdr(name, buf);
221                         continue;
222
223                 case FLDPLUS:
224                         compnum++;
225                         cp = getcpy(buf);
226                         while (state == FLDPLUS) {
227                                 state = m_getfld(state, name, buf,
228                                                 sizeof(buf), in);
229                                 cp = add(buf, cp);
230                         }
231                         proc_hdr(name, cp);
232                         free(cp);
233                         continue;
234
235                 case BODY:
236                 case FILEEOF:
237                         break;
238
239                 case LENERR:
240                 case FMTERR:
241                         adios(EX_DATAERR, NULL, "message format error in component #%d",
242                                         compnum);
243
244                 default:
245                         adios(EX_SOFTWARE, NULL, "getfld() returned %d", state);
246                 }
247                 break;
248         }
249         fclose(in);
250         return 0;
251 }
252
253
254 /*
255 ** Check if the header contains addresses we're interested in.
256 ** If so, extract the addresses and add them to the global list.
257 */
258 static void
259 proc_hdr(char *name, char *val)
260 {
261         char *cp;
262         int type = 0;
263
264         while (*val==' ' || *val=='\t' || *val=='\n') {
265                 val++;
266         }
267         if (strncasecmp(name, "resent-", 7)==0) {
268                 resent = HRESENT;
269         }
270         if (mh_strcasecmp(name, "to")==0) {
271                 type = HTO;
272         } else if (mh_strcasecmp(name, "cc")==0) {
273                 type = HCC;
274         } else if (mh_strcasecmp(name, "dcc")==0) {
275                 type = HDCC;
276         } else if (mh_strcasecmp(name, "bcc")==0) {
277                 type = HBCC;
278         } else if (mh_strcasecmp(name, "resent-to")==0) {
279                 type = (HRESENT | HTO);
280         } else if (mh_strcasecmp(name, "resent-cc")==0) {
281                 type = (HRESENT | HCC);
282         } else if (mh_strcasecmp(name, "resent-dcc")==0) {
283                 type = (HRESENT | HDCC);
284         } else if (mh_strcasecmp(name, "resent-bcc")==0) {
285                 type = (HRESENT | HBCC);
286         }
287         /* ignore non-recpient headers */
288         if (!type) {
289                 return;
290         }
291         /* ignore recipient headers we are not interested in */ 
292         if ((type&HTO || type&HCC) && !toccsw) {
293                 return;
294         }
295         if ((type&HDCC) && !dccsw) {
296                 return;
297         }
298         if ((type&HBCC) && !bccsw) {
299                 return;
300         }
301
302         while ((cp = getname(val))) {
303                 if (!(mp->m_next = getm(cp, NULL, 0, AD_NAME, NULL))) {
304                         adios(EX_DATAERR, NULL, "illegal address: %s", cp);
305                 }
306                 mp = mp->m_next;
307                 if (mp->m_type == BADHOST) {
308                         admonish(NULL, "bad address `%s'", mp->m_text);
309                         continue;
310                 }
311                 mp->m_type = type;
312         }
313 }
314
315
316 /*
317 ** Walk through the list of addresses and print the right ones.
318 */
319 static int
320 print(void)
321 {
322         int naddrs = 0;
323
324         for (mp=head.m_next; mp; mp=mp->m_next) {
325                 /* skip unless both are resent or neither one is */
326                 if (resent != (mp->m_type&HRESENT)) {
327                         continue;
328                 }
329                 if (mp->m_type & (HTO|HCC)) {
330                         naddrs++;
331                         printone(mp);
332                 }
333         }
334         return naddrs;
335 }
336
337 /*
338 ** Walk through the list of addresses and print the right ones.
339 */
340 static int
341 printdcc(void)
342 {
343         int naddrs = 0;
344
345         for (mp=head.m_next; mp; mp=mp->m_next) {
346                 /* skip unless both are resent or neither one is */
347                 if (resent != (mp->m_type&HRESENT)) {
348                         continue;
349                 }
350                 if (mp->m_type & HDCC) {
351                         if (!naddrs && (toccsw || bccsw)) {
352                                 puts(dccsep);
353                         }
354                         naddrs++;
355                         printone(mp);
356                 }
357         }
358         return naddrs;
359 }
360
361 /*
362 ** Walk through the list of addresses and print the right ones.
363 */
364 static int
365 printbcc(void)
366 {
367         int naddrs = 0;
368
369         for (mp=head.m_next; mp; mp=mp->m_next) {
370                 /* skip unless both are resent or neither one is */
371                 if (resent != (mp->m_type&HRESENT)) {
372                         continue;
373                 }
374                 if (mp->m_type & HBCC) {
375                         if (!naddrs && (toccsw || dccsw)) {
376                                 puts(bccsep);
377                         }
378                         naddrs++;
379                         printone(mp);
380                 }
381         }
382         return naddrs;
383 }
384
385
386 /*
387 ** Print one single address in appropriate form.
388 */
389 static void
390 printone(struct mailname *mp)
391 {
392         char buf[BUFSIZ];
393
394         if (mp->m_host) {
395                 snprintf(buf, sizeof buf, " %s@%s", mp->m_mbox, mp->m_host);
396         } else {
397                 snprintf(buf, sizeof buf, " %s", mp->m_mbox);
398         }
399         if (alisw) {
400                 cmd = add(buf, cmd);
401         } else {
402                 puts(buf+1);
403         }
404 }