1 /*
2 mairix - message index builder and finder for maildir folders.
3
4 **********************************************************************
5 * Copyright (C) Richard P. Curnow 2006,2007
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of version 2 of the GNU General Public License as
9 * published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 **********************************************************************
21 */
22
23 #ifdef VERBOSE_TEST
24 #define TEST 1
25 #endif
26
27 /* Parse name/value pairs from mail headers into a lookup table. */
28 #include <stdio.h>
29 #include <ctype.h>
30 #include "mairix.h"
31 #include "nvptypes.h"
32 #include "nvpscan.h"
33 #include "nvp.h"
34
35 enum nvp_type {/*{{{*/
36 NVP_NAME,
37 NVP_MAJORMINOR,
38 NVP_NAMEVALUE
39 };
40 /*}}}*/
41 struct nvp_entry {/*{{{*/
42 struct nvp_entry *next;
43 struct nvp_entry *prev;
44 enum nvp_type type;
45 char *lhs;
46 char *rhs;
47 };
48 /*}}}*/
49 struct nvp {/*{{{*/
50 struct nvp_entry *first, *last;
51 };
52 /*}}}*/
append(struct nvp * nvp,struct nvp_entry * ne)53 static void append(struct nvp *nvp, struct nvp_entry *ne)/*{{{*/
54 {
55 if (!ne->rhs) {
56 ne->rhs = Malloc(1);
57 ne->rhs[0] = 0;
58 }
59 if (!ne->lhs) {
60 ne->lhs = Malloc(1);
61 ne->lhs[0] = 0;
62 }
63 ne->next = NULL;
64 ne->prev = nvp->last;
65 if (nvp->last) nvp->last->next = ne;
66 else nvp->first = ne;
67 nvp->last = ne;
68 }
69 /*}}}*/
append_name(struct nvp * nvp,char ** name)70 static void append_name(struct nvp *nvp, char **name)/*{{{*/
71 {
72 struct nvp_entry *ne;
73 ne = new(struct nvp_entry);
74 ne->type = NVP_NAME;
75 ne->lhs = *name;
76 *name = NULL;
77 append(nvp, ne);
78 }
79 /*}}}*/
append_majorminor(struct nvp * nvp,char ** major,char ** minor)80 static void append_majorminor(struct nvp *nvp, char **major, char **minor)/*{{{*/
81 {
82 struct nvp_entry *ne;
83 ne = new(struct nvp_entry);
84 ne->type = NVP_MAJORMINOR;
85 ne->lhs = *major;
86 ne->rhs = *minor;
87 *major = *minor = NULL;
88 append(nvp, ne);
89
90 }
91 /*}}}*/
append_namevalue(struct nvp * nvp,char ** name,char ** value)92 static void append_namevalue(struct nvp *nvp, char **name, char **value)/*{{{*/
93 {
94 struct nvp_entry *ne;
95 ne = new(struct nvp_entry);
96 ne->type = NVP_NAMEVALUE;
97 ne->lhs = *name;
98 ne->rhs = *value;
99 *name = *value = NULL;
100 append(nvp, ne);
101 }
102 /*}}}*/
combine_namevalue(struct nvp * nvp,char ** name,char ** value)103 static void combine_namevalue(struct nvp *nvp, char **name, char **value)/*{{{*/
104 {
105 struct nvp_entry *n;
106 for (n=nvp->first; n; n=n->next) {
107 if (n->type == NVP_NAMEVALUE) {
108 if (!strcmp(n->lhs, *name)) {
109 char *new_rhs;
110 new_rhs = new_array(char, strlen(n->rhs) + strlen(*value) + 1);
111 strcpy(new_rhs, n->rhs);
112 strcat(new_rhs, *value);
113 free(n->rhs);
114 n->rhs = new_rhs;
115 return;
116 }
117 }
118 }
119 /* No match : it's the first one */
120 append_namevalue(nvp, name, value);
121 }
122 /*}}}*/
hex_to_val(int ch)123 static int hex_to_val(int ch)/*{{{*/
124 {
125 if (isdigit(ch))
126 return (ch - '0');
127 if (ch >= 'a' && ch <= 'f')
128 return (10 + ch - 'a');
129 if (ch >= 'A' && ch <= 'F')
130 return (10 + ch - 'A');
131 return (-1);
132 }
133 /*}}}*/
release_nvp(struct nvp * nvp)134 static void release_nvp(struct nvp *nvp)/*{{{*/
135 {
136 struct nvp_entry *e, *ne;
137 for (e=nvp->first; e; e=ne) {
138 ne = e->next;
139 switch (e->type) {
140 case NVP_NAME:
141 free(e->lhs);
142 break;
143 case NVP_MAJORMINOR:
144 case NVP_NAMEVALUE:
145 free(e->lhs);
146 free(e->rhs);
147 break;
148 }
149 free(e);
150 }
151 free(nvp);
152 }
153 /*}}}*/
make_nvp(struct msg_src * src,char * s,const char * pfx)154 struct nvp *make_nvp(struct msg_src *src, char *s, const char *pfx)/*{{{*/
155 {
156 int current_state;
157 unsigned int tok;
158 char *q, *tempsrc, *tempdst;
159 unsigned char qq;
160 char *name = NULL;
161 char *minor = NULL;
162 char *value = NULL;
163 char *copy_start;
164 enum nvp_action last_action, current_action;
165 enum nvp_copier last_copier;
166 struct nvp *result;
167 size_t pfxlen;
168
169 pfxlen = strlen(pfx);
170 if (strncasecmp(pfx, s, pfxlen))
171 return NULL;
172 s += pfxlen;
173
174 result = new(struct nvp);
175 result->first = result->last = NULL;
176
177 current_state = nvp_in;
178
179 q = s;
180 last_action = GOT_NOTHING;
181 last_copier = COPY_NOWHERE;
182 do {
183 qq = *(unsigned char *) q;
184 if (qq) {
185 tok = nvp_char2tok[qq];
186 } else {
187 tok = nvp_EOS;
188 }
189 current_state = nvp_next_state(current_state, tok);
190 #ifdef VERBOSE_TEST
191 fprintf(stderr, "Char %02x (%c) tok=%d new_current_state=%d\n",
192 qq, ((qq>=32) && (qq<=126)) ? qq : '.',
193 tok, current_state);
194 #endif
195
196 if (current_state < 0) {
197 #ifdef TEST
198 fprintf(stderr, "'%s' could not be parsed\n", s);
199 #else
200 fprintf(stderr, "Header '%s%s' in %s could not be parsed\n",
201 pfx, s, format_msg_src(src));
202 #endif
203 release_nvp(result);
204 return NULL;
205 }
206
207 if (nvp_copier[current_state] != last_copier) {
208 if (last_copier != COPY_NOWHERE) {
209 char *newstring = Malloc(q - copy_start + 1);
210 memcpy(newstring, copy_start, q - copy_start);
211 newstring[q - copy_start] = 0;
212 switch (last_copier) {
213 case COPY_TO_NAME:
214 free(name);
215 name = newstring;
216 #ifdef VERBOSE_TEST
217 fprintf(stderr, " COPY_TO_NAME \"%s\"\n", name);
218 #endif
219 break;
220 case COPY_TO_MINOR:
221 free(minor);
222 minor = newstring;
223 #ifdef VERBOSE_TEST
224 fprintf(stderr, " COPY_TO_MINOR \"%s\"\n", minor);
225 #endif
226 break;
227 case COPY_TO_VALUE:
228 free(value);
229 value = newstring;
230 #ifdef VERBOSE_TEST
231 fprintf(stderr, " COPY_TO_VALUE \"%s\"\n", value);
232 #endif
233 break;
234 case COPY_NOWHERE:
235 /* NOTREACHED */
236 break;
237 }
238 }
239 last_copier = nvp_copier[current_state];
240 copy_start = q;
241 }
242
243 current_action = nvp_action[current_state];
244 switch (current_action) {
245 case GOT_NAME:
246 case GOT_NAME_TRAILING_SPACE:
247 case GOT_MAJORMINOR:
248 case GOT_NAMEVALUE:
249 case GOT_NAMEVALUE_CONT:
250 case GOT_NAMEVALUE_CSET:
251 case GOT_NAMEVALUE_CCONT:
252 #ifdef VERBOSE_TEST
253 fprintf(stderr, " Setting last action to %d\n", current_action);
254 #endif
255 last_action = current_action;
256 break;
257 case GOT_TERMINATOR:
258 #ifdef VERBOSE_TEST
259 fprintf(stderr, " Hit terminator; last_action=%d\n", last_action);
260 #endif
261 switch (last_action) {
262 case GOT_NAME:
263 append_name(result, &name);
264 break;
265 case GOT_NAME_TRAILING_SPACE:
266 tempdst = name + strlen(name);
267 while (isspace(*--tempdst)) {}
268 *++tempdst = 0;
269 append_name(result, &name);
270 break;
271 case GOT_MAJORMINOR:
272 append_majorminor(result, &name, &minor);
273 break;
274 case GOT_NAMEVALUE:
275 append_namevalue(result, &name, &value);
276 break;
277 case GOT_NAMEVALUE_CSET:
278 case GOT_NAMEVALUE_CCONT:
279 for(tempsrc = tempdst = value; *tempsrc; tempsrc++) {
280 if (*tempsrc == '%') {
281 int val = hex_to_val(*++tempsrc) << 4;
282 val |= hex_to_val(*++tempsrc);
283 if (val < 0) {
284 #ifdef TEST
285 fprintf(stderr, "'%s' could not be parsed (%%)\n", s);
286 #else
287 fprintf(stderr, "Header '%s%s' in %s could not be parsed\n",
288 pfx, s, format_msg_src(src));
289 #endif
290 release_nvp(result);
291 result = NULL;
292 goto out;
293 }
294 *tempdst++ = val;
295 } else
296 *tempdst++ = *tempsrc;
297 }
298 *tempdst = 0;
299 if (current_action == GOT_NAMEVALUE_CSET)
300 append_namevalue(result, &name, &value);
301 else
302 combine_namevalue(result, &name, &value);
303 break;
304 case GOT_NAMEVALUE_CONT:
305 combine_namevalue(result, &name, &value);
306 break;
307 default:
308 break;
309 }
310 break;
311 case GOT_NOTHING:
312 break;
313 }
314
315 q++;
316 } while (tok != nvp_EOS);
317
318 out:
319 /* Not all productions consume these values */
320 free(name);
321 free(value);
322 free(minor);
323 return result;
324 }
325 /*}}}*/
free_nvp(struct nvp * nvp)326 void free_nvp(struct nvp *nvp)/*{{{*/
327 {
328 struct nvp_entry *ne, *nne;
329 for (ne = nvp->first; ne; ne=nne) {
330 nne = ne->next;
331 switch (ne->type) {
332 case NVP_NAME:
333 free(ne->lhs);
334 break;
335 case NVP_MAJORMINOR:
336 case NVP_NAMEVALUE:
337 free(ne->lhs);
338 free(ne->rhs);
339 break;
340 }
341 free(ne);
342 }
343 free(nvp);
344 }
345 /*}}}*/
nvp_lookup(struct nvp * nvp,const char * name)346 const char *nvp_lookup(struct nvp *nvp, const char *name)/*{{{*/
347 {
348 struct nvp_entry *ne;
349 for (ne = nvp->first; ne; ne=ne->next) {
350 if (ne->type == NVP_NAMEVALUE) {
351 if (!strcmp(ne->lhs, name)) {
352 return ne->rhs;
353 }
354 }
355 }
356 return NULL;
357 }
358 /*}}}*/
nvp_lookupcase(struct nvp * nvp,const char * name)359 const char *nvp_lookupcase(struct nvp *nvp, const char *name)/*{{{*/
360 {
361 struct nvp_entry *ne;
362 for (ne = nvp->first; ne; ne=ne->next) {
363 if (ne->type == NVP_NAMEVALUE) {
364 if (!strcasecmp(ne->lhs, name)) {
365 return ne->rhs;
366 }
367 }
368 }
369 return NULL;
370 }
371 /*}}}*/
372
nvp_dump(struct nvp * nvp,FILE * out)373 void nvp_dump(struct nvp *nvp, FILE *out)/*{{{*/
374 {
375 struct nvp_entry *ne;
376 fprintf(out, "----\n");
377 for (ne = nvp->first; ne; ne=ne->next) {
378 switch (ne->type) {
379 case NVP_NAME:
380 fprintf(out, "NAME: %s\n", ne->lhs);
381 break;
382 case NVP_MAJORMINOR:
383 fprintf(out, "MAJORMINOR: %s/%s\n", ne->lhs, ne->rhs);
384 break;
385 case NVP_NAMEVALUE:
386 fprintf(out, "NAMEVALUE: %s=%s\n", ne->lhs, ne->rhs);
387 break;
388 }
389 }
390 }
391 /*}}}*/
392
393 /* In these cases, we only look at the first entry */
nvp_major(struct nvp * nvp)394 const char *nvp_major(struct nvp *nvp)/*{{{*/
395 {
396 struct nvp_entry *ne;
397 ne = nvp->first;
398 if (ne) {
399 if (ne->type == NVP_MAJORMINOR) {
400 return ne->lhs;
401 } else {
402 return NULL;
403 }
404 } else {
405 return NULL;
406 }
407 }
408 /*}}}*/
nvp_minor(struct nvp * nvp)409 const char *nvp_minor(struct nvp *nvp)/*{{{*/
410 {
411 struct nvp_entry *ne;
412 ne = nvp->first;
413 if (ne) {
414 if (ne->type == NVP_MAJORMINOR) {
415 return ne->rhs;
416 } else {
417 return NULL;
418 }
419 } else {
420 return NULL;
421 }
422 }
423 /*}}}*/
nvp_first(struct nvp * nvp)424 const char *nvp_first(struct nvp *nvp)/*{{{*/
425 {
426 struct nvp_entry *ne;
427 ne = nvp->first;
428 if (ne) {
429 if (ne->type == NVP_NAME) {
430 return ne->lhs;
431 } else {
432 return NULL;
433 }
434 } else {
435 return NULL;
436 }
437 }
438 /*}}}*/
439
440 #ifdef TEST
441
do_test(char * s)442 static void do_test(char *s)
443 {
444 struct nvp *n;
445 n = make_nvp(NULL, s, "");
446 if (n) {
447 nvp_dump(n, stderr);
448 free_nvp(n);
449 }
450 }
451
452
main(int argc,char ** argv)453 int main (int argc, char **argv) {
454 struct nvp *n;
455
456 if (argc > 1) {
457 while (*++argv)
458 do_test(*argv);
459 return 0;
460 }
461
462 #if 0
463 do_test("attachment; filename=\"foo.c\"; prot=ro");
464 do_test("attachment; filename= \"foo bar.c\" ;prot=ro");
465 do_test("attachment ; filename= \"foo bar.c\" ;prot= ro");
466 do_test("attachment ; filename= \"foo bar.c\" ;prot= ro");
467 do_test("attachment ; filename= \"foo ; bar.c\" ;prot= ro");
468 do_test("attachment ; x*0=\"hi \"; x*1=\"there\"");
469 #endif
470 do_test("attachment; filename*=utf-8''Section%204-1%20%E2%80%93%20Response%20Matrix%20PartIIA%2Edoc");
471 #if 0
472 do_test("application/vnd.ms-excel; name=\"thequiz.xls\"");
473 do_test("inline; filename*0=\"aaaa bbbb cccc dddd eeee ffff gggg hhhh iiii jjjj\t kkkkllll\"");
474 do_test(" text/plain ; name= \"foo bar.c\" ;prot= ro/rw; read/write; read= foo bar");
475 #endif
476 return 0;
477 }
478 #endif
479