masqmail-0.2

annotate src/parse.c @ 3:8c55886cacd8

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