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