masqmail

view src/parse.c @ 11:24872a9fe6e1

merged
author meillo@marmaro.de
date Mon, 27 Oct 2008 16:23:31 +0100
parents 08114f7dcc23
children f671821d8222
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, gchar** domain_end, gchar** address_end)
129 {
130 gint angle_brackets = 0;
132 gchar *p = string;
133 gchar *b, *e;
135 *local_begin = *local_end = NULL;
136 *domain_begin = *domain_end = NULL;
138 /* might be some memory left from previous call: */
139 if (parse_error != NULL) {
140 g_free(parse_error);
141 parse_error = NULL;
142 }
144 /* leading spaces and angle brackets */
145 while (*p && (isspace(*p) || (*p == '<'))) {
146 if (*p == '<')
147 angle_brackets++;
148 p++;
149 }
151 if (*p) {
152 while (TRUE) {
153 if (read_word_with_dots(p, &b, &e)) {
154 p = e;
155 #ifdef PARSE_TEST
156 g_print("after read_word_with_dots: %s\n", p);
157 #endif
158 /* eat white spaces and comments */
159 while ((*p && (isspace(*p))) || (*p == '(')) {
160 if (*p == '(') {
161 if (!(p = skip_comment(p))) {
162 parse_error = g_strdup("missing right bracket ')'");
163 return FALSE;
164 }
165 } else
166 p++;
167 }
168 /* we now have a non-space char that is not
169 the beginning of a comment */
171 if (*p == '@') {
172 /* the last word was the local_part
173 of an addr-spec */
174 *local_begin = b;
175 *local_end = e;
176 #ifdef PARSE_TEST
177 g_print("found local part: %s\n", *local_begin);
178 #endif
179 if (*p == '@') {
180 p++; /* skip @ */
181 /* now the domain */
182 if (read_domain(p, &b, &e)) {
183 p = e;
184 *domain_begin = b;
185 *domain_end = e;
186 } else
187 return FALSE;
188 } else {
189 /* unqualified? */
190 *domain_begin = *domain_end = NULL;
191 }
192 break;
193 } else if (*p == '<') {
194 /* addr-spec follows */
195 while (isspace(*p) || (*p == '<')) {
196 if (*p == '<')
197 angle_brackets++;
198 p++;
199 }
200 if (read_word_with_dots(p, &b, &e)) {
201 p = e;
202 *local_begin = b;
203 *local_end = e;
204 #ifdef PARSE_TEST
205 g_print("found local part: %s\n", *local_begin);
206 #endif
207 } else
208 return FALSE;
209 if (*p == '@') {
210 p++;
211 if (read_domain(p, &b, &e)) {
212 p = e;
213 *domain_begin = b;
214 *domain_end = e;
215 } else
216 return FALSE;
217 } else {
218 /* may be unqualified address */
219 *domain_begin = *domain_end = NULL;
220 }
221 break;
222 } else if (!*p || *p == '>') {
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 *domain_begin = *domain_end = NULL;
229 break;
230 } else if (strchr(specials, *p) || iscntrl(*p) || isspace(*p)) {
231 parse_error = g_strdup_printf("unexpected character: %c", *p);
232 return FALSE;
233 }
234 } else
235 return FALSE;
236 }
237 /* trailing spaces and angle brackets */
238 #ifdef PARSE_TEST
239 g_print("down counting trailing '>'\n");
240 #endif
241 while (*p && (isspace(*p) || (*p == '>'))) {
242 if (*p == '>')
243 angle_brackets--;
244 p++;
245 }
247 *address_end = p;
249 if (angle_brackets != 0) {
250 if (angle_brackets > 0)
251 parse_error = g_strdup("missing '>' at end of string");
252 else
253 parse_error = g_strdup("superfluous '>' at end of string");
254 return FALSE;
255 } else {
256 /* we successfully parsed the address */
257 return TRUE;
258 }
259 /* we never get here */
260 }
261 return FALSE;
262 }
264 gboolean
265 parse_address_rfc821(gchar* string, gchar** local_begin, gchar** local_end, gchar** domain_begin, gchar** domain_end, gchar** address_end)
266 {
267 gint angle_brackets = 0;
269 gchar *p = string;
270 gchar *b, *e;
272 *local_begin = *local_end = NULL;
273 *domain_begin = *domain_end = NULL;
275 /* might be some memory left from previous call: */
276 if (parse_error != NULL) {
277 g_free(parse_error);
278 parse_error = NULL;
279 }
281 /* leading spaces and angle brackets */
282 while (*p && (isspace(*p) || (*p == '<'))) {
283 if (*p == '<')
284 angle_brackets++;
285 p++;
286 }
288 if (*p) {
289 while (TRUE) {
290 if (read_word_with_dots(p, &b, &e)) {
291 p = e;
292 #ifdef PARSE_TEST
293 g_print("after read_word_with_dots: %s\n", p);
294 #endif
295 *local_begin = b;
296 *local_end = e;
297 #ifdef PARSE_TEST
298 g_print("found local part: %s\n", *local_begin);
299 g_print("local_end = %s\n", *local_end);
300 #endif
301 if (!(*p) || isspace(*p) || (*p == '>')) {
302 /* unqualified ? */
303 domain_begin = domain_end = NULL;
304 break;
305 } else if (*p == '@') {
306 p++;
307 if (read_domain(p, &b, &e)) {
308 p = e;
309 *domain_begin = b;
310 *domain_end = e;
311 }
312 break;
313 } else {
314 parse_error = g_strdup_printf ("unexpected character after local part '%c'", *p);
315 return FALSE;
316 }
317 } else
318 return FALSE;
319 }
321 /* trailing spaces and angle brackets */
322 #ifdef PARSE_TEST
323 g_print("down counting trailing '>'\n");
324 #endif
325 while (*p && (isspace(*p) || (*p == '>'))) {
326 if (*p == '>')
327 angle_brackets--;
328 p++;
329 }
330 *address_end = p;
332 if (angle_brackets != 0) {
333 if (angle_brackets > 0)
334 parse_error = g_strdup("missing '>' at end of string");
335 else
336 parse_error = g_strdup("superfluous '>' at end of string");
337 return FALSE;
338 } else {
339 /* we successfully parsed the address */
340 return TRUE;
341 }
342 /* we never get here */
343 }
344 return FALSE;
345 }
347 /*
348 allocate address, reading from string.
349 On failure, returns NULL.
350 after call, end contatins a pointer to the end of the parsed string
351 end may be NULL, if we are not interested.
353 parses both rfc 821 and rfc 822 addresses, depending on flag is_rfc821
354 */
356 address*
357 _create_address(gchar * string, gchar ** end, gboolean is_rfc821)
358 {
359 gchar *loc_beg, *loc_end;
360 gchar *dom_beg, *dom_end;
361 gchar *addr_end;
363 if (string && (string[0] == 0)) {
364 address *addr = g_malloc(sizeof(address));
365 addr->address = g_strdup("");
366 addr->local_part = g_strdup("");
367 addr->domain = g_strdup(""); /* 'NULL' address (failure notice), "" makes sure it will not be qualified with a hostname */
368 return addr;
369 }
371 if (is_rfc821
372 ? parse_address_rfc821(string, &loc_beg, &loc_end, &dom_beg, &dom_end, &addr_end)
373 : parse_address_rfc822(string, &loc_beg, &loc_end, &dom_beg, &dom_end, &addr_end))
374 {
375 address *addr = g_malloc(sizeof(address));
376 gchar *p = addr_end;
379 memset(addr, 0, sizeof(address));
381 if (loc_beg[0] == '|') {
382 parse_error = g_strdup("no pipe allowed for RFC 822/821 address");
383 return NULL;
384 }
386 while (*p && (*p != ','))
387 p++;
388 addr->address = g_strndup(string, p - string);
390 addr->local_part = g_strndup(loc_beg, loc_end - loc_beg);
392 #ifdef PARSE_TEST
393 g_print("addr->local_part = %s\n", addr->local_part);
394 #endif
396 if (dom_beg != NULL) {
397 addr->domain = g_strndup(dom_beg, dom_end - dom_beg);
398 } else {
399 if (addr->local_part[0] == 0)
400 addr->domain = g_strdup(""); /* 'NULL' address (failure notice), "" makes sure it will not be qualified with a hostname */
401 else
402 addr->domain = NULL;
403 }
405 if (end != NULL)
406 *end = p;
408 #ifndef PARSE_TEST
409 addr_unmark_delivered(addr);
410 #endif
412 return addr;
413 }
414 return NULL;
415 }
417 address*
418 create_address_rfc822(gchar * string, gchar ** end)
419 {
420 return _create_address(string, end, FALSE);
421 }
423 address*
424 create_address_rfc821(gchar * string, gchar ** end)
425 {
426 return _create_address(string, end, TRUE);
427 }
429 GList*
430 addr_list_append_rfc822(GList * addr_list, gchar * string, gchar * domain)
431 {
432 gchar *p = string;
433 gchar *end;
435 while (*p) {
436 address *addr = _create_address(p, &end, FALSE);
437 if (addr) {
438 if (domain)
439 if (addr->domain == NULL)
440 addr->domain = g_strdup(domain);
442 addr_list = g_list_append(addr_list, addr);
443 p = end;
444 } else
445 break;
446 while (*p == ',' || isspace(*p))
447 p++;
448 }
449 return addr_list;
450 }