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