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