masqmail

view src/parse.c @ 421:f37384470855

Changed lockdir to /var/lock/masqmail; Create lockdir and piddir on startup. Moved the lockdir out of the spool dir. (When /var/lock is a ramdisk we do well to have the lock files there.) Added the new configure option --with-lockdir to change that location. Nontheless, if we run_as_user, then lock files are always stored in the spool dir directly. Instead of installing the lockdir and piddir at installation time, we create them on startup time now if they are missing. This is necessary if lockdir or piddir are a tmpfs.
author markus schnalke <meillo@marmaro.de>
date Wed, 30 May 2012 09:38:38 +0200
parents cff967b2f51e
children
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 if (!string) {
394 return NULL;
395 }
396 while (isspace(*string)) {
397 string++;
398 }
399 /* TODO: what about (string == NULL)? */
400 if (string && (string[0] == '\0')) {
401 address *addr = g_malloc(sizeof(address));
402 addr->address = g_strdup("");
403 addr->local_part = g_strdup("");
404 /*
405 ** 'NULL' address: failure notice
406 ** "": will *not* be qualified with a hostname
407 */
408 addr->domain = g_strdup("");
409 return addr;
410 }
412 if (is_rfc821) {
413 ret = parse_address_rfc821(string, &loc_beg, &loc_end, &dom_beg, &dom_end, &addr_end);
414 } else {
415 ret = parse_address_rfc822(string, &loc_beg, &loc_end, &dom_beg, &dom_end, &addr_end);
416 }
417 if (!ret) {
418 return NULL;
419 }
420 if (*loc_beg == '|') {
421 parse_error = g_strdup("no pipe allowed for RFC 822/821 address");
422 return NULL;
423 }
425 address *addr = g_malloc(sizeof(address));
426 memset(addr, 0, sizeof(address));
428 gchar *p = addr_end;
429 while (*p && (*p != ',')) {
430 /* it seems as if we do this for the code in rewrite.c */
431 p++;
432 }
433 addr->address = g_strstrip(g_strndup(string, p - string));
434 addr->local_part = g_strndup(loc_beg, loc_end - loc_beg);
436 #ifdef PARSE_TEST
437 g_print("addr->local_part = %s\n", addr->local_part);
438 #endif
440 if (dom_beg != NULL) {
441 addr->domain = g_strndup(dom_beg, dom_end - dom_beg);
442 } else if (addr->local_part[0] == '\0') {
443 /*
444 ** 'NULL' address: failure notice
445 ** "": will *not* be qualified with a hostname
446 */
447 addr->domain = g_strdup("");
448 } else {
449 addr->domain = NULL;
450 }
452 if (end) {
453 *end = p;
454 }
456 DEBUG(6) debugf("_create_address(): address: `%s'\n", addr->address);
457 DEBUG(6) debugf("_create_address(): local_part: `%s'\n", addr->local_part);
458 DEBUG(6) debugf("_create_address(): domain: `%s'\n", addr->domain);
460 #ifndef PARSE_TEST
461 addr_unmark_delivered(addr);
462 #endif
464 return addr;
465 }
467 address*
468 create_address_rfc822(gchar *string, gchar **end)
469 {
470 return _create_address(string, end, FALSE);
471 }
473 address*
474 create_address_rfc821(gchar *string, gchar **end)
475 {
476 return _create_address(string, end, TRUE);
477 }
479 GList*
480 addr_list_append_rfc822(GList *addr_list, gchar *string, gchar *domain)
481 {
482 gchar *p = string;
483 gchar *end;
485 while (*p) {
486 #ifdef PARSE_TEST
487 g_print("string: %s\n", p);
488 #endif
490 address *addr = _create_address(p, &end, FALSE);
491 if (!addr) {
492 break;
493 }
495 #ifdef PARSE_TEST
496 g_print("addr: %s (%s<@>%s)", addr->address, addr->local_part, addr->domain);
497 #endif
498 if (domain && !addr->domain) {
499 addr->domain = g_strdup(domain);
500 }
501 #ifdef PARSE_TEST
502 g_print(" (%s<@>%s)\n", addr->local_part, addr->domain);
503 #endif
505 addr_list = g_list_append(addr_list, addr);
506 p = end;
508 while (*p == ',' || isspace(*p)) {
509 p++;
510 }
511 }
512 return addr_list;
513 }