masqmail

view src/parse.c @ 271:899175e8dff0

heavy refactoring in the small of parse.c I really hope I didn't change any behavior. This reminds me that we really need a test framework.
author markus schnalke <meillo@marmaro.de>
date Fri, 03 Dec 2010 13:05:59 -0300
parents a80ebfa16cd5
children 00724782b6c9
line source
1 /* MasqMail
2 Copyright (C) 1999-2001 Oliver Kurth
3 Copyright (C) 2010 markus schnalke <meillo@marmaro.de>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
20 #ifndef PARSE_TEST
21 #include "masqmail.h"
22 #endif
24 /* This is really dangerous. I hope that I was careful enough,
25 but maybe there is some malformed address possible that causes
26 this to segfault or be caught in endless loops.
28 If you find something like that, PLEASE mail the string to me
29 (no matter how idiotic it is), so that I can debug that.
30 Those things really should not happen.
31 */
33 static gchar *specials = "()<>@,;:\\\".[]`";
35 char *parse_error = NULL;
37 static gchar*
38 skip_comment(gchar * p)
39 {
41 #ifdef PARSE_TEST
42 g_print("skip_comment: %s\n", p);
43 #endif
45 p++;
46 while (*p && *p != ')') {
47 p++;
48 if (*p == '(') {
49 p = skip_comment(p);
50 }
51 }
52 p++;
54 return p;
55 }
57 static gboolean
58 read_word(gchar * p, gchar ** b, gchar ** e)
59 {
60 #ifdef PARSE_TEST
61 g_print("read_word: %s\n", p);
62 #endif
63 /* eat leading spaces */
64 while (*p && isspace(*p)) {
65 p++;
66 }
68 *b = p;
69 /* b = &p; */
70 if (*p == '\"') {
71 /* quoted-string */
72 p++;
73 while (*p && (*p != '\"')) {
74 p++;
75 }
76 p++;
77 } else {
78 /* atom */
79 while (*p && !strchr(specials, *p) && !iscntrl(*p) && !isspace(*p)) {
80 p++;
81 }
82 }
83 *e = p;
84 return TRUE;
85 }
87 static gboolean
88 read_word_with_dots(gchar * p, gchar ** b, gchar ** e)
89 {
90 gchar *b0 = p;
92 #ifdef PARSE_TEST
93 g_print("read_word_with_dots: %s\n", p);
94 #endif
95 while (TRUE) {
96 if (!read_word(p, b, e)) {
97 return FALSE;
98 }
99 p = *e;
100 if (*p != '.') {
101 break;
102 }
103 p++;
104 }
105 *b = b0;
106 *e = p;
107 return TRUE;
108 }
110 static gboolean
111 read_domain(gchar * p, gchar ** b, gchar ** e)
112 {
113 #ifdef PARSE_TEST
114 g_print("read_domain: %s\n", p);
115 #endif
116 *b = p;
117 if (*p != '[') {
118 while (isalnum(*p) || (*p == '-') || (*p == '.')) {
119 p++;
120 }
121 } else {
122 p++;
123 while (isalpha(*p) || (*p == '.')) {
124 p++;
125 }
126 if (*p != ']') {
127 parse_error = g_strdup_printf("']' expected at end of literal address %s", *b);
128 return FALSE;
129 }
130 p++;
131 }
132 *e = p;
133 return TRUE;
134 }
136 gboolean
137 parse_address_rfc822(gchar* string, gchar** local_begin, gchar** local_end, gchar** domain_begin,
138 gchar** domain_end, gchar** address_end)
139 {
140 gint angle_brackets = 0;
142 gchar *p = string;
143 gchar *b, *e;
145 *local_begin = *local_end = NULL;
146 *domain_begin = *domain_end = NULL;
148 /* might be some memory left from previous call: */
149 if (parse_error != NULL) {
150 g_free(parse_error);
151 parse_error = NULL;
152 }
154 /* leading spaces and angle brackets */
155 while (*p && (isspace(*p) || (*p == '<'))) {
156 if (*p == '<') {
157 angle_brackets++;
158 }
159 p++;
160 }
162 if (!*p) {
163 return FALSE;
164 }
166 while (TRUE) {
167 if (!read_word_with_dots(p, &b, &e)) {
168 return FALSE;
169 }
171 p = e;
172 #ifdef PARSE_TEST
173 g_print("after read_word_with_dots: %s\n", p);
174 #endif
175 /* eat white spaces and comments */
176 while ((*p && (isspace(*p))) || (*p == '(')) {
177 if (*p == '(') {
178 if (!(p = skip_comment(p))) {
179 parse_error = g_strdup("missing right bracket ')'");
180 return FALSE;
181 }
182 } else {
183 p++;
184 }
185 }
186 /* we now have a non-space char that is not
187 the beginning of a comment */
189 if (*p == '@') {
190 /* the last word was the local_part of an addr-spec */
191 *local_begin = b;
192 *local_end = e;
193 #ifdef PARSE_TEST
194 g_print("found local part: %s\n", *local_begin);
195 #endif
196 if (*p == '@') {
197 p++; /* skip @ */
198 /* now the domain */
199 if (!read_domain(p, &b, &e)) {
200 return FALSE;
201 }
202 p = e;
203 *domain_begin = b;
204 *domain_end = e;
205 } else {
206 /* unqualified? */
207 *domain_begin = *domain_end = NULL;
208 }
209 break;
211 } else if (*p == '<') {
212 /* addr-spec follows */
213 while (isspace(*p) || (*p == '<')) {
214 if (*p == '<') {
215 angle_brackets++;
216 }
217 p++;
218 }
219 if (!read_word_with_dots(p, &b, &e)) {
220 return FALSE;
221 }
222 p = e;
223 *local_begin = b;
224 *local_end = e;
225 #ifdef PARSE_TEST
226 g_print("found local part: %s\n", *local_begin);
227 #endif
228 if (*p == '@') {
229 p++;
230 if (!read_domain(p, &b, &e)) {
231 return FALSE;
232 }
233 p = e;
234 *domain_begin = b;
235 *domain_end = e;
236 } else {
237 /* may be unqualified address */
238 *domain_begin = *domain_end = NULL;
239 }
240 break;
242 } else if (!*p || *p == '>') {
243 *local_begin = b;
244 *local_end = e;
245 #ifdef PARSE_TEST
246 g_print("found local part: %s\n", *local_begin);
247 #endif
248 *domain_begin = *domain_end = NULL;
249 break;
251 } else if (strchr(specials, *p) || iscntrl(*p) || isspace(*p)) {
252 parse_error = g_strdup_printf("unexpected character: %c", *p);
253 return FALSE;
254 }
255 }
257 /* trailing spaces and angle brackets */
258 #ifdef PARSE_TEST
259 g_print("down counting trailing '>'\n");
260 #endif
261 while (*p && (isspace(*p) || (*p == '>'))) {
262 if (*p == '>') {
263 angle_brackets--;
264 }
265 p++;
266 }
268 *address_end = p;
270 if (angle_brackets > 0) {
271 parse_error = g_strdup("missing '>' at end of string");
272 return FALSE;
273 } else if (angle_brackets < 0) {
274 parse_error = g_strdup("superfluous '>' at end of string");
275 return FALSE;
276 }
278 /* we successfully parsed the address */
279 return TRUE;
280 }
282 gboolean
283 parse_address_rfc821(gchar* string, gchar** local_begin, gchar** local_end, gchar** domain_begin,
284 gchar** domain_end, gchar** address_end)
285 {
286 gint angle_brackets = 0;
288 gchar *p = string;
289 gchar *b, *e;
291 *local_begin = *local_end = NULL;
292 *domain_begin = *domain_end = NULL;
294 /* might be some memory left from previous call: */
295 if (parse_error != NULL) {
296 g_free(parse_error);
297 parse_error = NULL;
298 }
300 /* leading spaces and angle brackets */
301 while (*p && (isspace(*p) || (*p == '<'))) {
302 if (*p == '<') {
303 angle_brackets++;
304 }
305 p++;
306 }
308 if (!*p) {
309 return FALSE;
310 }
312 while (TRUE) {
313 if (!read_word_with_dots(p, &b, &e)) {
314 return FALSE;
315 }
317 p = e;
318 #ifdef PARSE_TEST
319 g_print("after read_word_with_dots: %s\n", p);
320 #endif
321 *local_begin = b;
322 *local_end = e;
323 #ifdef PARSE_TEST
324 g_print("found local part: %s\n", *local_begin);
325 g_print("local_end = %s\n", *local_end);
326 #endif
327 if (!(*p) || isspace(*p) || (*p == '>')) {
328 /* unqualified ? */
329 domain_begin = domain_end = NULL;
330 break;
331 } else if (*p == '@') {
332 p++;
333 if (read_domain(p, &b, &e)) {
334 p = e;
335 *domain_begin = b;
336 *domain_end = e;
337 }
338 break;
339 } else {
340 parse_error = g_strdup_printf ("unexpected character after local part '%c'", *p);
341 return FALSE;
342 }
343 }
345 /* trailing spaces and angle brackets */
346 #ifdef PARSE_TEST
347 g_print("down counting trailing '>'\n");
348 #endif
349 while (*p && (isspace(*p) || (*p == '>'))) {
350 if (*p == '>') {
351 angle_brackets--;
352 }
353 p++;
354 }
355 *address_end = p;
357 if (angle_brackets > 0) {
358 parse_error = g_strdup("missing '>' at end of string");
359 return FALSE;
360 } else if (angle_brackets < 0) {
361 parse_error = g_strdup("superfluous '>' at end of string");
362 return FALSE;
363 }
365 /* we successfully parsed the address */
366 return TRUE;
367 }
369 /*
370 allocate address, reading from string.
371 On failure, returns NULL.
372 after call, end contains a pointer to the end of the parsed string
373 end may be NULL, if we are not interested.
375 parses both rfc 821 and rfc 822 addresses, depending on flag is_rfc821
376 */
377 address*
378 _create_address(gchar * string, gchar ** end, gboolean is_rfc821)
379 {
380 gchar *loc_beg, *loc_end;
381 gchar *dom_beg, *dom_end;
382 gchar *addr_end;
383 gboolean ret;
385 if (string && (string[0] == '\0')) {
386 address *addr = g_malloc(sizeof(address));
387 addr->address = g_strdup("");
388 addr->local_part = g_strdup("");
389 /* 'NULL' address (failure notice),
390 "" makes sure it will not be qualified with a hostname */
391 addr->domain = g_strdup("");
392 return addr;
393 }
395 if (is_rfc821) {
396 ret = parse_address_rfc821(string, &loc_beg, &loc_end, &dom_beg, &dom_end, &addr_end);
397 } else {
398 ret = parse_address_rfc822(string, &loc_beg, &loc_end, &dom_beg, &dom_end, &addr_end);
399 }
400 if (!ret) {
401 return NULL;
402 }
404 address *addr = g_malloc(sizeof(address));
405 gchar *p = addr_end;
407 memset(addr, 0, sizeof(address));
409 if (loc_beg[0] == '|') {
410 parse_error = g_strdup("no pipe allowed for RFC 822/821 address");
411 return NULL;
412 }
414 while (*p && (*p != ',')) {
415 p++;
416 }
417 addr->address = g_strndup(string, p - string);
418 addr->local_part = g_strndup(loc_beg, loc_end - loc_beg);
420 #ifdef PARSE_TEST
421 g_print("addr->local_part = %s\n", addr->local_part);
422 #endif
424 if (dom_beg != NULL) {
425 addr->domain = g_strndup(dom_beg, dom_end - dom_beg);
426 } else if (addr->local_part[0] == 0) {
427 /* 'NULL' address (failure notice),
428 "" makes sure it will not be qualified with a hostname */
429 addr->domain = g_strdup("");
430 } else {
431 addr->domain = NULL;
432 }
434 if (end != NULL) {
435 *end = p;
436 }
438 #ifndef PARSE_TEST
439 addr_unmark_delivered(addr);
440 #endif
442 return addr;
443 }
445 address*
446 create_address_rfc822(gchar * string, gchar ** end)
447 {
448 return _create_address(string, end, FALSE);
449 }
451 address*
452 create_address_rfc821(gchar * string, gchar ** end)
453 {
454 return _create_address(string, end, TRUE);
455 }
457 GList*
458 addr_list_append_rfc822(GList * addr_list, gchar * string, gchar * domain)
459 {
460 gchar *p = string;
461 gchar *end;
463 while (*p) {
464 address *addr = _create_address(p, &end, FALSE);
465 fprintf(stderr, "string: %s\n", p);
467 if (!addr) {
468 break;
469 }
471 fprintf(stderr, " addr: %s (%s<@>%s)\n", addr->address, addr->local_part, addr->domain);
472 if (domain && !addr->domain) {
473 addr->domain = g_strdup(domain);
474 }
475 fprintf(stderr, " %s (%s<@>%s)\n", addr->address, addr->local_part, addr->domain);
477 addr_list = g_list_append(addr_list, addr);
478 p = end;
480 while (*p == ',' || isspace(*p)) {
481 p++;
482 }
483 }
484 return addr_list;
485 }