comparison src/parse.c @ 0:08114f7dcc23 0.2.21

this is masqmail-0.2.21 from oliver kurth
author meillo@marmaro.de
date Fri, 26 Sep 2008 17:05:23 +0200
parents
children 26e34ae9a3e3
comparison
equal deleted inserted replaced
-1:000000000000 0:08114f7dcc23
1 /* MasqMail
2 Copyright (C) 1999-2001 Oliver Kurth
3
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.
8
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.
13
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 */
18
19 #ifndef PARSE_TEST
20 #include "masqmail.h"
21 #endif
22
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.
26
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 */
31
32 static gchar *specials = "()<>@,;:\\\".[]`";
33
34 char *parse_error = NULL;
35
36 static
37 gchar *skip_comment(gchar *p)
38 {
39
40 #ifdef PARSE_TEST
41 g_print("skip_comment: %s\n", p);
42 #endif
43
44 p++;
45 while(*p && *p != ')'){
46 p++;
47 if(*p == '(')
48 p = skip_comment(p);
49 }
50 p++;
51
52 return p;
53 }
54
55 static
56 gboolean 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)) p++;
63
64 *b = p;
65 /* b = &p;*/
66 if(*p == '\"'){
67 /* quoted-string */
68 p++;
69 while(*p && (*p != '\"')) p++;
70 p++;
71 }else{
72 /* atom */
73 while(*p && !strchr(specials, *p) && !iscntrl(*p) && !isspace(*p))
74 p++;
75 }
76 *e = p;
77 return TRUE;
78 }
79
80 static
81 gboolean read_word_with_dots(gchar *p, gchar **b, gchar **e)
82 {
83 gchar *b0 = p;
84
85 #ifdef PARSE_TEST
86 g_print("read_word_with_dots: %s\n", p);
87 #endif
88 while(TRUE){
89 if(!read_word(p, b, e))
90 return FALSE;
91 p = *e;
92 if(*p != '.') break;
93 p++;
94 }
95 *b = b0;
96 *e = p;
97 return TRUE;
98 }
99
100 static
101 gboolean read_domain(gchar *p, gchar **b, gchar **e)
102 {
103 #ifdef PARSE_TEST
104 g_print("read_domain: %s\n", p);
105 #endif
106 *b = p;
107 if(*p != '['){
108 while(isalnum(*p) || (*p == '-') || (*p == '.'))
109 p++;
110 }else{
111 p++;
112 while(isalpha(*p) || (*p == '.'))
113 p++;
114 if(*p != ']'){
115 parse_error =
116 g_strdup_printf("']' expected at end of literal address %s", *b);
117 return FALSE;
118 }
119 p++;
120 }
121 *e = p;
122 return TRUE;
123 }
124
125 gboolean parse_address_rfc822(gchar *string,
126 gchar **local_begin, gchar **local_end,
127 gchar **domain_begin, gchar **domain_end,
128 gchar **address_end)
129 {
130 gint angle_brackets = 0;
131
132 gchar *p = string;
133 gchar *b, *e;
134
135 *local_begin = *local_end = NULL;
136 *domain_begin = *domain_end = NULL;
137
138 /* might be some memory left from previous call: */
139 if(parse_error != NULL){
140 g_free(parse_error);
141 parse_error = NULL;
142 }
143
144 /* leading spaces and angle brackets */
145 while(*p && (isspace(*p) || (*p == '<'))){
146 if(*p == '<')
147 angle_brackets++;
148 p++;
149 }
150
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 =
163 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 */
171
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 }
188 else
189 return FALSE;
190 }else{
191 /* unqualified? */
192 *domain_begin = *domain_end = NULL;
193 }
194 break;
195 }else if(*p == '<'){
196 /* addr-spec follows */
197 while(isspace(*p) || (*p == '<')){
198 if(*p == '<')
199 angle_brackets++;
200 p++;
201 }
202 if(read_word_with_dots(p, &b, &e)){
203 p = e;
204 *local_begin = b;
205 *local_end = e;
206 #ifdef PARSE_TEST
207 g_print("found local part: %s\n", *local_begin);
208 #endif
209 }else
210 return FALSE;
211 if(*p == '@'){
212 p++;
213 if(read_domain(p, &b, &e)){
214 p = e;
215 *domain_begin = b;
216 *domain_end = e;
217 }else
218 return FALSE;
219 }else{
220 /* may be unqualified address */
221 *domain_begin = *domain_end = NULL;
222 }
223 break;
224 }else if(!*p || *p == '>'){
225 *local_begin = b;
226 *local_end = e;
227 #ifdef PARSE_TEST
228 g_print("found local part: %s\n", *local_begin);
229 #endif
230 *domain_begin = *domain_end = NULL;
231 break;
232 }else if(strchr(specials, *p) || iscntrl(*p) || isspace(*p)){
233 parse_error = g_strdup_printf("unexpected character: %c", *p);
234 return FALSE;
235 }
236 }else
237 return FALSE;
238 }
239 /* trailing spaces and angle brackets */
240 #ifdef PARSE_TEST
241 g_print("down counting trailing '>'\n");
242 #endif
243 while(*p && (isspace(*p) || (*p == '>'))){
244 if(*p == '>')
245 angle_brackets--;
246 p++;
247 }
248
249 *address_end = p;
250
251 if(angle_brackets != 0){
252 if(angle_brackets > 0)
253 parse_error = g_strdup("missing '>' at end of string");
254 else
255 parse_error = g_strdup("superfluous '>' at end of string");
256 return FALSE;
257 }else{
258 /* we successfully parsed the address */
259 return TRUE;
260 }
261 /* we never get here */
262 }
263 return FALSE;
264 }
265
266 gboolean parse_address_rfc821(gchar *string,
267 gchar **local_begin, gchar **local_end,
268 gchar **domain_begin, gchar **domain_end,
269 gchar **address_end)
270 {
271 gint angle_brackets = 0;
272
273 gchar *p = string;
274 gchar *b, *e;
275
276 *local_begin = *local_end = NULL;
277 *domain_begin = *domain_end = NULL;
278
279 /* might be some memory left from previous call: */
280 if(parse_error != NULL){
281 g_free(parse_error);
282 parse_error = NULL;
283 }
284
285 /* leading spaces and angle brackets */
286 while(*p && (isspace(*p) || (*p == '<'))){
287 if(*p == '<')
288 angle_brackets++;
289 p++;
290 }
291
292 if(*p){
293 while(TRUE){
294 if(read_word_with_dots(p, &b, &e)){
295 p = e;
296 #ifdef PARSE_TEST
297 g_print("after read_word_with_dots: %s\n", p);
298 #endif
299 *local_begin = b;
300 *local_end = e;
301 #ifdef PARSE_TEST
302 g_print("found local part: %s\n", *local_begin);
303 g_print("local_end = %s\n", *local_end);
304 #endif
305 if(!(*p) || isspace(*p) || (*p == '>')){
306 /* unqualified ?*/
307 domain_begin = domain_end = NULL;
308 break;
309 }else if(*p == '@'){
310 p++;
311 if(read_domain(p, &b, &e)){
312 p = e;
313 *domain_begin = b;
314 *domain_end = e;
315 }
316 break;
317 }else{
318 parse_error =
319 g_strdup_printf("unexpected character after local part '%c'",*p);
320 return FALSE;
321 }
322 } else
323 return FALSE;
324 }
325
326 /* trailing spaces and angle brackets */
327 #ifdef PARSE_TEST
328 g_print("down counting trailing '>'\n");
329 #endif
330 while(*p && (isspace(*p) || (*p == '>'))){
331 if(*p == '>')
332 angle_brackets--;
333 p++;
334 }
335 *address_end = p;
336
337 if(angle_brackets != 0){
338 if(angle_brackets > 0)
339 parse_error = g_strdup("missing '>' at end of string");
340 else
341 parse_error = g_strdup("superfluous '>' at end of string");
342 return FALSE;
343 }else{
344 /* we successfully parsed the address */
345 return TRUE;
346 }
347 /* we never get here */
348 }
349 return FALSE;
350 }
351
352 /*
353 allocate address, reading from string.
354 On failure, returns NULL.
355 after call, end contatins a pointer to the end of the parsed string
356 end may be NULL, if we are not interested.
357
358 parses both rfc 821 and rfc 822 addresses, depending on flag is_rfc821
359 */
360
361 address *_create_address(gchar *string, gchar **end, gboolean is_rfc821)
362 {
363 gchar *loc_beg, *loc_end;
364 gchar *dom_beg, *dom_end;
365 gchar *addr_end;
366
367 if (string && (string[0] == 0)) {
368 address *addr = g_malloc(sizeof(address));
369 addr->address = g_strdup("");
370 addr->local_part = g_strdup("");
371 addr->domain = g_strdup(""); /* 'NULL' address (failure notice),
372 "" makes sure it will not be qualified with a hostname */
373 return addr;
374 }
375
376 if(is_rfc821 ?
377 parse_address_rfc821(string,
378 &loc_beg, &loc_end, &dom_beg, &dom_end, &addr_end) :
379 parse_address_rfc822(string,
380 &loc_beg, &loc_end, &dom_beg, &dom_end, &addr_end)){
381 address *addr = g_malloc(sizeof(address));
382 gchar *p = addr_end;
383
384
385 memset(addr, 0, sizeof(address));
386
387 if(loc_beg[0] == '|'){
388 parse_error = g_strdup("no pipe allowed for RFC 822/821 address");
389 return NULL;
390 }
391
392 while(*p && (*p != ',')) p++;
393 addr->address = g_strndup(string, p - string);
394
395 addr->local_part = g_strndup(loc_beg, loc_end - loc_beg);
396
397 #ifdef PARSE_TEST
398 g_print("addr->local_part = %s\n", addr->local_part);
399 #endif
400
401 if(dom_beg != NULL){
402 addr->domain = g_strndup(dom_beg, dom_end - dom_beg);
403 }else{
404 if(addr->local_part[0] == 0)
405 addr->domain = g_strdup(""); /* 'NULL' address (failure notice),
406 "" makes sure it will not be qualified with a hostname */
407 else
408 addr->domain = NULL;
409 }
410
411 if(end != NULL)
412 *end = p;
413
414 #ifndef PARSE_TEST
415 addr_unmark_delivered(addr);
416 #endif
417
418 return addr;
419 }
420 return NULL;
421 }
422
423 address *create_address_rfc822(gchar *string, gchar **end){
424 return _create_address(string, end, FALSE);
425 }
426
427 address *create_address_rfc821(gchar *string, gchar **end){
428 return _create_address(string, end, TRUE);
429 }
430
431 GList *addr_list_append_rfc822(GList *addr_list, gchar *string, gchar *domain)
432 {
433 gchar *p = string;
434 gchar *end;
435
436 while(*p){
437 address *addr = _create_address(p, &end, FALSE);
438 if(addr){
439 if(domain)
440 if(addr->domain == NULL)
441 addr->domain = g_strdup(domain);
442
443 addr_list = g_list_append(addr_list, addr);
444 p = end;
445 }else
446 break;
447 while(*p == ',' || isspace(*p)) p++;
448 }
449 return addr_list;
450 }