masqmail

view src/parse.c @ 378:5781ba87df95

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