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