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