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