masqmail-0.2
view src/parse.c @ 3:8c55886cacd8
man pages will be maintained in troff now
author | meillo@marmaro.de |
---|---|
date | Fri, 26 Sep 2008 21:40:10 +0200 |
parents | |
children | 26e34ae9a3e3 |
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
37 gchar *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
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++;
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 }
80 static
81 gboolean read_word_with_dots(gchar *p, gchar **b, gchar **e)
82 {
83 gchar *b0 = p;
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 }
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 }
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;
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 =
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 */
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 }
249 *address_end = p;
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 }
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;
273 gchar *p = string;
274 gchar *b, *e;
276 *local_begin = *local_end = NULL;
277 *domain_begin = *domain_end = NULL;
279 /* might be some memory left from previous call: */
280 if(parse_error != NULL){
281 g_free(parse_error);
282 parse_error = NULL;
283 }
285 /* leading spaces and angle brackets */
286 while(*p && (isspace(*p) || (*p == '<'))){
287 if(*p == '<')
288 angle_brackets++;
289 p++;
290 }
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 }
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;
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 }
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.
358 parses both rfc 821 and rfc 822 addresses, depending on flag is_rfc821
359 */
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;
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 }
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;
385 memset(addr, 0, sizeof(address));
387 if(loc_beg[0] == '|'){
388 parse_error = g_strdup("no pipe allowed for RFC 822/821 address");
389 return NULL;
390 }
392 while(*p && (*p != ',')) p++;
393 addr->address = g_strndup(string, p - string);
395 addr->local_part = g_strndup(loc_beg, loc_end - loc_beg);
397 #ifdef PARSE_TEST
398 g_print("addr->local_part = %s\n", addr->local_part);
399 #endif
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 }
411 if(end != NULL)
412 *end = p;
414 #ifndef PARSE_TEST
415 addr_unmark_delivered(addr);
416 #endif
418 return addr;
419 }
420 return NULL;
421 }
423 address *create_address_rfc822(gchar *string, gchar **end){
424 return _create_address(string, end, FALSE);
425 }
427 address *create_address_rfc821(gchar *string, gchar **end){
428 return _create_address(string, end, TRUE);
429 }
431 GList *addr_list_append_rfc822(GList *addr_list, gchar *string, gchar *domain)
432 {
433 gchar *p = string;
434 gchar *end;
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);
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 }