masqmail

annotate src/parse.c @ 10:26e34ae9a3e3

changed indention and line wrapping to a more consistent style
author meillo@marmaro.de
date Mon, 27 Oct 2008 16:23:10 +0100
parents 08114f7dcc23
children f671821d8222
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@10 36 static gchar*
meillo@10 37 skip_comment(gchar * p)
meillo@0 38 {
meillo@0 39
meillo@0 40 #ifdef PARSE_TEST
meillo@10 41 g_print("skip_comment: %s\n", p);
meillo@0 42 #endif
meillo@0 43
meillo@10 44 p++;
meillo@10 45 while (*p && *p != ')') {
meillo@10 46 p++;
meillo@10 47 if (*p == '(')
meillo@10 48 p = skip_comment(p);
meillo@10 49 }
meillo@10 50 p++;
meillo@0 51
meillo@10 52 return p;
meillo@0 53 }
meillo@0 54
meillo@10 55 static gboolean
meillo@10 56 read_word(gchar * p, gchar ** b, gchar ** e)
meillo@0 57 {
meillo@0 58 #ifdef PARSE_TEST
meillo@10 59 g_print("read_word: %s\n", p);
meillo@0 60 #endif
meillo@10 61 /* eat leading spaces */
meillo@10 62 while (*p && isspace(*p))
meillo@10 63 p++;
meillo@10 64
meillo@10 65 *b = p;
meillo@10 66 /* b = &p; */
meillo@10 67 if (*p == '\"') {
meillo@10 68 /* quoted-string */
meillo@10 69 p++;
meillo@10 70 while (*p && (*p != '\"'))
meillo@10 71 p++;
meillo@10 72 p++;
meillo@10 73 } else {
meillo@10 74 /* atom */
meillo@10 75 while (*p && !strchr(specials, *p) && !iscntrl(*p) && !isspace(*p))
meillo@10 76 p++;
meillo@10 77 }
meillo@10 78 *e = p;
meillo@10 79 return TRUE;
meillo@0 80 }
meillo@0 81
meillo@10 82 static gboolean
meillo@10 83 read_word_with_dots(gchar * p, gchar ** b, gchar ** e)
meillo@0 84 {
meillo@10 85 gchar *b0 = p;
meillo@0 86
meillo@0 87 #ifdef PARSE_TEST
meillo@10 88 g_print("read_word_with_dots: %s\n", p);
meillo@0 89 #endif
meillo@10 90 while (TRUE) {
meillo@10 91 if (!read_word(p, b, e))
meillo@10 92 return FALSE;
meillo@10 93 p = *e;
meillo@10 94 if (*p != '.')
meillo@10 95 break;
meillo@10 96 p++;
meillo@10 97 }
meillo@10 98 *b = b0;
meillo@10 99 *e = p;
meillo@10 100 return TRUE;
meillo@0 101 }
meillo@0 102
meillo@10 103 static gboolean
meillo@10 104 read_domain(gchar * p, gchar ** b, gchar ** e)
meillo@0 105 {
meillo@0 106 #ifdef PARSE_TEST
meillo@10 107 g_print("read_domain: %s\n", p);
meillo@0 108 #endif
meillo@10 109 *b = p;
meillo@10 110 if (*p != '[') {
meillo@10 111 while (isalnum(*p) || (*p == '-') || (*p == '.'))
meillo@10 112 p++;
meillo@10 113 } else {
meillo@10 114 p++;
meillo@10 115 while (isalpha(*p) || (*p == '.'))
meillo@10 116 p++;
meillo@10 117 if (*p != ']') {
meillo@10 118 parse_error = g_strdup_printf("']' expected at end of literal address %s", *b);
meillo@10 119 return FALSE;
meillo@10 120 }
meillo@10 121 p++;
meillo@10 122 }
meillo@10 123 *e = p;
meillo@10 124 return TRUE;
meillo@0 125 }
meillo@0 126
meillo@10 127 gboolean
meillo@10 128 parse_address_rfc822(gchar* string, gchar** local_begin, gchar** local_end, gchar** domain_begin, gchar** domain_end, gchar** address_end)
meillo@0 129 {
meillo@10 130 gint angle_brackets = 0;
meillo@0 131
meillo@10 132 gchar *p = string;
meillo@10 133 gchar *b, *e;
meillo@0 134
meillo@10 135 *local_begin = *local_end = NULL;
meillo@10 136 *domain_begin = *domain_end = NULL;
meillo@0 137
meillo@10 138 /* might be some memory left from previous call: */
meillo@10 139 if (parse_error != NULL) {
meillo@10 140 g_free(parse_error);
meillo@10 141 parse_error = NULL;
meillo@10 142 }
meillo@0 143
meillo@10 144 /* leading spaces and angle brackets */
meillo@10 145 while (*p && (isspace(*p) || (*p == '<'))) {
meillo@10 146 if (*p == '<')
meillo@10 147 angle_brackets++;
meillo@10 148 p++;
meillo@10 149 }
meillo@10 150
meillo@10 151 if (*p) {
meillo@10 152 while (TRUE) {
meillo@10 153 if (read_word_with_dots(p, &b, &e)) {
meillo@10 154 p = e;
meillo@0 155 #ifdef PARSE_TEST
meillo@10 156 g_print("after read_word_with_dots: %s\n", p);
meillo@0 157 #endif
meillo@10 158 /* eat white spaces and comments */
meillo@10 159 while ((*p && (isspace(*p))) || (*p == '(')) {
meillo@10 160 if (*p == '(') {
meillo@10 161 if (!(p = skip_comment(p))) {
meillo@10 162 parse_error = g_strdup("missing right bracket ')'");
meillo@10 163 return FALSE;
meillo@10 164 }
meillo@10 165 } else
meillo@10 166 p++;
meillo@10 167 }
meillo@10 168 /* we now have a non-space char that is not
meillo@10 169 the beginning of a comment */
meillo@10 170
meillo@10 171 if (*p == '@') {
meillo@10 172 /* the last word was the local_part
meillo@10 173 of an addr-spec */
meillo@10 174 *local_begin = b;
meillo@10 175 *local_end = e;
meillo@10 176 #ifdef PARSE_TEST
meillo@10 177 g_print("found local part: %s\n", *local_begin);
meillo@10 178 #endif
meillo@10 179 if (*p == '@') {
meillo@10 180 p++; /* skip @ */
meillo@10 181 /* now the domain */
meillo@10 182 if (read_domain(p, &b, &e)) {
meillo@10 183 p = e;
meillo@10 184 *domain_begin = b;
meillo@10 185 *domain_end = e;
meillo@10 186 } else
meillo@10 187 return FALSE;
meillo@10 188 } else {
meillo@10 189 /* unqualified? */
meillo@10 190 *domain_begin = *domain_end = NULL;
meillo@10 191 }
meillo@10 192 break;
meillo@10 193 } else if (*p == '<') {
meillo@10 194 /* addr-spec follows */
meillo@10 195 while (isspace(*p) || (*p == '<')) {
meillo@10 196 if (*p == '<')
meillo@10 197 angle_brackets++;
meillo@10 198 p++;
meillo@10 199 }
meillo@10 200 if (read_word_with_dots(p, &b, &e)) {
meillo@10 201 p = e;
meillo@10 202 *local_begin = b;
meillo@10 203 *local_end = e;
meillo@10 204 #ifdef PARSE_TEST
meillo@10 205 g_print("found local part: %s\n", *local_begin);
meillo@10 206 #endif
meillo@10 207 } else
meillo@10 208 return FALSE;
meillo@10 209 if (*p == '@') {
meillo@10 210 p++;
meillo@10 211 if (read_domain(p, &b, &e)) {
meillo@10 212 p = e;
meillo@10 213 *domain_begin = b;
meillo@10 214 *domain_end = e;
meillo@10 215 } else
meillo@10 216 return FALSE;
meillo@10 217 } else {
meillo@10 218 /* may be unqualified address */
meillo@10 219 *domain_begin = *domain_end = NULL;
meillo@10 220 }
meillo@10 221 break;
meillo@10 222 } else if (!*p || *p == '>') {
meillo@10 223 *local_begin = b;
meillo@10 224 *local_end = e;
meillo@10 225 #ifdef PARSE_TEST
meillo@10 226 g_print("found local part: %s\n", *local_begin);
meillo@10 227 #endif
meillo@10 228 *domain_begin = *domain_end = NULL;
meillo@10 229 break;
meillo@10 230 } else if (strchr(specials, *p) || iscntrl(*p) || isspace(*p)) {
meillo@10 231 parse_error = g_strdup_printf("unexpected character: %c", *p);
meillo@10 232 return FALSE;
meillo@10 233 }
meillo@10 234 } else
meillo@10 235 return FALSE;
meillo@10 236 }
meillo@10 237 /* trailing spaces and angle brackets */
meillo@10 238 #ifdef PARSE_TEST
meillo@10 239 g_print("down counting trailing '>'\n");
meillo@10 240 #endif
meillo@10 241 while (*p && (isspace(*p) || (*p == '>'))) {
meillo@10 242 if (*p == '>')
meillo@10 243 angle_brackets--;
meillo@10 244 p++;
meillo@10 245 }
meillo@10 246
meillo@10 247 *address_end = p;
meillo@10 248
meillo@10 249 if (angle_brackets != 0) {
meillo@10 250 if (angle_brackets > 0)
meillo@10 251 parse_error = g_strdup("missing '>' at end of string");
meillo@10 252 else
meillo@10 253 parse_error = g_strdup("superfluous '>' at end of string");
meillo@10 254 return FALSE;
meillo@10 255 } else {
meillo@10 256 /* we successfully parsed the address */
meillo@10 257 return TRUE;
meillo@10 258 }
meillo@10 259 /* we never get here */
meillo@0 260 }
meillo@0 261 return FALSE;
meillo@0 262 }
meillo@0 263
meillo@10 264 gboolean
meillo@10 265 parse_address_rfc821(gchar* string, gchar** local_begin, gchar** local_end, gchar** domain_begin, gchar** domain_end, gchar** address_end)
meillo@0 266 {
meillo@10 267 gint angle_brackets = 0;
meillo@0 268
meillo@10 269 gchar *p = string;
meillo@10 270 gchar *b, *e;
meillo@0 271
meillo@10 272 *local_begin = *local_end = NULL;
meillo@10 273 *domain_begin = *domain_end = NULL;
meillo@0 274
meillo@10 275 /* might be some memory left from previous call: */
meillo@10 276 if (parse_error != NULL) {
meillo@10 277 g_free(parse_error);
meillo@10 278 parse_error = NULL;
meillo@10 279 }
meillo@0 280
meillo@10 281 /* leading spaces and angle brackets */
meillo@10 282 while (*p && (isspace(*p) || (*p == '<'))) {
meillo@10 283 if (*p == '<')
meillo@10 284 angle_brackets++;
meillo@10 285 p++;
meillo@10 286 }
meillo@10 287
meillo@10 288 if (*p) {
meillo@10 289 while (TRUE) {
meillo@10 290 if (read_word_with_dots(p, &b, &e)) {
meillo@10 291 p = e;
meillo@0 292 #ifdef PARSE_TEST
meillo@10 293 g_print("after read_word_with_dots: %s\n", p);
meillo@0 294 #endif
meillo@10 295 *local_begin = b;
meillo@10 296 *local_end = e;
meillo@0 297 #ifdef PARSE_TEST
meillo@10 298 g_print("found local part: %s\n", *local_begin);
meillo@10 299 g_print("local_end = %s\n", *local_end);
meillo@0 300 #endif
meillo@10 301 if (!(*p) || isspace(*p) || (*p == '>')) {
meillo@10 302 /* unqualified ? */
meillo@10 303 domain_begin = domain_end = NULL;
meillo@10 304 break;
meillo@10 305 } else if (*p == '@') {
meillo@10 306 p++;
meillo@10 307 if (read_domain(p, &b, &e)) {
meillo@10 308 p = e;
meillo@10 309 *domain_begin = b;
meillo@10 310 *domain_end = e;
meillo@10 311 }
meillo@10 312 break;
meillo@10 313 } else {
meillo@10 314 parse_error = g_strdup_printf ("unexpected character after local part '%c'", *p);
meillo@10 315 return FALSE;
meillo@10 316 }
meillo@10 317 } else
meillo@10 318 return FALSE;
meillo@10 319 }
meillo@10 320
meillo@10 321 /* trailing spaces and angle brackets */
meillo@10 322 #ifdef PARSE_TEST
meillo@10 323 g_print("down counting trailing '>'\n");
meillo@10 324 #endif
meillo@10 325 while (*p && (isspace(*p) || (*p == '>'))) {
meillo@10 326 if (*p == '>')
meillo@10 327 angle_brackets--;
meillo@10 328 p++;
meillo@10 329 }
meillo@10 330 *address_end = p;
meillo@10 331
meillo@10 332 if (angle_brackets != 0) {
meillo@10 333 if (angle_brackets > 0)
meillo@10 334 parse_error = g_strdup("missing '>' at end of string");
meillo@10 335 else
meillo@10 336 parse_error = g_strdup("superfluous '>' at end of string");
meillo@10 337 return FALSE;
meillo@10 338 } else {
meillo@10 339 /* we successfully parsed the address */
meillo@10 340 return TRUE;
meillo@10 341 }
meillo@10 342 /* we never get here */
meillo@0 343 }
meillo@10 344 return FALSE;
meillo@0 345 }
meillo@0 346
meillo@0 347 /*
meillo@0 348 allocate address, reading from string.
meillo@0 349 On failure, returns NULL.
meillo@0 350 after call, end contatins a pointer to the end of the parsed string
meillo@0 351 end may be NULL, if we are not interested.
meillo@0 352
meillo@0 353 parses both rfc 821 and rfc 822 addresses, depending on flag is_rfc821
meillo@0 354 */
meillo@0 355
meillo@10 356 address*
meillo@10 357 _create_address(gchar * string, gchar ** end, gboolean is_rfc821)
meillo@0 358 {
meillo@10 359 gchar *loc_beg, *loc_end;
meillo@10 360 gchar *dom_beg, *dom_end;
meillo@10 361 gchar *addr_end;
meillo@0 362
meillo@10 363 if (string && (string[0] == 0)) {
meillo@10 364 address *addr = g_malloc(sizeof(address));
meillo@10 365 addr->address = g_strdup("");
meillo@10 366 addr->local_part = g_strdup("");
meillo@10 367 addr->domain = g_strdup(""); /* 'NULL' address (failure notice), "" makes sure it will not be qualified with a hostname */
meillo@10 368 return addr;
meillo@10 369 }
meillo@0 370
meillo@10 371 if (is_rfc821
meillo@10 372 ? parse_address_rfc821(string, &loc_beg, &loc_end, &dom_beg, &dom_end, &addr_end)
meillo@10 373 : parse_address_rfc822(string, &loc_beg, &loc_end, &dom_beg, &dom_end, &addr_end))
meillo@10 374 {
meillo@10 375 address *addr = g_malloc(sizeof(address));
meillo@10 376 gchar *p = addr_end;
meillo@0 377
meillo@0 378
meillo@10 379 memset(addr, 0, sizeof(address));
meillo@0 380
meillo@10 381 if (loc_beg[0] == '|') {
meillo@10 382 parse_error = g_strdup("no pipe allowed for RFC 822/821 address");
meillo@10 383 return NULL;
meillo@10 384 }
meillo@0 385
meillo@10 386 while (*p && (*p != ','))
meillo@10 387 p++;
meillo@10 388 addr->address = g_strndup(string, p - string);
meillo@10 389
meillo@10 390 addr->local_part = g_strndup(loc_beg, loc_end - loc_beg);
meillo@0 391
meillo@0 392 #ifdef PARSE_TEST
meillo@10 393 g_print("addr->local_part = %s\n", addr->local_part);
meillo@0 394 #endif
meillo@0 395
meillo@10 396 if (dom_beg != NULL) {
meillo@10 397 addr->domain = g_strndup(dom_beg, dom_end - dom_beg);
meillo@10 398 } else {
meillo@10 399 if (addr->local_part[0] == 0)
meillo@10 400 addr->domain = g_strdup(""); /* 'NULL' address (failure notice), "" makes sure it will not be qualified with a hostname */
meillo@10 401 else
meillo@10 402 addr->domain = NULL;
meillo@10 403 }
meillo@0 404
meillo@10 405 if (end != NULL)
meillo@10 406 *end = p;
meillo@0 407
meillo@0 408 #ifndef PARSE_TEST
meillo@10 409 addr_unmark_delivered(addr);
meillo@0 410 #endif
meillo@0 411
meillo@10 412 return addr;
meillo@10 413 }
meillo@10 414 return NULL;
meillo@0 415 }
meillo@0 416
meillo@10 417 address*
meillo@10 418 create_address_rfc822(gchar * string, gchar ** end)
meillo@10 419 {
meillo@10 420 return _create_address(string, end, FALSE);
meillo@0 421 }
meillo@0 422
meillo@10 423 address*
meillo@10 424 create_address_rfc821(gchar * string, gchar ** end)
meillo@10 425 {
meillo@10 426 return _create_address(string, end, TRUE);
meillo@0 427 }
meillo@0 428
meillo@10 429 GList*
meillo@10 430 addr_list_append_rfc822(GList * addr_list, gchar * string, gchar * domain)
meillo@0 431 {
meillo@10 432 gchar *p = string;
meillo@10 433 gchar *end;
meillo@0 434
meillo@10 435 while (*p) {
meillo@10 436 address *addr = _create_address(p, &end, FALSE);
meillo@10 437 if (addr) {
meillo@10 438 if (domain)
meillo@10 439 if (addr->domain == NULL)
meillo@10 440 addr->domain = g_strdup(domain);
meillo@0 441
meillo@10 442 addr_list = g_list_append(addr_list, addr);
meillo@10 443 p = end;
meillo@10 444 } else
meillo@10 445 break;
meillo@10 446 while (*p == ',' || isspace(*p))
meillo@10 447 p++;
meillo@10 448 }
meillo@10 449 return addr_list;
meillo@0 450 }