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