3d3eb8464ebb937aca6a84ebd15bf03e6efb28ad
[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
15 static struct swit switches[] = {
16 #define VERSIONSW 0
17         { "Version", 0 },
18 #define HELPSW 1
19         { "help", 0 },
20 #define TOCCSW 2
21         { "tocc", 0 },
22 #define NTOCCSW 3
23         { "notocc", 2 },
24 #define BCCSW 4
25         { "bcc", 0 },
26 #define NBCCSW 5
27         { "nobcc", 2 },
28 #define ALISW 6
29         { "alias", 0 },
30 #define NALISW 7
31         { "noalias", 2 },
32         { NULL, 0 }
33 };
34
35
36 #define NFILES 32
37
38 #define HRESENT (1<<1)
39 #define HTO (1<<2)
40 #define HCC (1<<3)
41 #define HBCC (1<<4)
42
43 struct mailname head = {0};
44 struct mailname *mp = &head;
45
46 static char *cmd;
47
48 static int resent = 0;  /* consider normal or resent headers */
49 static int toccsw = 1;  /* list sighted recipients */
50 static int bccsw = 1;  /* list hidden recipients */
51 static int alisw = 1;  /* expand aliases on rcpt addrs */
52
53 static char *separator = "\t==BCC==";
54
55
56 /*
57 ** static prototypes
58 */
59 static int process(char *);
60 static void proc_hdr(char *, char *);
61 static int print(void);
62 static int printbcc(void);
63 static void printone(struct mailname *);
64
65
66 int
67 main(int argc, char **argv)
68 {
69         int filep=0, naddrs=0, n;
70         char *cp;
71         char buf[BUFSIZ], **argp;
72         char **arguments, *files[NFILES];
73         FILE *in;
74
75         setlocale(LC_ALL, "");
76         invo_name = mhbasename(argv[0]);
77
78         /* read user profile/context */
79         context_read();
80
81         arguments = getarguments(invo_name, argc, argv, 1);
82         argp = arguments;
83
84         while ((cp = *argp++)) {
85                 if (*cp == '-') {
86                         switch (smatch(++cp, switches)) {
87                         case AMBIGSW:
88                                 ambigsw(cp, switches);
89                                 exit(1);
90
91                         case UNKWNSW:
92                                 adios(NULL, "-%s unknown", cp);
93
94                         case HELPSW:
95                                 snprintf(buf, sizeof(buf),
96                                                 "%s [switches] file ...",
97                                                 invo_name);
98                                 print_help(buf, switches, 1);
99                                 exit(0);
100                         case VERSIONSW:
101                                 print_version(invo_name);
102                                 exit(0);
103
104                         case TOCCSW:
105                                 toccsw = 1;
106                                 continue;
107                         case NTOCCSW:
108                                 toccsw = 0;
109                                 continue;
110
111                         case BCCSW:
112                                 bccsw = 1;
113                                 continue;
114                         case NBCCSW:
115                                 bccsw = 0;
116                                 continue;
117
118                         case ALISW:
119                                 alisw = 1;
120                                 continue;
121                         case NALISW:
122                                 alisw = 0;
123                                 continue;
124                         }
125                 }
126                 if (filep > NFILES) {
127                         adios(NULL, "too many files (more than %d)",
128                                         NFILES);
129                 } else {
130                         files[filep++] = cp;
131                 }
132         }
133         files[filep] = NULL;
134         if (!filep) {
135                 adios(NULL, "usage: %s [switches] file ...", invo_name);
136         }
137         if (!toccsw && !bccsw) {
138                 adios(NULL, "give -tocc or -bcc or both to produce output");
139         }
140         for (filep=0; files[filep]; filep++) {
141                 process(files[filep]);
142         }
143
144         cmd = add("ali -list", NULL);
145         if ((n=print()) && alisw) {
146                 if (!(in = popen(cmd, "r"))) {
147                         adios("popen", "unable to");
148                 }
149                 while (fgets(buf, sizeof buf, in)) {
150                         fputs(buf, stdout);
151                 }
152                 pclose(in);
153         }
154         free(cmd);
155         naddrs += n;
156
157         cmd = add("ali -list", NULL);
158         if ((n=printbcc()) && alisw) {
159                 if (!(in = popen(cmd, "r"))) {
160                         adios("popen", "unable to");
161                 }
162                 while (fgets(buf, sizeof buf, in)) {
163                         fputs(buf, stdout);
164                 }
165                 pclose(in);
166         }
167         free(cmd);
168         naddrs += n;
169         return naddrs ? 0 : 1;
170 }
171
172
173 static int
174 process(char *file)
175 {
176         int state, compnum;
177         char *cp = NULL;
178         char buf[BUFSIZ], name[NAMESZ];
179         FILE *in;
180
181
182         if ((in = fopen(file, "r")) == NULL) {
183                 adios(file, "unable to open");
184         }
185
186         for (compnum = 1, state = FLD;;) {
187                 switch (state = m_getfld(state, name, buf, sizeof(buf), in)) {
188                 case FLD:
189                         compnum++;
190                         proc_hdr(name, buf);
191                         continue;
192
193                 case FLDPLUS:
194                         compnum++;
195                         cp = add(buf, cp);
196                         while (state == FLDPLUS) {
197                                 state = m_getfld(state, name, buf,
198                                                 sizeof(buf), in);
199                                 cp = add(buf, cp);
200                         }
201                         proc_hdr(name, cp);
202                         free(cp);
203                         continue;
204
205                 case BODY:
206                 case FILEEOF:
207                         break;
208
209                 case LENERR:
210                 case FMTERR:
211                         adios(NULL, "message format error in component #%d",
212                                         compnum);
213
214                 default:
215                         adios(NULL, "getfld() returned %d", state);
216                 }
217                 break;
218         }
219         fclose(in);
220         return 0;
221 }
222
223
224 /*
225 ** Check if the header contains addresses we're interested in.
226 ** If so, extract the addresses and add them to the global list.
227 */
228 static void
229 proc_hdr(char *name, char *val)
230 {
231         char *cp;
232         int type = 0;
233
234         while (*val==' ' || *val=='\t' || *val=='\n') {
235                 val++;
236         }
237         if (strncasecmp(name, "resent-", 7)==0) {
238                 resent = HRESENT;
239         }
240         if (mh_strcasecmp(name, "to")==0) {
241                 type = HTO;
242         } else if (mh_strcasecmp(name, "cc")==0) {
243                 type = HCC;
244         } else if (mh_strcasecmp(name, "bcc")==0) {
245                 type = HBCC;
246         } else if (mh_strcasecmp(name, "resent-to")==0) {
247                 type = (HRESENT | HTO);
248         } else if (mh_strcasecmp(name, "resent-cc")==0) {
249                 type = (HRESENT | HCC);
250         } else if (mh_strcasecmp(name, "resent-bcc")==0) {
251                 type = (HRESENT | HBCC);
252         }
253         /* ignore non-recpient headers */
254         if (!type) {
255                 return;
256         }
257         /* ignore recipient headers we are not interested in */ 
258         if ((type&HTO || type&HCC) && !toccsw) {
259                 return;
260         }
261         if ((type&HBCC) && !bccsw) {
262                 return;
263         }
264
265         while ((cp = getname(val))) {
266                 if (!(mp->m_next = getm(cp, NULL, 0, AD_NAME, NULL))) {
267                         adios(NULL, "illegal address: %s", cp);
268                 }
269                 mp = mp->m_next;
270                 if (mp->m_type == BADHOST) {
271                         admonish(NULL, "bad address `%s'", mp->m_text);
272                         continue;
273                 }
274                 mp->m_type = type;
275         }
276 }
277
278
279 /*
280 ** Walk through the list of addresses and print the right ones.
281 */
282 static int
283 print(void)
284 {
285         int naddrs = 0;
286
287         for (mp=head.m_next; mp; mp=mp->m_next) {
288                 /* skip unless both are resent or neither one is */
289                 if (resent != (mp->m_type&HRESENT)) {
290                         continue;
291                 }
292                 if (mp->m_type & (HTO|HCC)) {
293                         naddrs++;
294                         printone(mp);
295                 }
296         }
297         return naddrs;
298 }
299
300 /*
301 ** Walk through the list of addresses and print the right ones.
302 */
303 static int
304 printbcc(void)
305 {
306         int naddrs = 0;
307
308         for (mp=head.m_next; mp; mp=mp->m_next) {
309                 /* skip unless both are resent or neither one is */
310                 if (resent != (mp->m_type&HRESENT)) {
311                         continue;
312                 }
313                 if (mp->m_type & HBCC) {
314                         if (!naddrs && toccsw) {
315                                 puts(separator);
316                         }
317                         naddrs++;
318                         printone(mp);
319                 }
320         }
321         return naddrs;
322 }
323
324
325 /*
326 ** Print one single address in appropriate form.
327 */
328 static void
329 printone(struct mailname *mp)
330 {
331         char buf[BUFSIZ];
332
333         if (mp->m_host) {
334                 snprintf(buf, sizeof buf, " %s@%s", mp->m_mbox, mp->m_host);
335         } else {
336                 snprintf(buf, sizeof buf, " %s", mp->m_mbox);
337         }
338         if (alisw) {
339                 cmd = add(buf, cmd);
340         } else {
341                 puts(buf+1);
342         }
343 }