masqmail-0.2

view src/parse.c @ 114:a80ebfa16cd5

better debugging output (thanks to Paolo)
author meillo@marmaro.de
date Wed, 30 Jun 2010 15:00:59 +0200
parents f671821d8222
children
line source
1 /* MasqMail
2 Copyright (C) 1999-2001 Oliver Kurth
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
19 #ifndef PARSE_TEST
20 #include "masqmail.h"
21 #endif
23 /* This is really dangerous. I hope that I was careful enough,
24 but maybe there is some malformed address possible that causes
25 this to segfault or be caught in endless loops.
27 If you find something like that, PLEASE mail the string to me
28 (no matter how idiotic it is), so that I can debug that.
29 Those things really should not happen.
30 */
32 static gchar *specials = "()<>@,;:\\\".[]`";
34 char *parse_error = NULL;
36 static gchar*
37 skip_comment(gchar * p)
38 {
40 #ifdef PARSE_TEST
41 g_print("skip_comment: %s\n", p);
42 #endif
44 p++;
45 while (*p && *p != ')') {
46 p++;
47 if (*p == '(')
48 p = skip_comment(p);
49 }
50 p++;
52 return p;
53 }
55 static gboolean
56 read_word(gchar * p, gchar ** b, gchar ** e)
57 {
58 #ifdef PARSE_TEST
59 g_print("read_word: %s\n", p);
60 #endif
61 /* eat leading spaces */
62 while (*p && isspace(*p))
63 p++;
65 *b = p;
66 /* b = &p; */
67 if (*p == '\"') {
68 /* quoted-string */
69 p++;
70 while (*p && (*p != '\"'))
71 p++;
72 p++;
73 } else {
74 /* atom */
75 while (*p && !strchr(specials, *p) && !iscntrl(*p) && !isspace(*p))
76 p++;
77 }
78 *e = p;
79 return TRUE;
80 }
82 static gboolean
83 read_word_with_dots(gchar * p, gchar ** b, gchar ** e)
84 {
85 gchar *b0 = p;
87 #ifdef PARSE_TEST
88 g_print("read_word_with_dots: %s\n", p);
89 #endif
90 while (TRUE) {
91 if (!read_word(p, b, e))
92 return FALSE;
93 p = *e;
94 if (*p != '.')
95 break;
96 p++;
97 }
98 *b = b0;
99 *e = p;
100 return TRUE;
101 }
103 static gboolean
104 read_domain(gchar * p, gchar ** b, gchar ** e)
105 {
106 #ifdef PARSE_TEST
107 g_print("read_domain: %s\n", p);
108 #endif
109 *b = p;
110 if (*p != '[') {
111 while (isalnum(*p) || (*p == '-') || (*p == '.'))
112 p++;
113 } else {
114 p++;
115 while (isalpha(*p) || (*p == '.'))
116 p++;
117 if (*p != ']') {
118 parse_error = g_strdup_printf("']' expected at end of literal address %s", *b);
119 return FALSE;
120 }
121 p++;
122 }
123 *e = p;
124 return TRUE;
125 }
127 gboolean
128 parse_address_rfc822(gchar* string, gchar** local_begin, gchar** local_end, gchar** domain_begin,
129 gchar** domain_end, gchar** address_end)
130 {
131 gint angle_brackets = 0;
133 gchar *p = string;
134 gchar *b, *e;
136 *local_begin = *local_end = NULL;
137 *domain_begin = *domain_end = NULL;
139 /* might be some memory left from previous call: */
140 if (parse_error != NULL) {
141 g_free(parse_error);
142 parse_error = NULL;
143 }
145 /* leading spaces and angle brackets */
146 while (*p && (isspace(*p) || (*p == '<'))) {
147 if (*p == '<')
148 angle_brackets++;
149 p++;
150 }
152 if (*p) {
153 while (TRUE) {
154 if (read_word_with_dots(p, &b, &e)) {
155 p = e;
156 #ifdef PARSE_TEST
157 g_print("after read_word_with_dots: %s\n", p);
158 #endif
159 /* eat white spaces and comments */
160 while ((*p && (isspace(*p))) || (*p == '(')) {
161 if (*p == '(') {
162 if (!(p = skip_comment(p))) {
163 parse_error = g_strdup("missing right bracket ')'");
164 return FALSE;
165 }
166 } else
167 p++;
168 }
169 /* we now have a non-space char that is not
170 the beginning of a comment */
172 if (*p == '@') {
173 /* the last word was the local_part
174 of an addr-spec */
175 *local_begin = b;
176 *local_end = e;
177 #ifdef PARSE_TEST
178 g_print("found local part: %s\n", *local_begin);
179 #endif
180 if (*p == '@') {
181 p++; /* skip @ */
182 /* now the domain */
183 if (read_domain(p, &b, &e)) {
184 p = e;
185 *domain_begin = b;
186 *domain_end = e;
187 } else
188 return FALSE;
189 } else {
190 /* unqualified? */
191 *domain_begin = *domain_end = NULL;
192 }
193 break;
194 } else if (*p == '<') {
195 /* addr-spec follows */
196 while (isspace(*p) || (*p == '<')) {
197 if (*p == '<')
198 angle_brackets++;
199 p++;
200 }
201 if (read_word_with_dots(p, &b, &e)) {
202 p = e;
203 *local_begin = b;
204 *local_end = e;
205 #ifdef PARSE_TEST
206 g_print("found local part: %s\n", *local_begin);
207 #endif
208 } else
209 return FALSE;
210 if (*p == '@') {
211 p++;
212 if (read_domain(p, &b, &e)) {
213 p = e;
214 *domain_begin = b;
215 *domain_end = e;
216 } else
217 return FALSE;
218 } else {
219 /* may be unqualified address */
220 *domain_begin = *domain_end = NULL;
221 }
222 break;
223 } else if (!*p || *p == '>') {
224 *local_begin = b;
225 *local_end = e;
226 #ifdef PARSE_TEST
227 g_print("found local part: %s\n", *local_begin);
228 #endif
229 *domain_begin = *domain_end = NULL;
230 break;
231 } else if (strchr(specials, *p) || iscntrl(*p) || isspace(*p)) {
232 parse_error = g_strdup_printf("unexpected character: %c", *p);
233 return FALSE;
234 }
235 } else
236 return FALSE;
237 }
238 /* trailing spaces and angle brackets */
239 #ifdef PARSE_TEST
240 g_print("down counting trailing '>'\n");
241 #endif
242 while (*p && (isspace(*p) || (*p == '>'))) {
243 if (*p == '>')
244 angle_brackets--;
245 p++;
246 }
248 *address_end = p;
250 if (angle_brackets != 0) {
251 if (angle_brackets > 0)
252 parse_error = g_strdup("missing '>' at end of string");
253 else
254 parse_error = g_strdup("superfluous '>' at end of string");
255 return FALSE;
256 } else {
257 /* we successfully parsed the address */
258 return TRUE;
259 }
260 /* we never get here */
261 }
262 return FALSE;
263 }
265 gboolean
266 parse_address_rfc821(gchar* string, gchar** local_begin, gchar** local_end, gchar** domain_begin,
267 gchar** domain_end, gchar** address_end)
268 {
269 gint angle_brackets = 0;
271 gchar *p = string;
272 gchar *b, *e;
274 *local_begin = *local_end = NULL;
275 *domain_begin = *domain_end = NULL;
277 /* might be some memory left from previous call: */
278 if (parse_error != NULL) {
279 g_free(parse_error);
280 parse_error = NULL;
281 }
283 /* leading spaces and angle brackets */
284 while (*p && (isspace(*p) || (*p == '<'))) {
285 if (*p == '<')
286 angle_brackets++;
287 p++;
288 }
290 if (*p) {
291 while (TRUE) {
292 if (read_word_with_dots(p, &b, &e)) {
293 p = e;
294 #ifdef PARSE_TEST
295 g_print("after read_word_with_dots: %s\n", p);
296 #endif
297 *local_begin = b;
298 *local_end = e;
299 #ifdef PARSE_TEST
300 g_print("found local part: %s\n", *local_begin);
301 g_print("local_end = %s\n", *local_end);
302 #endif
303 if (!(*p) || isspace(*p) || (*p == '>')) {
304 /* unqualified ? */
305 domain_begin = domain_end = NULL;
306 break;
307 } else if (*p == '@') {
308 p++;
309 if (read_domain(p, &b, &e)) {
310 p = e;
311 *domain_begin = b;
312 *domain_end = e;
313 }
314 break;
315 } else {
316 parse_error = g_strdup_printf ("unexpected character after local part '%c'", *p);
317 return FALSE;
318 }
319 } else
320 return FALSE;
321 }
323 /* trailing spaces and angle brackets */
324 #ifdef PARSE_TEST
325 g_print("down counting trailing '>'\n");
326 #endif
327 while (*p && (isspace(*p) || (*p == '>'))) {
328 if (*p == '>')
329 angle_brackets--;
330 p++;
331 }
332 *address_end = p;
334 if (angle_brackets != 0) {
335 if (angle_brackets > 0)
336 parse_error = g_strdup("missing '>' at end of string");
337 else
338 parse_error = g_strdup("superfluous '>' at end of string");
339 return FALSE;
340 } else {
341 /* we successfully parsed the address */
342 return TRUE;
343 }
344 /* we never get here */
345 }
346 return FALSE;
347 }
349 /*
350 allocate address, reading from string.
351 On failure, returns NULL.
352 after call, end contains a pointer to the end of the parsed string
353 end may be NULL, if we are not interested.
355 parses both rfc 821 and rfc 822 addresses, depending on flag is_rfc821
356 */
357 address*
358 _create_address(gchar * string, gchar ** end, gboolean is_rfc821)
359 {
360 gchar *loc_beg, *loc_end;
361 gchar *dom_beg, *dom_end;
362 gchar *addr_end;
364 if (string && (string[0] == 0)) {
365 address *addr = g_malloc(sizeof(address));
366 addr->address = g_strdup("");
367 addr->local_part = g_strdup("");
368 addr->domain = g_strdup(""); /* 'NULL' address (failure notice),
369 "" makes sure it will not be qualified with a hostname */
370 return addr;
371 }
373 if (is_rfc821
374 ? parse_address_rfc821(string, &loc_beg, &loc_end, &dom_beg, &dom_end, &addr_end)
375 : parse_address_rfc822(string, &loc_beg, &loc_end, &dom_beg, &dom_end, &addr_end))
376 {
377 address *addr = g_malloc(sizeof(address));
378 gchar *p = addr_end;
380 memset(addr, 0, sizeof(address));
382 if (loc_beg[0] == '|') {
383 parse_error = g_strdup("no pipe allowed for RFC 822/821 address");
384 return NULL;
385 }
387 while (*p && (*p != ','))
388 p++;
389 addr->address = g_strndup(string, p - string);
391 addr->local_part = g_strndup(loc_beg, loc_end - loc_beg);
393 #ifdef PARSE_TEST
394 g_print("addr->local_part = %s\n", addr->local_part);
395 #endif
397 if (dom_beg != NULL) {
398 addr->domain = g_strndup(dom_beg, dom_end - dom_beg);
399 } else {
400 if (addr->local_part[0] == 0)
401 addr->domain = g_strdup(""); /* 'NULL' address (failure notice),
402 "" makes sure it will not be qualified with a hostname */
403 else
404 addr->domain = NULL;
405 }
407 if (end != NULL)
408 *end = p;
410 #ifndef PARSE_TEST
411 addr_unmark_delivered(addr);
412 #endif
414 return addr;
415 }
416 return NULL;
417 }
419 address*
420 create_address_rfc822(gchar * string, gchar ** end)
421 {
422 return _create_address(string, end, FALSE);
423 }
425 address*
426 create_address_rfc821(gchar * string, gchar ** end)
427 {
428 return _create_address(string, end, TRUE);
429 }
431 GList*
432 addr_list_append_rfc822(GList * addr_list, gchar * string, gchar * domain)
433 {
434 gchar *p = string;
435 gchar *end;
437 while (*p) {
438 address *addr = _create_address(p, &end, FALSE);
439 if (addr) {
440 if (domain)
441 if (addr->domain == NULL)
442 addr->domain = g_strdup(domain);
444 addr_list = g_list_append(addr_list, addr);
445 p = end;
446 } else
447 break;
448 while (*p == ',' || isspace(*p))
449 p++;
450 }
451 return addr_list;
452 }