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