masqmail

view src/parse.c @ 273:00724782b6c9

parse.c: comments, better debugging, tiny refactoring
author markus schnalke <meillo@marmaro.de>
date Fri, 03 Dec 2010 19:25:43 -0300
parents 899175e8dff0
children 89199eda6144
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) {
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 #ifdef PARSE_TEST
254 g_print("unexpected character: %c", *p);
255 #endif
256 return FALSE;
257 }
258 }
260 /* trailing spaces and angle brackets */
261 #ifdef PARSE_TEST
262 g_print("down counting trailing '>'\n");
263 #endif
264 while (*p && (isspace(*p) || (*p == '>'))) {
265 if (*p == '>') {
266 angle_brackets--;
267 }
268 p++;
269 }
271 *address_end = p;
273 if (angle_brackets > 0) {
274 parse_error = g_strdup("missing '>' at end of string");
275 return FALSE;
276 } else if (angle_brackets < 0) {
277 parse_error = g_strdup("superfluous '>' at end of string");
278 return FALSE;
279 }
281 /* we successfully parsed the address */
282 return TRUE;
283 }
285 gboolean
286 parse_address_rfc821(gchar* string, gchar** local_begin, gchar** local_end, gchar** domain_begin,
287 gchar** domain_end, gchar** address_end)
288 {
289 gint angle_brackets = 0;
291 gchar *p = string;
292 gchar *b, *e;
294 *local_begin = *local_end = NULL;
295 *domain_begin = *domain_end = NULL;
297 /* might be some memory left from previous call: */
298 if (parse_error != NULL) {
299 g_free(parse_error);
300 parse_error = NULL;
301 }
303 /* leading spaces and angle brackets */
304 while (*p && (isspace(*p) || (*p == '<'))) {
305 if (*p == '<') {
306 angle_brackets++;
307 }
308 p++;
309 }
311 if (!*p) {
312 return FALSE;
313 }
315 while (TRUE) {
316 if (!read_word_with_dots(p, &b, &e)) {
317 return FALSE;
318 }
320 p = e;
321 #ifdef PARSE_TEST
322 g_print("after read_word_with_dots: %s\n", p);
323 #endif
324 *local_begin = b;
325 *local_end = e;
326 #ifdef PARSE_TEST
327 g_print("found local part: %s\n", *local_begin);
328 g_print("local_end = %s\n", *local_end);
329 #endif
330 if (!(*p) || isspace(*p) || (*p == '>')) {
331 /* unqualified ? */
332 domain_begin = domain_end = NULL;
333 break;
334 } else if (*p == '@') {
335 p++;
336 if (read_domain(p, &b, &e)) {
337 p = e;
338 *domain_begin = b;
339 *domain_end = e;
340 }
341 break;
342 } else {
343 parse_error = g_strdup_printf ("unexpected character after local part '%c'", *p);
344 return FALSE;
345 }
346 }
348 /* trailing spaces and angle brackets */
349 #ifdef PARSE_TEST
350 g_print("down counting trailing '>'\n");
351 #endif
352 while (*p && (isspace(*p) || (*p == '>'))) {
353 if (*p == '>') {
354 angle_brackets--;
355 }
356 p++;
357 }
358 *address_end = p;
360 if (angle_brackets > 0) {
361 parse_error = g_strdup("missing '>' at end of string");
362 return FALSE;
363 } else if (angle_brackets < 0) {
364 parse_error = g_strdup("superfluous '>' at end of string");
365 return FALSE;
366 }
368 /* we successfully parsed the address */
369 return TRUE;
370 }
372 /*
373 allocate address, reading from string.
374 On failure, returns NULL.
375 after call, end contains a pointer to the end of the parsed string
376 end may be NULL, if we are not interested.
378 parses both rfc 821 and rfc 822 addresses, depending on flag is_rfc821
379 */
380 address*
381 _create_address(gchar * string, gchar ** end, gboolean is_rfc821)
382 {
383 gchar *loc_beg, *loc_end;
384 gchar *dom_beg, *dom_end;
385 gchar *addr_end;
386 gboolean ret;
388 /* TODO: what about (string == NULL)? */
389 if (string && (string[0] == '\0')) {
390 address *addr = g_malloc(sizeof(address));
391 addr->address = g_strdup("");
392 addr->local_part = g_strdup("");
393 /* 'NULL' address (failure notice),
394 "" makes sure it will not be qualified with a hostname */
395 addr->domain = g_strdup("");
396 return addr;
397 }
399 if (is_rfc821) {
400 ret = parse_address_rfc821(string, &loc_beg, &loc_end, &dom_beg, &dom_end, &addr_end);
401 } else {
402 ret = parse_address_rfc822(string, &loc_beg, &loc_end, &dom_beg, &dom_end, &addr_end);
403 }
404 if (!ret) {
405 return NULL;
406 }
408 address *addr = g_malloc(sizeof(address));
409 gchar *p = addr_end;
411 memset(addr, 0, sizeof(address));
413 if (loc_beg[0] == '|') {
414 parse_error = g_strdup("no pipe allowed for RFC 822/821 address");
415 return NULL;
416 }
418 while (*p && (*p != ',')) {
419 p++;
420 }
421 addr->address = g_strndup(string, p - string);
422 addr->local_part = g_strndup(loc_beg, loc_end - loc_beg);
424 #ifdef PARSE_TEST
425 g_print("addr->local_part = %s\n", addr->local_part);
426 #endif
428 if (dom_beg != NULL) {
429 addr->domain = g_strndup(dom_beg, dom_end - dom_beg);
430 } else if (addr->local_part[0] == '\0') {
431 /* 'NULL' address (failure notice),
432 "" makes sure it will not be qualified with a hostname */
433 addr->domain = g_strdup("");
434 } else {
435 addr->domain = NULL;
436 }
438 if (end) {
439 *end = p;
440 }
442 #ifndef PARSE_TEST
443 addr_unmark_delivered(addr);
444 #endif
446 return addr;
447 }
449 address*
450 create_address_rfc822(gchar * string, gchar ** end)
451 {
452 return _create_address(string, end, FALSE);
453 }
455 address*
456 create_address_rfc821(gchar * string, gchar ** end)
457 {
458 return _create_address(string, end, TRUE);
459 }
461 GList*
462 addr_list_append_rfc822(GList * addr_list, gchar * string, gchar * domain)
463 {
464 gchar *p = string;
465 gchar *end;
467 while (*p) {
468 #ifdef PARSE_TEST
469 g_print("string: %s\n", p);
470 #endif
472 address *addr = _create_address(p, &end, FALSE);
473 if (!addr) {
474 break;
475 }
477 #ifdef PARSE_TEST
478 g_print("addr: %s (%s<@>%s)", addr->address, addr->local_part, addr->domain);
479 #endif
480 if (domain && !addr->domain) {
481 addr->domain = g_strdup(domain);
482 }
483 #ifdef PARSE_TEST
484 g_print(" (%s<@>%s)\n", addr->local_part, addr->domain);
485 #endif
487 addr_list = g_list_append(addr_list, addr);
488 p = end;
490 while (*p == ',' || isspace(*p)) {
491 p++;
492 }
493 }
494 return addr_list;
495 }