masqmail

annotate src/parse.c @ 227:cab46cefa4ce

renamed contrib/ to admin/ because the contents are for system admins and possibly for advanced users too
author meillo@marmaro.de
date Fri, 23 Jul 2010 11:49:44 +0200
parents f671821d8222
children 899175e8dff0
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@15 128 parse_address_rfc822(gchar* string, gchar** local_begin, gchar** local_end, gchar** domain_begin,
meillo@15 129 gchar** domain_end, gchar** address_end)
meillo@0 130 {
meillo@10 131 gint angle_brackets = 0;
meillo@0 132
meillo@10 133 gchar *p = string;
meillo@10 134 gchar *b, *e;
meillo@0 135
meillo@10 136 *local_begin = *local_end = NULL;
meillo@10 137 *domain_begin = *domain_end = NULL;
meillo@0 138
meillo@10 139 /* might be some memory left from previous call: */
meillo@10 140 if (parse_error != NULL) {
meillo@10 141 g_free(parse_error);
meillo@10 142 parse_error = NULL;
meillo@10 143 }
meillo@0 144
meillo@10 145 /* leading spaces and angle brackets */
meillo@10 146 while (*p && (isspace(*p) || (*p == '<'))) {
meillo@10 147 if (*p == '<')
meillo@10 148 angle_brackets++;
meillo@10 149 p++;
meillo@10 150 }
meillo@10 151
meillo@10 152 if (*p) {
meillo@10 153 while (TRUE) {
meillo@10 154 if (read_word_with_dots(p, &b, &e)) {
meillo@10 155 p = e;
meillo@0 156 #ifdef PARSE_TEST
meillo@10 157 g_print("after read_word_with_dots: %s\n", p);
meillo@0 158 #endif
meillo@10 159 /* eat white spaces and comments */
meillo@10 160 while ((*p && (isspace(*p))) || (*p == '(')) {
meillo@10 161 if (*p == '(') {
meillo@10 162 if (!(p = skip_comment(p))) {
meillo@10 163 parse_error = g_strdup("missing right bracket ')'");
meillo@10 164 return FALSE;
meillo@10 165 }
meillo@10 166 } else
meillo@10 167 p++;
meillo@10 168 }
meillo@10 169 /* we now have a non-space char that is not
meillo@10 170 the beginning of a comment */
meillo@10 171
meillo@10 172 if (*p == '@') {
meillo@10 173 /* the last word was the local_part
meillo@10 174 of an addr-spec */
meillo@10 175 *local_begin = b;
meillo@10 176 *local_end = e;
meillo@10 177 #ifdef PARSE_TEST
meillo@10 178 g_print("found local part: %s\n", *local_begin);
meillo@10 179 #endif
meillo@10 180 if (*p == '@') {
meillo@10 181 p++; /* skip @ */
meillo@10 182 /* now the domain */
meillo@10 183 if (read_domain(p, &b, &e)) {
meillo@10 184 p = e;
meillo@10 185 *domain_begin = b;
meillo@10 186 *domain_end = e;
meillo@10 187 } else
meillo@10 188 return FALSE;
meillo@10 189 } else {
meillo@10 190 /* unqualified? */
meillo@10 191 *domain_begin = *domain_end = NULL;
meillo@10 192 }
meillo@10 193 break;
meillo@10 194 } else if (*p == '<') {
meillo@10 195 /* addr-spec follows */
meillo@10 196 while (isspace(*p) || (*p == '<')) {
meillo@10 197 if (*p == '<')
meillo@10 198 angle_brackets++;
meillo@10 199 p++;
meillo@10 200 }
meillo@10 201 if (read_word_with_dots(p, &b, &e)) {
meillo@10 202 p = e;
meillo@10 203 *local_begin = b;
meillo@10 204 *local_end = e;
meillo@10 205 #ifdef PARSE_TEST
meillo@10 206 g_print("found local part: %s\n", *local_begin);
meillo@10 207 #endif
meillo@10 208 } else
meillo@10 209 return FALSE;
meillo@10 210 if (*p == '@') {
meillo@10 211 p++;
meillo@10 212 if (read_domain(p, &b, &e)) {
meillo@10 213 p = e;
meillo@10 214 *domain_begin = b;
meillo@10 215 *domain_end = e;
meillo@10 216 } else
meillo@10 217 return FALSE;
meillo@10 218 } else {
meillo@10 219 /* may be unqualified address */
meillo@10 220 *domain_begin = *domain_end = NULL;
meillo@10 221 }
meillo@10 222 break;
meillo@10 223 } else if (!*p || *p == '>') {
meillo@10 224 *local_begin = b;
meillo@10 225 *local_end = e;
meillo@10 226 #ifdef PARSE_TEST
meillo@10 227 g_print("found local part: %s\n", *local_begin);
meillo@10 228 #endif
meillo@10 229 *domain_begin = *domain_end = NULL;
meillo@10 230 break;
meillo@10 231 } else if (strchr(specials, *p) || iscntrl(*p) || isspace(*p)) {
meillo@10 232 parse_error = g_strdup_printf("unexpected character: %c", *p);
meillo@10 233 return FALSE;
meillo@10 234 }
meillo@10 235 } else
meillo@10 236 return FALSE;
meillo@10 237 }
meillo@10 238 /* trailing spaces and angle brackets */
meillo@10 239 #ifdef PARSE_TEST
meillo@10 240 g_print("down counting trailing '>'\n");
meillo@10 241 #endif
meillo@10 242 while (*p && (isspace(*p) || (*p == '>'))) {
meillo@10 243 if (*p == '>')
meillo@10 244 angle_brackets--;
meillo@10 245 p++;
meillo@10 246 }
meillo@10 247
meillo@10 248 *address_end = p;
meillo@10 249
meillo@10 250 if (angle_brackets != 0) {
meillo@10 251 if (angle_brackets > 0)
meillo@10 252 parse_error = g_strdup("missing '>' at end of string");
meillo@10 253 else
meillo@10 254 parse_error = g_strdup("superfluous '>' at end of string");
meillo@10 255 return FALSE;
meillo@10 256 } else {
meillo@10 257 /* we successfully parsed the address */
meillo@10 258 return TRUE;
meillo@10 259 }
meillo@10 260 /* we never get here */
meillo@0 261 }
meillo@0 262 return FALSE;
meillo@0 263 }
meillo@0 264
meillo@10 265 gboolean
meillo@15 266 parse_address_rfc821(gchar* string, gchar** local_begin, gchar** local_end, gchar** domain_begin,
meillo@15 267 gchar** domain_end, gchar** address_end)
meillo@0 268 {
meillo@10 269 gint angle_brackets = 0;
meillo@0 270
meillo@10 271 gchar *p = string;
meillo@10 272 gchar *b, *e;
meillo@0 273
meillo@10 274 *local_begin = *local_end = NULL;
meillo@10 275 *domain_begin = *domain_end = NULL;
meillo@0 276
meillo@10 277 /* might be some memory left from previous call: */
meillo@10 278 if (parse_error != NULL) {
meillo@10 279 g_free(parse_error);
meillo@10 280 parse_error = NULL;
meillo@10 281 }
meillo@0 282
meillo@10 283 /* leading spaces and angle brackets */
meillo@10 284 while (*p && (isspace(*p) || (*p == '<'))) {
meillo@10 285 if (*p == '<')
meillo@10 286 angle_brackets++;
meillo@10 287 p++;
meillo@10 288 }
meillo@10 289
meillo@10 290 if (*p) {
meillo@10 291 while (TRUE) {
meillo@10 292 if (read_word_with_dots(p, &b, &e)) {
meillo@10 293 p = e;
meillo@0 294 #ifdef PARSE_TEST
meillo@10 295 g_print("after read_word_with_dots: %s\n", p);
meillo@0 296 #endif
meillo@10 297 *local_begin = b;
meillo@10 298 *local_end = e;
meillo@0 299 #ifdef PARSE_TEST
meillo@10 300 g_print("found local part: %s\n", *local_begin);
meillo@10 301 g_print("local_end = %s\n", *local_end);
meillo@0 302 #endif
meillo@10 303 if (!(*p) || isspace(*p) || (*p == '>')) {
meillo@10 304 /* unqualified ? */
meillo@10 305 domain_begin = domain_end = NULL;
meillo@10 306 break;
meillo@10 307 } else if (*p == '@') {
meillo@10 308 p++;
meillo@10 309 if (read_domain(p, &b, &e)) {
meillo@10 310 p = e;
meillo@10 311 *domain_begin = b;
meillo@10 312 *domain_end = e;
meillo@10 313 }
meillo@10 314 break;
meillo@10 315 } else {
meillo@10 316 parse_error = g_strdup_printf ("unexpected character after local part '%c'", *p);
meillo@10 317 return FALSE;
meillo@10 318 }
meillo@10 319 } else
meillo@10 320 return FALSE;
meillo@10 321 }
meillo@10 322
meillo@10 323 /* trailing spaces and angle brackets */
meillo@10 324 #ifdef PARSE_TEST
meillo@10 325 g_print("down counting trailing '>'\n");
meillo@10 326 #endif
meillo@10 327 while (*p && (isspace(*p) || (*p == '>'))) {
meillo@10 328 if (*p == '>')
meillo@10 329 angle_brackets--;
meillo@10 330 p++;
meillo@10 331 }
meillo@10 332 *address_end = p;
meillo@10 333
meillo@10 334 if (angle_brackets != 0) {
meillo@10 335 if (angle_brackets > 0)
meillo@10 336 parse_error = g_strdup("missing '>' at end of string");
meillo@10 337 else
meillo@10 338 parse_error = g_strdup("superfluous '>' at end of string");
meillo@10 339 return FALSE;
meillo@10 340 } else {
meillo@10 341 /* we successfully parsed the address */
meillo@10 342 return TRUE;
meillo@10 343 }
meillo@10 344 /* we never get here */
meillo@0 345 }
meillo@10 346 return FALSE;
meillo@0 347 }
meillo@0 348
meillo@0 349 /*
meillo@0 350 allocate address, reading from string.
meillo@0 351 On failure, returns NULL.
meillo@114 352 after call, end contains a pointer to the end of the parsed string
meillo@0 353 end may be NULL, if we are not interested.
meillo@0 354
meillo@0 355 parses both rfc 821 and rfc 822 addresses, depending on flag is_rfc821
meillo@0 356 */
meillo@10 357 address*
meillo@10 358 _create_address(gchar * string, gchar ** end, gboolean is_rfc821)
meillo@0 359 {
meillo@10 360 gchar *loc_beg, *loc_end;
meillo@10 361 gchar *dom_beg, *dom_end;
meillo@10 362 gchar *addr_end;
meillo@0 363
meillo@10 364 if (string && (string[0] == 0)) {
meillo@10 365 address *addr = g_malloc(sizeof(address));
meillo@10 366 addr->address = g_strdup("");
meillo@10 367 addr->local_part = g_strdup("");
meillo@15 368 addr->domain = g_strdup(""); /* 'NULL' address (failure notice),
meillo@15 369 "" makes sure it will not be qualified with a hostname */
meillo@10 370 return addr;
meillo@10 371 }
meillo@0 372
meillo@10 373 if (is_rfc821
meillo@10 374 ? parse_address_rfc821(string, &loc_beg, &loc_end, &dom_beg, &dom_end, &addr_end)
meillo@10 375 : parse_address_rfc822(string, &loc_beg, &loc_end, &dom_beg, &dom_end, &addr_end))
meillo@10 376 {
meillo@10 377 address *addr = g_malloc(sizeof(address));
meillo@10 378 gchar *p = addr_end;
meillo@0 379
meillo@10 380 memset(addr, 0, sizeof(address));
meillo@0 381
meillo@10 382 if (loc_beg[0] == '|') {
meillo@10 383 parse_error = g_strdup("no pipe allowed for RFC 822/821 address");
meillo@10 384 return NULL;
meillo@10 385 }
meillo@0 386
meillo@10 387 while (*p && (*p != ','))
meillo@10 388 p++;
meillo@10 389 addr->address = g_strndup(string, p - string);
meillo@10 390
meillo@10 391 addr->local_part = g_strndup(loc_beg, loc_end - loc_beg);
meillo@0 392
meillo@0 393 #ifdef PARSE_TEST
meillo@10 394 g_print("addr->local_part = %s\n", addr->local_part);
meillo@0 395 #endif
meillo@0 396
meillo@10 397 if (dom_beg != NULL) {
meillo@10 398 addr->domain = g_strndup(dom_beg, dom_end - dom_beg);
meillo@10 399 } else {
meillo@10 400 if (addr->local_part[0] == 0)
meillo@15 401 addr->domain = g_strdup(""); /* 'NULL' address (failure notice),
meillo@15 402 "" makes sure it will not be qualified with a hostname */
meillo@10 403 else
meillo@10 404 addr->domain = NULL;
meillo@10 405 }
meillo@0 406
meillo@10 407 if (end != NULL)
meillo@10 408 *end = p;
meillo@0 409
meillo@0 410 #ifndef PARSE_TEST
meillo@10 411 addr_unmark_delivered(addr);
meillo@0 412 #endif
meillo@0 413
meillo@10 414 return addr;
meillo@10 415 }
meillo@10 416 return NULL;
meillo@0 417 }
meillo@0 418
meillo@10 419 address*
meillo@10 420 create_address_rfc822(gchar * string, gchar ** end)
meillo@10 421 {
meillo@10 422 return _create_address(string, end, FALSE);
meillo@0 423 }
meillo@0 424
meillo@10 425 address*
meillo@10 426 create_address_rfc821(gchar * string, gchar ** end)
meillo@10 427 {
meillo@10 428 return _create_address(string, end, TRUE);
meillo@0 429 }
meillo@0 430
meillo@10 431 GList*
meillo@10 432 addr_list_append_rfc822(GList * addr_list, gchar * string, gchar * domain)
meillo@0 433 {
meillo@10 434 gchar *p = string;
meillo@10 435 gchar *end;
meillo@0 436
meillo@10 437 while (*p) {
meillo@10 438 address *addr = _create_address(p, &end, FALSE);
meillo@10 439 if (addr) {
meillo@10 440 if (domain)
meillo@10 441 if (addr->domain == NULL)
meillo@10 442 addr->domain = g_strdup(domain);
meillo@0 443
meillo@10 444 addr_list = g_list_append(addr_list, addr);
meillo@10 445 p = end;
meillo@10 446 } else
meillo@10 447 break;
meillo@10 448 while (*p == ',' || isspace(*p))
meillo@10 449 p++;
meillo@10 450 }
meillo@10 451 return addr_list;
meillo@0 452 }