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