meillo@367: /* meillo@367: ** MasqMail meillo@367: ** Copyright (C) 1999-2001 Oliver Kurth meillo@367: ** Copyright (C) 2010 markus schnalke meillo@367: ** meillo@367: ** This program is free software; you can redistribute it and/or modify meillo@367: ** it under the terms of the GNU General Public License as published by meillo@367: ** the Free Software Foundation; either version 2 of the License, or meillo@367: ** (at your option) any later version. meillo@367: ** meillo@367: ** This program is distributed in the hope that it will be useful, meillo@367: ** but WITHOUT ANY WARRANTY; without even the implied warranty of meillo@367: ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the meillo@367: ** GNU General Public License for more details. meillo@367: ** meillo@367: ** You should have received a copy of the GNU General Public License meillo@367: ** along with this program; if not, write to the Free Software meillo@367: ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. meillo@0: */ meillo@0: meillo@0: #ifndef PARSE_TEST meillo@0: #include "masqmail.h" meillo@0: #endif meillo@0: meillo@367: /* meillo@367: ** This is really dangerous. I hope that I was careful enough, meillo@367: ** but maybe there is some malformed address possible that causes meillo@367: ** this to segfault or be caught in endless loops. meillo@0: meillo@367: ** If you find something like that, PLEASE mail the string to me meillo@367: ** (no matter how idiotic it is), so that I can debug that. meillo@367: ** Those things really should not happen. meillo@0: */ meillo@0: meillo@0: static gchar *specials = "()<>@,;:\\\".[]`"; meillo@0: meillo@0: char *parse_error = NULL; meillo@0: meillo@10: static gchar* meillo@366: skip_comment(gchar *p) meillo@0: { meillo@0: meillo@0: #ifdef PARSE_TEST meillo@10: g_print("skip_comment: %s\n", p); meillo@0: #endif meillo@0: meillo@10: p++; meillo@10: while (*p && *p != ')') { meillo@10: p++; meillo@271: if (*p == '(') { meillo@10: p = skip_comment(p); meillo@271: } meillo@10: } meillo@10: p++; meillo@0: meillo@10: return p; meillo@0: } meillo@0: meillo@10: static gboolean meillo@366: read_word(gchar *p, gchar **b, gchar **e) meillo@0: { meillo@0: #ifdef PARSE_TEST meillo@10: g_print("read_word: %s\n", p); meillo@0: #endif meillo@10: /* eat leading spaces */ meillo@271: while (*p && isspace(*p)) { meillo@10: p++; meillo@271: } meillo@10: meillo@10: *b = p; meillo@10: /* b = &p; */ meillo@10: if (*p == '\"') { meillo@10: /* quoted-string */ meillo@10: p++; meillo@271: while (*p && (*p != '\"')) { meillo@10: p++; meillo@271: } meillo@10: p++; meillo@10: } else { meillo@10: /* atom */ meillo@271: while (*p && !strchr(specials, *p) && !iscntrl(*p) && !isspace(*p)) { meillo@10: p++; meillo@271: } meillo@10: } meillo@10: *e = p; meillo@10: return TRUE; meillo@0: } meillo@0: meillo@10: static gboolean meillo@366: read_word_with_dots(gchar *p, gchar **b, gchar **e) meillo@0: { meillo@10: gchar *b0 = p; meillo@0: meillo@0: #ifdef PARSE_TEST meillo@10: g_print("read_word_with_dots: %s\n", p); meillo@0: #endif meillo@10: while (TRUE) { meillo@271: if (!read_word(p, b, e)) { meillo@10: return FALSE; meillo@271: } meillo@10: p = *e; meillo@271: if (*p != '.') { meillo@10: break; meillo@271: } meillo@10: p++; meillo@10: } meillo@10: *b = b0; meillo@10: *e = p; meillo@10: return TRUE; meillo@0: } meillo@0: meillo@10: static gboolean meillo@366: read_domain(gchar *p, gchar **b, gchar **e) meillo@0: { meillo@0: #ifdef PARSE_TEST meillo@10: g_print("read_domain: %s\n", p); meillo@0: #endif meillo@10: *b = p; meillo@10: if (*p != '[') { meillo@271: while (isalnum(*p) || (*p == '-') || (*p == '.')) { meillo@10: p++; meillo@271: } meillo@10: } else { meillo@10: p++; meillo@271: while (isalpha(*p) || (*p == '.')) { meillo@10: p++; meillo@271: } meillo@10: if (*p != ']') { meillo@10: parse_error = g_strdup_printf("']' expected at end of literal address %s", *b); meillo@10: return FALSE; meillo@10: } meillo@10: p++; meillo@10: } meillo@10: *e = p; meillo@10: return TRUE; meillo@0: } meillo@0: meillo@10: gboolean meillo@367: parse_address_rfc822(gchar *string, gchar **local_begin, gchar **local_end, meillo@367: gchar **domain_begin, gchar **domain_end, gchar **address_end) meillo@0: { meillo@10: gint angle_brackets = 0; meillo@0: meillo@10: gchar *p = string; meillo@10: gchar *b, *e; meillo@0: meillo@10: *local_begin = *local_end = NULL; meillo@10: *domain_begin = *domain_end = NULL; meillo@0: meillo@10: /* might be some memory left from previous call: */ meillo@273: if (parse_error) { meillo@10: g_free(parse_error); meillo@10: parse_error = NULL; meillo@10: } meillo@0: meillo@10: /* leading spaces and angle brackets */ meillo@10: while (*p && (isspace(*p) || (*p == '<'))) { meillo@271: if (*p == '<') { meillo@10: angle_brackets++; meillo@271: } meillo@10: p++; meillo@10: } meillo@10: meillo@271: if (!*p) { meillo@271: return FALSE; meillo@271: } meillo@271: meillo@271: while (TRUE) { meillo@271: if (!read_word_with_dots(p, &b, &e)) { meillo@271: return FALSE; meillo@271: } meillo@271: meillo@271: p = e; meillo@0: #ifdef PARSE_TEST meillo@271: g_print("after read_word_with_dots: %s\n", p); meillo@0: #endif meillo@271: /* eat white spaces and comments */ meillo@271: while ((*p && (isspace(*p))) || (*p == '(')) { meillo@271: if (*p == '(') { meillo@271: if (!(p = skip_comment(p))) { meillo@271: parse_error = g_strdup("missing right bracket ')'"); meillo@10: return FALSE; meillo@10: } meillo@271: } else { meillo@271: p++; meillo@271: } meillo@271: } meillo@367: /* meillo@367: ** we now have a non-space char that is not meillo@367: ** the beginning of a comment meillo@367: */ meillo@271: meillo@274: if (*p == '@' || *p == ',') { meillo@271: /* the last word was the local_part of an addr-spec */ meillo@271: *local_begin = b; meillo@271: *local_end = e; meillo@271: #ifdef PARSE_TEST meillo@271: g_print("found local part: %s\n", *local_begin); meillo@271: #endif meillo@271: if (*p == '@') { meillo@271: p++; /* skip @ */ meillo@271: /* now the domain */ meillo@271: if (!read_domain(p, &b, &e)) { meillo@271: return FALSE; meillo@271: } meillo@271: p = e; meillo@271: *domain_begin = b; meillo@271: *domain_end = e; meillo@271: } else { meillo@271: /* unqualified? */ meillo@274: /* something like `To: alice, bob' with -t */ meillo@271: *domain_begin = *domain_end = NULL; meillo@271: } meillo@271: break; meillo@271: meillo@271: } else if (*p == '<') { meillo@271: /* addr-spec follows */ meillo@271: while (isspace(*p) || (*p == '<')) { meillo@271: if (*p == '<') { meillo@271: angle_brackets++; meillo@271: } meillo@271: p++; meillo@271: } meillo@271: if (!read_word_with_dots(p, &b, &e)) { meillo@10: return FALSE; meillo@271: } meillo@271: p = e; meillo@271: *local_begin = b; meillo@271: *local_end = e; meillo@271: #ifdef PARSE_TEST meillo@271: g_print("found local part: %s\n", *local_begin); meillo@271: #endif meillo@271: if (*p == '@') { meillo@271: p++; meillo@271: if (!read_domain(p, &b, &e)) { meillo@271: return FALSE; meillo@271: } meillo@271: p = e; meillo@271: *domain_begin = b; meillo@271: *domain_end = e; meillo@271: } else { meillo@271: /* may be unqualified address */ meillo@271: *domain_begin = *domain_end = NULL; meillo@271: } meillo@271: break; meillo@271: meillo@271: } else if (!*p || *p == '>') { meillo@271: *local_begin = b; meillo@271: *local_end = e; meillo@271: #ifdef PARSE_TEST meillo@271: g_print("found local part: %s\n", *local_begin); meillo@271: #endif meillo@271: *domain_begin = *domain_end = NULL; meillo@271: break; meillo@271: meillo@271: } else if (strchr(specials, *p) || iscntrl(*p) || isspace(*p)) { meillo@271: parse_error = g_strdup_printf("unexpected character: %c", *p); meillo@273: #ifdef PARSE_TEST meillo@273: g_print("unexpected character: %c", *p); meillo@273: #endif meillo@271: return FALSE; meillo@10: } meillo@271: } meillo@271: meillo@271: /* trailing spaces and angle brackets */ meillo@10: #ifdef PARSE_TEST meillo@271: g_print("down counting trailing '>'\n"); meillo@10: #endif meillo@271: while (*p && (isspace(*p) || (*p == '>'))) { meillo@271: if (*p == '>') { meillo@271: angle_brackets--; meillo@10: } meillo@271: p++; meillo@271: } meillo@10: meillo@271: *address_end = p; meillo@10: meillo@271: if (angle_brackets > 0) { meillo@271: parse_error = g_strdup("missing '>' at end of string"); meillo@271: return FALSE; meillo@271: } else if (angle_brackets < 0) { meillo@271: parse_error = g_strdup("superfluous '>' at end of string"); meillo@271: return FALSE; meillo@0: } meillo@271: meillo@271: /* we successfully parsed the address */ meillo@271: return TRUE; meillo@0: } meillo@0: meillo@10: gboolean meillo@367: parse_address_rfc821(gchar *string, gchar **local_begin, gchar **local_end, meillo@367: gchar **domain_begin, gchar **domain_end, gchar **address_end) meillo@0: { meillo@10: gint angle_brackets = 0; meillo@0: meillo@10: gchar *p = string; meillo@10: gchar *b, *e; meillo@0: meillo@10: *local_begin = *local_end = NULL; meillo@10: *domain_begin = *domain_end = NULL; meillo@0: meillo@10: /* might be some memory left from previous call: */ meillo@10: if (parse_error != NULL) { meillo@10: g_free(parse_error); meillo@10: parse_error = NULL; meillo@10: } meillo@0: meillo@10: /* leading spaces and angle brackets */ meillo@10: while (*p && (isspace(*p) || (*p == '<'))) { meillo@271: if (*p == '<') { meillo@10: angle_brackets++; meillo@271: } meillo@10: p++; meillo@10: } meillo@10: meillo@271: if (!*p) { meillo@271: return FALSE; meillo@271: } meillo@271: meillo@271: while (TRUE) { meillo@271: if (!read_word_with_dots(p, &b, &e)) { meillo@271: return FALSE; meillo@10: } meillo@10: meillo@271: p = e; meillo@10: #ifdef PARSE_TEST meillo@271: g_print("after read_word_with_dots: %s\n", p); meillo@10: #endif meillo@271: *local_begin = b; meillo@271: *local_end = e; meillo@271: #ifdef PARSE_TEST meillo@271: g_print("found local part: %s\n", *local_begin); meillo@271: g_print("local_end = %s\n", *local_end); meillo@271: #endif meillo@271: if (!(*p) || isspace(*p) || (*p == '>')) { meillo@271: /* unqualified ? */ meillo@271: domain_begin = domain_end = NULL; meillo@271: break; meillo@271: } else if (*p == '@') { meillo@10: p++; meillo@271: if (read_domain(p, &b, &e)) { meillo@271: p = e; meillo@271: *domain_begin = b; meillo@271: *domain_end = e; meillo@271: } meillo@271: break; meillo@271: } else { meillo@271: parse_error = g_strdup_printf ("unexpected character after local part '%c'", *p); meillo@271: return FALSE; meillo@10: } meillo@271: } meillo@10: meillo@271: /* trailing spaces and angle brackets */ meillo@271: #ifdef PARSE_TEST meillo@271: g_print("down counting trailing '>'\n"); meillo@271: #endif meillo@271: while (*p && (isspace(*p) || (*p == '>'))) { meillo@271: if (*p == '>') { meillo@271: angle_brackets--; meillo@10: } meillo@271: p++; meillo@0: } meillo@271: *address_end = p; meillo@271: meillo@271: if (angle_brackets > 0) { meillo@271: parse_error = g_strdup("missing '>' at end of string"); meillo@271: return FALSE; meillo@271: } else if (angle_brackets < 0) { meillo@271: parse_error = g_strdup("superfluous '>' at end of string"); meillo@271: return FALSE; meillo@271: } meillo@271: meillo@271: /* we successfully parsed the address */ meillo@271: return TRUE; meillo@0: } meillo@0: meillo@0: /* meillo@367: ** allocate address, reading from string. meillo@367: ** On failure, returns NULL. meillo@367: ** after call, end contains a pointer to the end of the parsed string meillo@367: ** end may be NULL, if we are not interested. meillo@367: ** meillo@367: ** parses both rfc 821 and rfc 822 addresses, depending on flag is_rfc821 meillo@0: */ meillo@10: address* meillo@366: _create_address(gchar *string, gchar **end, gboolean is_rfc821) meillo@0: { meillo@10: gchar *loc_beg, *loc_end; meillo@10: gchar *dom_beg, *dom_end; meillo@10: gchar *addr_end; meillo@271: gboolean ret; meillo@0: meillo@413: if (!string) { meillo@413: return NULL; meillo@413: } meillo@413: while (isspace(*string)) { meillo@413: string++; meillo@413: } meillo@273: /* TODO: what about (string == NULL)? */ meillo@271: if (string && (string[0] == '\0')) { meillo@10: address *addr = g_malloc(sizeof(address)); meillo@10: addr->address = g_strdup(""); meillo@10: addr->local_part = g_strdup(""); meillo@410: /* meillo@410: ** 'NULL' address: failure notice meillo@410: ** "": will *not* be qualified with a hostname meillo@410: */ meillo@271: addr->domain = g_strdup(""); meillo@10: return addr; meillo@10: } meillo@0: meillo@271: if (is_rfc821) { meillo@271: ret = parse_address_rfc821(string, &loc_beg, &loc_end, &dom_beg, &dom_end, &addr_end); meillo@271: } else { meillo@271: ret = parse_address_rfc822(string, &loc_beg, &loc_end, &dom_beg, &dom_end, &addr_end); meillo@271: } meillo@271: if (!ret) { meillo@271: return NULL; meillo@271: } meillo@413: if (*loc_beg == '|') { meillo@271: parse_error = g_strdup("no pipe allowed for RFC 822/821 address"); meillo@271: return NULL; meillo@271: } meillo@10: meillo@413: address *addr = g_malloc(sizeof(address)); meillo@413: memset(addr, 0, sizeof(address)); meillo@413: meillo@413: gchar *p = addr_end; meillo@271: while (*p && (*p != ',')) { meillo@413: /* it seems as if we do this for the code in rewrite.c */ meillo@271: p++; meillo@271: } meillo@413: addr->address = g_strstrip(g_strndup(string, p - string)); meillo@271: addr->local_part = g_strndup(loc_beg, loc_end - loc_beg); meillo@0: meillo@0: #ifdef PARSE_TEST meillo@271: g_print("addr->local_part = %s\n", addr->local_part); meillo@0: #endif meillo@0: meillo@271: if (dom_beg != NULL) { meillo@271: addr->domain = g_strndup(dom_beg, dom_end - dom_beg); meillo@273: } else if (addr->local_part[0] == '\0') { meillo@410: /* meillo@410: ** 'NULL' address: failure notice meillo@410: ** "": will *not* be qualified with a hostname meillo@410: */ meillo@271: addr->domain = g_strdup(""); meillo@271: } else { meillo@271: addr->domain = NULL; meillo@271: } meillo@0: meillo@273: if (end) { meillo@271: *end = p; meillo@271: } meillo@0: meillo@413: DEBUG(6) debugf("_create_address(): address: `%s'\n", addr->address); meillo@413: DEBUG(6) debugf("_create_address(): local_part: `%s'\n", addr->local_part); meillo@413: DEBUG(6) debugf("_create_address(): domain: `%s'\n", addr->domain); meillo@413: meillo@0: #ifndef PARSE_TEST meillo@271: addr_unmark_delivered(addr); meillo@0: #endif meillo@0: meillo@271: return addr; meillo@0: } meillo@0: meillo@10: address* meillo@366: create_address_rfc822(gchar *string, gchar **end) meillo@10: { meillo@10: return _create_address(string, end, FALSE); meillo@0: } meillo@0: meillo@10: address* meillo@366: create_address_rfc821(gchar *string, gchar **end) meillo@10: { meillo@10: return _create_address(string, end, TRUE); meillo@0: } meillo@0: meillo@10: GList* meillo@366: addr_list_append_rfc822(GList *addr_list, gchar *string, gchar *domain) meillo@0: { meillo@10: gchar *p = string; meillo@10: gchar *end; meillo@0: meillo@10: while (*p) { meillo@273: #ifdef PARSE_TEST meillo@273: g_print("string: %s\n", p); meillo@273: #endif meillo@273: meillo@10: address *addr = _create_address(p, &end, FALSE); meillo@271: if (!addr) { meillo@10: break; meillo@271: } meillo@271: meillo@273: #ifdef PARSE_TEST meillo@273: g_print("addr: %s (%s<@>%s)", addr->address, addr->local_part, addr->domain); meillo@273: #endif meillo@271: if (domain && !addr->domain) { meillo@271: addr->domain = g_strdup(domain); meillo@271: } meillo@273: #ifdef PARSE_TEST meillo@273: g_print(" (%s<@>%s)\n", addr->local_part, addr->domain); meillo@273: #endif meillo@271: meillo@271: addr_list = g_list_append(addr_list, addr); meillo@271: p = end; meillo@271: meillo@271: while (*p == ',' || isspace(*p)) { meillo@10: p++; meillo@271: } meillo@10: } meillo@10: return addr_list; meillo@0: }