masqmail-0.2

annotate src/parse.c @ 179:ec3fe72a3e99

Fixed an important bug with folded headers! g_strconcat() returns a *copy* of the string, but hdr->value still pointed to the old header (which probably was a memory leak, too). If the folded part had been quite small it was likely that the new string was at the same position as the old one, thus making everything go well. But if pretty long headers were folded several times it was likely that the new string was allocated somewhere else in memory, thus breaking things. In result mails to lots of recipients (folded header) were frequently only sent to the ones in the first line. Sorry for the inconvenience.
author meillo@marmaro.de
date Fri, 03 Jun 2011 09:52:17 +0200
parents f671821d8222
children
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 }