masqmail
diff src/parse.c @ 0:08114f7dcc23
this is masqmail-0.2.21 from oliver kurth
author | meillo@marmaro.de |
---|---|
date | Fri, 26 Sep 2008 17:05:23 +0200 |
parents | |
children | 26e34ae9a3e3 |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/parse.c Fri Sep 26 17:05:23 2008 +0200 1.3 @@ -0,0 +1,450 @@ 1.4 +/* MasqMail 1.5 + Copyright (C) 1999-2001 Oliver Kurth 1.6 + 1.7 + This program is free software; you can redistribute it and/or modify 1.8 + it under the terms of the GNU General Public License as published by 1.9 + the Free Software Foundation; either version 2 of the License, or 1.10 + (at your option) any later version. 1.11 + 1.12 + This program is distributed in the hope that it will be useful, 1.13 + but WITHOUT ANY WARRANTY; without even the implied warranty of 1.14 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1.15 + GNU General Public License for more details. 1.16 + 1.17 + You should have received a copy of the GNU General Public License 1.18 + along with this program; if not, write to the Free Software 1.19 + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 1.20 +*/ 1.21 + 1.22 +#ifndef PARSE_TEST 1.23 +#include "masqmail.h" 1.24 +#endif 1.25 + 1.26 +/* This is really dangerous. I hope that I was careful enough, 1.27 + but maybe there is some malformed address possible that causes 1.28 + this to segfault or be caught in endless loops. 1.29 + 1.30 + If you find something like that, PLEASE mail the string to me 1.31 + (no matter how idiotic it is), so that I can debug that. 1.32 + Those things really should not happen. 1.33 +*/ 1.34 + 1.35 +static gchar *specials = "()<>@,;:\\\".[]`"; 1.36 + 1.37 +char *parse_error = NULL; 1.38 + 1.39 +static 1.40 +gchar *skip_comment(gchar *p) 1.41 +{ 1.42 + 1.43 +#ifdef PARSE_TEST 1.44 + g_print("skip_comment: %s\n", p); 1.45 +#endif 1.46 + 1.47 + p++; 1.48 + while(*p && *p != ')'){ 1.49 + p++; 1.50 + if(*p == '(') 1.51 + p = skip_comment(p); 1.52 + } 1.53 + p++; 1.54 + 1.55 + return p; 1.56 +} 1.57 + 1.58 +static 1.59 +gboolean read_word(gchar *p, gchar **b, gchar **e) 1.60 +{ 1.61 +#ifdef PARSE_TEST 1.62 + g_print("read_word: %s\n", p); 1.63 +#endif 1.64 + /* eat leading spaces */ 1.65 + while(*p && isspace(*p)) p++; 1.66 + 1.67 + *b = p; 1.68 + /* b = &p;*/ 1.69 + if(*p == '\"'){ 1.70 + /* quoted-string */ 1.71 + p++; 1.72 + while(*p && (*p != '\"')) p++; 1.73 + p++; 1.74 + }else{ 1.75 + /* atom */ 1.76 + while(*p && !strchr(specials, *p) && !iscntrl(*p) && !isspace(*p)) 1.77 + p++; 1.78 + } 1.79 + *e = p; 1.80 + return TRUE; 1.81 +} 1.82 + 1.83 +static 1.84 +gboolean read_word_with_dots(gchar *p, gchar **b, gchar **e) 1.85 +{ 1.86 + gchar *b0 = p; 1.87 + 1.88 +#ifdef PARSE_TEST 1.89 + g_print("read_word_with_dots: %s\n", p); 1.90 +#endif 1.91 + while(TRUE){ 1.92 + if(!read_word(p, b, e)) 1.93 + return FALSE; 1.94 + p = *e; 1.95 + if(*p != '.') break; 1.96 + p++; 1.97 + } 1.98 + *b = b0; 1.99 + *e = p; 1.100 + return TRUE; 1.101 +} 1.102 + 1.103 +static 1.104 +gboolean read_domain(gchar *p, gchar **b, gchar **e) 1.105 +{ 1.106 +#ifdef PARSE_TEST 1.107 + g_print("read_domain: %s\n", p); 1.108 +#endif 1.109 + *b = p; 1.110 + if(*p != '['){ 1.111 + while(isalnum(*p) || (*p == '-') || (*p == '.')) 1.112 + p++; 1.113 + }else{ 1.114 + p++; 1.115 + while(isalpha(*p) || (*p == '.')) 1.116 + p++; 1.117 + if(*p != ']'){ 1.118 + parse_error = 1.119 + g_strdup_printf("']' expected at end of literal address %s", *b); 1.120 + return FALSE; 1.121 + } 1.122 + p++; 1.123 + } 1.124 + *e = p; 1.125 + return TRUE; 1.126 +} 1.127 + 1.128 +gboolean parse_address_rfc822(gchar *string, 1.129 + gchar **local_begin, gchar **local_end, 1.130 + gchar **domain_begin, gchar **domain_end, 1.131 + gchar **address_end) 1.132 +{ 1.133 + gint angle_brackets = 0; 1.134 + 1.135 + gchar *p = string; 1.136 + gchar *b, *e; 1.137 + 1.138 + *local_begin = *local_end = NULL; 1.139 + *domain_begin = *domain_end = NULL; 1.140 + 1.141 + /* might be some memory left from previous call: */ 1.142 + if(parse_error != NULL){ 1.143 + g_free(parse_error); 1.144 + parse_error = NULL; 1.145 + } 1.146 + 1.147 + /* leading spaces and angle brackets */ 1.148 + while(*p && (isspace(*p) || (*p == '<'))){ 1.149 + if(*p == '<') 1.150 + angle_brackets++; 1.151 + p++; 1.152 + } 1.153 + 1.154 + if(*p){ 1.155 + while(TRUE){ 1.156 + if(read_word_with_dots(p, &b, &e)){ 1.157 + p = e; 1.158 +#ifdef PARSE_TEST 1.159 + g_print("after read_word_with_dots: %s\n", p); 1.160 +#endif 1.161 + /* eat white spaces and comments */ 1.162 + while((*p && (isspace(*p))) || (*p == '(')){ 1.163 + if(*p == '('){ 1.164 + if(!(p = skip_comment(p))){ 1.165 + parse_error = 1.166 + g_strdup("missing right bracket ')'"); 1.167 + return FALSE; 1.168 + } 1.169 + }else 1.170 + p++; 1.171 + } 1.172 + /* we now have a non-space char that is not 1.173 + the beginning of a comment */ 1.174 + 1.175 + if(*p == '@'){ 1.176 + /* the last word was the local_part 1.177 + of an addr-spec */ 1.178 + *local_begin = b; 1.179 + *local_end = e; 1.180 +#ifdef PARSE_TEST 1.181 + g_print("found local part: %s\n", *local_begin); 1.182 +#endif 1.183 + if(*p == '@'){ 1.184 + p++; /* skip @ */ 1.185 + /* now the domain */ 1.186 + if(read_domain(p, &b, &e)){ 1.187 + p = e; 1.188 + *domain_begin = b; 1.189 + *domain_end = e; 1.190 + } 1.191 + else 1.192 + return FALSE; 1.193 + }else{ 1.194 + /* unqualified? */ 1.195 + *domain_begin = *domain_end = NULL; 1.196 + } 1.197 + break; 1.198 + }else if(*p == '<'){ 1.199 + /* addr-spec follows */ 1.200 + while(isspace(*p) || (*p == '<')){ 1.201 + if(*p == '<') 1.202 + angle_brackets++; 1.203 + p++; 1.204 + } 1.205 + if(read_word_with_dots(p, &b, &e)){ 1.206 + p = e; 1.207 + *local_begin = b; 1.208 + *local_end = e; 1.209 +#ifdef PARSE_TEST 1.210 + g_print("found local part: %s\n", *local_begin); 1.211 +#endif 1.212 + }else 1.213 + return FALSE; 1.214 + if(*p == '@'){ 1.215 + p++; 1.216 + if(read_domain(p, &b, &e)){ 1.217 + p = e; 1.218 + *domain_begin = b; 1.219 + *domain_end = e; 1.220 + }else 1.221 + return FALSE; 1.222 + }else{ 1.223 + /* may be unqualified address */ 1.224 + *domain_begin = *domain_end = NULL; 1.225 + } 1.226 + break; 1.227 + }else if(!*p || *p == '>'){ 1.228 + *local_begin = b; 1.229 + *local_end = e; 1.230 +#ifdef PARSE_TEST 1.231 + g_print("found local part: %s\n", *local_begin); 1.232 +#endif 1.233 + *domain_begin = *domain_end = NULL; 1.234 + break; 1.235 + }else if(strchr(specials, *p) || iscntrl(*p) || isspace(*p)){ 1.236 + parse_error = g_strdup_printf("unexpected character: %c", *p); 1.237 + return FALSE; 1.238 + } 1.239 + }else 1.240 + return FALSE; 1.241 + } 1.242 + /* trailing spaces and angle brackets */ 1.243 +#ifdef PARSE_TEST 1.244 + g_print("down counting trailing '>'\n"); 1.245 +#endif 1.246 + while(*p && (isspace(*p) || (*p == '>'))){ 1.247 + if(*p == '>') 1.248 + angle_brackets--; 1.249 + p++; 1.250 + } 1.251 + 1.252 + *address_end = p; 1.253 + 1.254 + if(angle_brackets != 0){ 1.255 + if(angle_brackets > 0) 1.256 + parse_error = g_strdup("missing '>' at end of string"); 1.257 + else 1.258 + parse_error = g_strdup("superfluous '>' at end of string"); 1.259 + return FALSE; 1.260 + }else{ 1.261 + /* we successfully parsed the address */ 1.262 + return TRUE; 1.263 + } 1.264 + /* we never get here */ 1.265 + } 1.266 + return FALSE; 1.267 +} 1.268 + 1.269 +gboolean parse_address_rfc821(gchar *string, 1.270 + gchar **local_begin, gchar **local_end, 1.271 + gchar **domain_begin, gchar **domain_end, 1.272 + gchar **address_end) 1.273 +{ 1.274 + gint angle_brackets = 0; 1.275 + 1.276 + gchar *p = string; 1.277 + gchar *b, *e; 1.278 + 1.279 + *local_begin = *local_end = NULL; 1.280 + *domain_begin = *domain_end = NULL; 1.281 + 1.282 + /* might be some memory left from previous call: */ 1.283 + if(parse_error != NULL){ 1.284 + g_free(parse_error); 1.285 + parse_error = NULL; 1.286 + } 1.287 + 1.288 + /* leading spaces and angle brackets */ 1.289 + while(*p && (isspace(*p) || (*p == '<'))){ 1.290 + if(*p == '<') 1.291 + angle_brackets++; 1.292 + p++; 1.293 + } 1.294 + 1.295 + if(*p){ 1.296 + while(TRUE){ 1.297 + if(read_word_with_dots(p, &b, &e)){ 1.298 + p = e; 1.299 +#ifdef PARSE_TEST 1.300 + g_print("after read_word_with_dots: %s\n", p); 1.301 +#endif 1.302 + *local_begin = b; 1.303 + *local_end = e; 1.304 +#ifdef PARSE_TEST 1.305 + g_print("found local part: %s\n", *local_begin); 1.306 + g_print("local_end = %s\n", *local_end); 1.307 +#endif 1.308 + if(!(*p) || isspace(*p) || (*p == '>')){ 1.309 + /* unqualified ?*/ 1.310 + domain_begin = domain_end = NULL; 1.311 + break; 1.312 + }else if(*p == '@'){ 1.313 + p++; 1.314 + if(read_domain(p, &b, &e)){ 1.315 + p = e; 1.316 + *domain_begin = b; 1.317 + *domain_end = e; 1.318 + } 1.319 + break; 1.320 + }else{ 1.321 + parse_error = 1.322 + g_strdup_printf("unexpected character after local part '%c'",*p); 1.323 + return FALSE; 1.324 + } 1.325 + } else 1.326 + return FALSE; 1.327 + } 1.328 + 1.329 + /* trailing spaces and angle brackets */ 1.330 +#ifdef PARSE_TEST 1.331 + g_print("down counting trailing '>'\n"); 1.332 +#endif 1.333 + while(*p && (isspace(*p) || (*p == '>'))){ 1.334 + if(*p == '>') 1.335 + angle_brackets--; 1.336 + p++; 1.337 + } 1.338 + *address_end = p; 1.339 + 1.340 + if(angle_brackets != 0){ 1.341 + if(angle_brackets > 0) 1.342 + parse_error = g_strdup("missing '>' at end of string"); 1.343 + else 1.344 + parse_error = g_strdup("superfluous '>' at end of string"); 1.345 + return FALSE; 1.346 + }else{ 1.347 + /* we successfully parsed the address */ 1.348 + return TRUE; 1.349 + } 1.350 + /* we never get here */ 1.351 + } 1.352 + return FALSE; 1.353 +} 1.354 + 1.355 +/* 1.356 + allocate address, reading from string. 1.357 + On failure, returns NULL. 1.358 + after call, end contatins a pointer to the end of the parsed string 1.359 + end may be NULL, if we are not interested. 1.360 + 1.361 + parses both rfc 821 and rfc 822 addresses, depending on flag is_rfc821 1.362 +*/ 1.363 + 1.364 +address *_create_address(gchar *string, gchar **end, gboolean is_rfc821) 1.365 +{ 1.366 + gchar *loc_beg, *loc_end; 1.367 + gchar *dom_beg, *dom_end; 1.368 + gchar *addr_end; 1.369 + 1.370 + if (string && (string[0] == 0)) { 1.371 + address *addr = g_malloc(sizeof(address)); 1.372 + addr->address = g_strdup(""); 1.373 + addr->local_part = g_strdup(""); 1.374 + addr->domain = g_strdup(""); /* 'NULL' address (failure notice), 1.375 + "" makes sure it will not be qualified with a hostname */ 1.376 + return addr; 1.377 + } 1.378 + 1.379 + if(is_rfc821 ? 1.380 + parse_address_rfc821(string, 1.381 + &loc_beg, &loc_end, &dom_beg, &dom_end, &addr_end) : 1.382 + parse_address_rfc822(string, 1.383 + &loc_beg, &loc_end, &dom_beg, &dom_end, &addr_end)){ 1.384 + address *addr = g_malloc(sizeof(address)); 1.385 + gchar *p = addr_end; 1.386 + 1.387 + 1.388 + memset(addr, 0, sizeof(address)); 1.389 + 1.390 + if(loc_beg[0] == '|'){ 1.391 + parse_error = g_strdup("no pipe allowed for RFC 822/821 address"); 1.392 + return NULL; 1.393 + } 1.394 + 1.395 + while(*p && (*p != ',')) p++; 1.396 + addr->address = g_strndup(string, p - string); 1.397 + 1.398 + addr->local_part = g_strndup(loc_beg, loc_end - loc_beg); 1.399 + 1.400 +#ifdef PARSE_TEST 1.401 + g_print("addr->local_part = %s\n", addr->local_part); 1.402 +#endif 1.403 + 1.404 + if(dom_beg != NULL){ 1.405 + addr->domain = g_strndup(dom_beg, dom_end - dom_beg); 1.406 + }else{ 1.407 + if(addr->local_part[0] == 0) 1.408 + addr->domain = g_strdup(""); /* 'NULL' address (failure notice), 1.409 + "" makes sure it will not be qualified with a hostname */ 1.410 + else 1.411 + addr->domain = NULL; 1.412 + } 1.413 + 1.414 + if(end != NULL) 1.415 + *end = p; 1.416 + 1.417 +#ifndef PARSE_TEST 1.418 + addr_unmark_delivered(addr); 1.419 +#endif 1.420 + 1.421 + return addr; 1.422 + } 1.423 + return NULL; 1.424 +} 1.425 + 1.426 +address *create_address_rfc822(gchar *string, gchar **end){ 1.427 + return _create_address(string, end, FALSE); 1.428 +} 1.429 + 1.430 +address *create_address_rfc821(gchar *string, gchar **end){ 1.431 + return _create_address(string, end, TRUE); 1.432 +} 1.433 + 1.434 +GList *addr_list_append_rfc822(GList *addr_list, gchar *string, gchar *domain) 1.435 +{ 1.436 + gchar *p = string; 1.437 + gchar *end; 1.438 + 1.439 + while(*p){ 1.440 + address *addr = _create_address(p, &end, FALSE); 1.441 + if(addr){ 1.442 + if(domain) 1.443 + if(addr->domain == NULL) 1.444 + addr->domain = g_strdup(domain); 1.445 + 1.446 + addr_list = g_list_append(addr_list, addr); 1.447 + p = end; 1.448 + }else 1.449 + break; 1.450 + while(*p == ',' || isspace(*p)) p++; 1.451 + } 1.452 + return addr_list; 1.453 +}