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