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