masqmail

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