Annotation of win32/gnome/gmime-x.x.x/gmime-parser.c, revision 1.1
1.1 ! paf 1: /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
! 2: /*
! 3: * Authors: Jeffrey Stedfast <fejj@ximian.com>
! 4: *
! 5: * Copyright 2000-2002 Ximian, Inc. (www.ximian.com)
! 6: *
! 7: * This program is free software; you can redistribute it and/or modify
! 8: * it under the terms of the GNU General Public License as published by
! 9: * the Free Software Foundation; either version 2 of the License, or
! 10: * (at your option) any later version.
! 11: *
! 12: * This program is distributed in the hope that it will be useful,
! 13: * but WITHOUT ANY WARRANTY; without even the implied warranty of
! 14: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
! 15: * GNU General Public License for more details.
! 16: *
! 17: * You should have received a copy of the GNU General Public License
! 18: * along with this program; if not, write to the Free Software
! 19: * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
! 20: *
! 21: */
! 22:
! 23:
! 24: #ifdef HAVE_CONFIG_H
! 25: #include <config.h>
! 26: #endif
! 27:
! 28: #include "strlib.h"
! 29: #include <ctype.h>
! 30:
! 31: #include "gmime-parser.h"
! 32: #include "gmime-stream-mem.h"
! 33:
! 34: #ifndef HAVE_ISBLANK
! 35: #define isblank(c) ((c) == ' ' || (c) == '\t')
! 36: #endif /* HAVE_ISBLANK */
! 37:
! 38: #define d(x)
! 39:
! 40: typedef struct _GMimeParser GMimeParser;
! 41:
! 42: static GMimeParser *parser_new (GMimeStream *stream);
! 43: static void parser_destroy (GMimeParser *parser);
! 44:
! 45: static void parser_init (GMimeParser *parser, GMimeStream *stream);
! 46: static void parser_close (GMimeParser *parser);
! 47:
! 48: static GMimePart *parser_construct_leaf_part (GMimeParser *parser, GMimeContentType *content_type,
! 49: int *found);
! 50: static GMimePart *parser_construct_multipart (GMimeParser *parser, GMimeContentType *content_type,
! 51: int *found);
! 52:
! 53:
! 54: #define SCAN_BUF 4096 /* size of read buffer */
! 55: #define SCAN_HEAD 128 /* headroom guaranteed to be before each read buffer */
! 56:
! 57: enum {
! 58: GMIME_PARSER_STATE_INIT,
! 59: GMIME_PARSER_STATE_FROM,
! 60: GMIME_PARSER_STATE_HEADERS,
! 61: GMIME_PARSER_STATE_HEADERS_END,
! 62: GMIME_PARSER_STATE_CONTENT,
! 63: };
! 64:
! 65: struct _GMimeParser {
! 66: int state;
! 67:
! 68: GMimeStream *stream;
! 69:
! 70: off_t offset;
! 71:
! 72: /* i/o buffers */
! 73: unsigned char realbuf[SCAN_HEAD + SCAN_BUF + 1];
! 74: unsigned char *inbuf;
! 75: unsigned char *inptr;
! 76: unsigned char *inend;
! 77:
! 78: /* header buffer */
! 79: unsigned char *headerbuf;
! 80: unsigned char *headerptr;
! 81: unsigned int headerleft;
! 82:
! 83: off_t headers_start;
! 84: off_t header_start;
! 85:
! 86: unsigned int unstep:30;
! 87: unsigned int midline:1;
! 88: unsigned int seekable:1;
! 89:
! 90: GMimeContentType *content_type;
! 91: struct _header_raw *headers;
! 92:
! 93: struct _boundary_stack *bounds;
! 94: };
! 95:
! 96: struct _boundary_stack {
! 97: struct _boundary_stack *parent;
! 98: unsigned char *boundary;
! 99: unsigned int boundarylen;
! 100: unsigned int boundarylenfinal;
! 101: unsigned int boundarylenmax;
! 102: };
! 103:
! 104: static void
! 105: parser_push_boundary (GMimeParser *parser, const char *boundary)
! 106: {
! 107: struct _boundary_stack *s;
! 108: unsigned int max;
! 109:
! 110: max = parser->bounds ? parser->bounds->boundarylenmax : 0;
! 111:
! 112: s = g_new (struct _boundary_stack, 1);
! 113: s->parent = parser->bounds;
! 114: parser->bounds = s;
! 115:
! 116: s->boundary = g_strdup_printf ("--%s--", boundary);
! 117: s->boundarylen = strlen (boundary) + 2;
! 118: s->boundarylenfinal = strlen (s->boundary);
! 119:
! 120: s->boundarylenmax = MAX (s->boundarylenfinal, max);
! 121: }
! 122:
! 123: static void
! 124: parser_pop_boundary (GMimeParser *parser)
! 125: {
! 126: struct _boundary_stack *s;
! 127:
! 128: if (!parser->bounds) {
! 129: g_warning ("boundary stack underflow");
! 130: return;
! 131: }
! 132:
! 133: s = parser->bounds;
! 134: parser->bounds = parser->bounds->parent;
! 135:
! 136: g_free (s->boundary);
! 137: g_free (s);
! 138: }
! 139:
! 140: struct _header_raw {
! 141: struct _header_raw *next;
! 142: char *name;
! 143: char *value;
! 144: off_t offset;
! 145: };
! 146:
! 147: static const char *
! 148: header_raw_find (struct _header_raw *headers, const char *name, off_t *offset)
! 149: {
! 150: struct _header_raw *h;
! 151:
! 152: h = headers;
! 153: while (h) {
! 154: if (!strcasecmp (h->name, name)) {
! 155: if (offset)
! 156: *offset = h->offset;
! 157: return h->value;
! 158: }
! 159:
! 160: h = h->next;
! 161: }
! 162:
! 163: return NULL;
! 164: }
! 165:
! 166: static void
! 167: header_raw_clear (struct _header_raw **headers)
! 168: {
! 169: struct _header_raw *h, *n;
! 170:
! 171: h = *headers;
! 172: while (h) {
! 173: n = h->next;
! 174: g_free (h->name);
! 175: g_free (h->value);
! 176: g_free (h);
! 177: h = n;
! 178: }
! 179:
! 180: *headers = NULL;
! 181: }
! 182:
! 183: static GMimeParser *
! 184: parser_new (GMimeStream *stream)
! 185: {
! 186: GMimeParser *parser;
! 187:
! 188: parser = g_new (GMimeParser, 1);
! 189: parser_init (parser, stream);
! 190:
! 191: return parser;
! 192: }
! 193:
! 194: static void
! 195: parser_destroy (GMimeParser *parser)
! 196: {
! 197: if (parser) {
! 198: parser_close (parser);
! 199: g_free (parser);
! 200: }
! 201: }
! 202:
! 203:
! 204: static void
! 205: parser_init (GMimeParser *parser, GMimeStream *stream)
! 206: {
! 207: off_t offset = -1;
! 208:
! 209: if (stream) {
! 210: g_mime_stream_ref (stream);
! 211: offset = g_mime_stream_tell (stream);
! 212: }
! 213:
! 214: parser->state = GMIME_PARSER_STATE_INIT;
! 215:
! 216: parser->stream = stream;
! 217:
! 218: parser->offset = offset;
! 219:
! 220: parser->inbuf = parser->realbuf + SCAN_HEAD;
! 221: parser->inptr = parser->inbuf;
! 222: parser->inend = parser->inbuf;
! 223:
! 224: parser->headerbuf = g_malloc (SCAN_HEAD + 1);
! 225: parser->headerptr = parser->headerbuf;
! 226: parser->headerleft = SCAN_HEAD;
! 227:
! 228: parser->headers_start = -1;
! 229: parser->header_start = -1;
! 230:
! 231: parser->unstep = 0;
! 232: parser->midline = FALSE;
! 233: parser->seekable = offset != -1;
! 234:
! 235: parser->headers = NULL;
! 236:
! 237: parser->bounds = NULL;
! 238: }
! 239:
! 240: static void
! 241: parser_close (GMimeParser *parser)
! 242: {
! 243: if (parser->stream)
! 244: g_mime_stream_unref (parser->stream);
! 245:
! 246: if (parser->headerbuf)
! 247: g_free (parser->headerbuf);
! 248:
! 249: header_raw_clear (&parser->headers);
! 250:
! 251: while (parser->bounds)
! 252: parser_pop_boundary (parser);
! 253: }
! 254:
! 255: static void
! 256: parser_unstep (GMimeParser *parser)
! 257: {
! 258: parser->unstep++;
! 259: }
! 260:
! 261: static ssize_t
! 262: parser_fill (GMimeParser *parser)
! 263: {
! 264: unsigned char *inbuf, *inptr, *inend;
! 265: size_t inlen, atleast = SCAN_HEAD;
! 266: ssize_t nread;
! 267:
! 268: inbuf = parser->inbuf;
! 269: inptr = parser->inptr;
! 270: inend = parser->inend;
! 271: inlen = inend - inptr;
! 272:
! 273: atleast = MAX (atleast, parser->bounds ? parser->bounds->boundarylenmax : 0);
! 274:
! 275: if (inlen > atleast)
! 276: return inlen;
! 277:
! 278: inbuf = parser->realbuf;
! 279:
! 280: memmove (inbuf, inptr, inlen);
! 281: parser->inptr = inbuf;
! 282:
! 283: /* start reading into inbuf */
! 284: inbuf += inlen;
! 285:
! 286: parser->inend = inbuf;
! 287: inend = parser->realbuf + SCAN_HEAD + SCAN_BUF - 1;
! 288:
! 289: nread = g_mime_stream_read (parser->stream, inbuf, inend - inbuf);
! 290: if (nread > 0)
! 291: parser->inend += nread;
! 292: else /* paf */
! 293: return 0;
! 294:
! 295: parser->offset = g_mime_stream_tell (parser->stream);
! 296:
! 297: return parser->inend - parser->inptr;
! 298: }
! 299:
! 300: static off_t
! 301: parser_offset (GMimeParser *parser, unsigned char *cur)
! 302: {
! 303: unsigned char *inptr = cur;
! 304:
! 305: if (!inptr)
! 306: inptr = parser->inptr;
! 307:
! 308: return (parser->offset - (parser->inend - inptr));
! 309: }
! 310:
! 311: #define header_backup(parser, start, len) G_STMT_START { \
! 312: if (parser->headerleft <= len) { \
! 313: unsigned int hlen, hoff; \
! 314: \
! 315: hlen = hoff = parser->headerptr - parser->headerbuf; \
! 316: hlen = hlen ? hlen : 1; \
! 317: \
! 318: while (hlen < hoff + len) \
! 319: hlen <<= 1; \
! 320: \
! 321: parser->headerbuf = g_realloc (parser->headerbuf, hlen + 1); \
! 322: parser->headerptr = parser->headerbuf + hoff; \
! 323: parser->headerleft = hlen - hoff; \
! 324: } \
! 325: \
! 326: memcpy (parser->headerptr, start, len); \
! 327: parser->headerptr += len; \
! 328: parser->headerleft -= len; \
! 329: } G_STMT_END
! 330:
! 331: #define header_parse(parser, hend) G_STMT_START { \
! 332: register unsigned char *colon; \
! 333: struct _header_raw *header; \
! 334: unsigned int hlen; \
! 335: \
! 336: header = g_new (struct _header_raw, 1); \
! 337: header->next = NULL; \
! 338: \
! 339: *parser->headerptr = '\0'; \
! 340: colon = parser->headerbuf; \
! 341: while (*colon && *colon != ':') \
! 342: colon++; \
! 343: \
! 344: hlen = colon - parser->headerbuf; \
! 345: \
! 346: header->name = g_strstrip (g_strndup (parser->headerbuf, hlen)); \
! 347: header->value = g_strstrip (g_strdup (colon + 1)); \
! 348: header->offset = parser->header_start; \
! 349: \
! 350: hend->next = header; \
! 351: hend = header; \
! 352: \
! 353: parser->headerleft += parser->headerptr - parser->headerbuf; \
! 354: parser->headerptr = parser->headerbuf; \
! 355: } G_STMT_END
! 356:
! 357: static int
! 358: parser_step_headers (GMimeParser *parser)
! 359: {
! 360: register unsigned char *inptr;
! 361: unsigned char *start, *inend;
! 362: struct _header_raw *hend;
! 363: size_t len;
! 364:
! 365: parser->midline = FALSE;
! 366: hend = (struct _header_raw *) &parser->headers;
! 367: parser->headers_start = parser_offset (parser, NULL);
! 368: parser->header_start = parser_offset (parser, NULL);
! 369:
! 370: inptr = parser->inptr;
! 371:
! 372: do {
! 373: refill:
! 374: if (parser_fill (parser) <= 0)
! 375: break;
! 376:
! 377: inptr = parser->inptr;
! 378: inend = parser->inend;
! 379: /* Note: see optimization comment [1] */
! 380: *inend = '\n';
! 381:
! 382: g_assert (inptr <= inend);
! 383:
! 384: while (inptr < inend) {
! 385: start = inptr;
! 386: /* Note: see optimization comment [1] */
! 387: while (*inptr != '\n')
! 388: inptr++;
! 389:
! 390: if (inptr + 1 >= inend) {
! 391: /* we don't have enough data to tell if we
! 392: got all of the header or not... */
! 393: parser->inptr = start;
! 394: goto refill;
! 395: }
! 396:
! 397: /* check to see if we've reached the end of the headers */
! 398: if (!parser->midline && inptr == start)
! 399: goto headers_end;
! 400:
! 401: len = inptr - start;
! 402: header_backup (parser, start, len);
! 403:
! 404: if (inptr < inend) {
! 405: /* inptr has to be less than inend - 1 */
! 406: inptr++;
! 407: if (*inptr == ' ' || *inptr == '\t') {
! 408: parser->midline = TRUE;
! 409: } else {
! 410: parser->midline = FALSE;
! 411: header_parse (parser, hend);
! 412: parser->header_start = parser_offset (parser, inptr);
! 413: }
! 414: } else {
! 415: parser->midline = TRUE;
! 416: }
! 417: }
! 418:
! 419: parser->inptr = inptr;
! 420: } while (1);
! 421:
! 422: inptr = parser->inptr;
! 423: inend = parser->inend;
! 424:
! 425: header_backup (parser, inptr, inend - inptr);
! 426: /*header_parse (parser, hend);*/
! 427:
! 428: headers_end:
! 429:
! 430: if (parser->headerptr > parser->headerbuf)
! 431: header_parse (parser, hend);
! 432:
! 433: parser->state = GMIME_PARSER_STATE_HEADERS_END;
! 434:
! 435: g_assert (inptr <= parser->inend);
! 436:
! 437: parser->inptr = inptr;
! 438:
! 439: return 0;
! 440: }
! 441:
! 442: static GMimeContentType *
! 443: parser_content_type (GMimeParser *parser)
! 444: {
! 445: const char *content_type;
! 446:
! 447: content_type = header_raw_find (parser->headers, "Content-Type", NULL);
! 448: if (content_type)
! 449: return g_mime_content_type_new_from_string (content_type);
! 450:
! 451: return NULL;
! 452: }
! 453:
! 454: static int
! 455: parser_step (GMimeParser *parser)
! 456: {
! 457: if (!parser->unstep) {
! 458: switch (parser->state) {
! 459: case GMIME_PARSER_STATE_INIT:
! 460: parser->state = GMIME_PARSER_STATE_HEADERS;
! 461: case GMIME_PARSER_STATE_HEADERS:
! 462: parser_step_headers (parser);
! 463: break;
! 464: default:
! 465: g_assert_not_reached ();
! 466: break;
! 467: }
! 468: } else {
! 469: parser->unstep--;
! 470: }
! 471:
! 472: return parser->state;
! 473: }
! 474:
! 475: static void
! 476: parser_skip_line (GMimeParser *parser)
! 477: {
! 478: register unsigned char *inptr;
! 479: unsigned char *inend;
! 480:
! 481: inptr = parser->inptr;
! 482:
! 483: do {
! 484: if (parser_fill (parser) <= 0) {
! 485: inptr = parser->inptr;
! 486: break;
! 487: }
! 488:
! 489: inptr = parser->inptr;
! 490: inend = parser->inend;
! 491: *inend = '\n';
! 492:
! 493: while (*inptr != '\n')
! 494: inptr++;
! 495:
! 496: if (inptr < inend)
! 497: break;
! 498:
! 499: parser->inptr = inptr;
! 500: } while (1);
! 501:
! 502: parser->midline = FALSE;
! 503:
! 504: parser->inptr = MIN (inptr + 1, parser->inend);
! 505: }
! 506:
! 507: enum {
! 508: FOUND_EOS,
! 509: FOUND_BOUNDARY,
! 510: FOUND_END_BOUNDARY
! 511: };
! 512:
! 513: #define content_save(content, start, len) G_STMT_START { \
! 514: if (content) \
! 515: g_byte_array_append (content, start, len); \
! 516: } G_STMT_END
! 517:
! 518: #define possible_boundary(start, len) (len >= 2 && (start[0] == '-' && start[1] == '-'))
! 519:
! 520: /* Optimization Notes:
! 521: *
! 522: * 1. By making the parser->realbuf char array 1 extra char longer, we
! 523: * can safely set '*inend' to '\n' and not fear an ABW. Setting *inend
! 524: * to '\n' means that we can eliminate having to check that inptr <
! 525: * inend every trip through our inner while-loop. This cuts the number
! 526: * of instructions down from ~7 to ~4, assuming the compiler does its
! 527: * job correctly ;-)
! 528: **/
! 529:
! 530: static int
! 531: parser_scan_content (GMimeParser *parser, GByteArray *content)
! 532: {
! 533: register unsigned char *inptr;
! 534: unsigned char *start, *inend;
! 535: gboolean found_eos = FALSE;
! 536: size_t nleft, len;
! 537: int found;
! 538:
! 539: d(printf ("scan-content\n"));
! 540:
! 541: parser->midline = FALSE;
! 542:
! 543: g_assert (parser->inptr <= parser->inend);
! 544:
! 545: inptr = parser->inptr;
! 546:
! 547: do {
! 548: refill:
! 549: nleft = parser->inend - inptr;
! 550: if (parser_fill (parser) <= 0) {
! 551: start = parser->inptr;
! 552: found = FOUND_EOS;
! 553: break;
! 554: }
! 555:
! 556: inptr = parser->inptr;
! 557: inend = parser->inend;
! 558: /* Note: see optimization comment [1] */
! 559: *inend = '\n';
! 560:
! 561: if (inend - inptr == nleft)
! 562: found_eos = TRUE;
! 563:
! 564: while (inptr < inend) {
! 565: start = inptr;
! 566: /* Note: see optimization comment [1] */
! 567: while (*inptr != '\n')
! 568: inptr++;
! 569:
! 570: len = inptr - start;
! 571:
! 572: if (inptr < inend) {
! 573: inptr++;
! 574: if (possible_boundary (start, len)) {
! 575: struct _boundary_stack *s;
! 576:
! 577: d(printf ("checking boundary '%.*s'\n", len, start));
! 578:
! 579: s = parser->bounds;
! 580: while (s) {
! 581: if (len == s->boundarylenfinal &&
! 582: !strncmp (s->boundary, start,
! 583: s->boundarylenfinal)) {
! 584: found = FOUND_END_BOUNDARY;
! 585: goto boundary;
! 586: }
! 587:
! 588: if (len == s->boundarylen &&
! 589: !strncmp (s->boundary, start,
! 590: s->boundarylen)) {
! 591: found = FOUND_BOUNDARY;
! 592: goto boundary;
! 593: }
! 594:
! 595: s = s->parent;
! 596: }
! 597:
! 598: d(printf ("'%.*s' not a boundary\n", len, start));
! 599: }
! 600: len++;
! 601: } else if (!found_eos) {
! 602: /* not enough to tell if we found a boundary */
! 603: parser->inptr = start;
! 604: goto refill;
! 605: }
! 606:
! 607: content_save (content, start, len);
! 608: }
! 609:
! 610: parser->inptr = inptr;
! 611: } while (1);
! 612:
! 613: boundary:
! 614:
! 615: /* don't chew up the boundary */
! 616: parser->inptr = start;
! 617:
! 618: return found;
! 619: }
! 620:
! 621: static void
! 622: parser_scan_mime_part_content (GMimeParser *parser, GMimePart *mime_part, int *found)
! 623: {
! 624: GMimePartEncodingType encoding;
! 625: GByteArray *content = NULL;
! 626: GMimeDataWrapper *wrapper;
! 627: GMimeStream *stream;
! 628: off_t start, end;
! 629:
! 630: if (parser->seekable)
! 631: start = parser_offset (parser, NULL);
! 632: else
! 633: content = g_byte_array_new ();
! 634:
! 635: *found = parser_scan_content (parser, content);
! 636: if (*found != FOUND_EOS) {
! 637: /* last '\n' belongs to the boundary */
! 638: if (parser->seekable)
! 639: end = parser_offset (parser, NULL) - 1;
! 640: else
! 641: g_byte_array_set_size (content, MAX (content->len - 1, 0));
! 642: } else if (parser->seekable) {
! 643: end = parser_offset (parser, NULL);
! 644: }
! 645:
! 646: encoding = g_mime_part_get_encoding (mime_part);
! 647:
! 648: if (parser->seekable) {
! 649: stream = g_mime_stream_substream (parser->stream, start, end);
! 650: } else {
! 651: stream = g_mime_stream_mem_new_with_byte_array (content);
! 652: }
! 653:
! 654: wrapper = g_mime_data_wrapper_new_with_stream (stream, encoding);
! 655: g_mime_part_set_content_object (mime_part, wrapper);
! 656: g_mime_stream_unref (stream);
! 657: }
! 658:
! 659:
! 660:
! 661: enum {
! 662: CONTENT_TYPE = 0,
! 663: CONTENT_TRANSFER_ENCODING,
! 664: CONTENT_DISPOSITION,
! 665: CONTENT_DESCRIPTION,
! 666: CONTENT_LOCATION,
! 667: CONTENT_MD5,
! 668: CONTENT_ID,
! 669: CONTENT_UNKNOWN
! 670: };
! 671:
! 672: static char *content_headers[] = {
! 673: "Content-Type",
! 674: "Content-Transfer-Encoding",
! 675: "Content-Disposition",
! 676: "Content-Description",
! 677: "Content-Location",
! 678: "Content-Md5",
! 679: "Content-Id",
! 680: NULL
! 681: };
! 682:
! 683:
! 684: static void
! 685: mime_part_set_content_headers (GMimePart *mime_part, struct _header_raw *headers)
! 686: {
! 687: struct _header_raw *header;
! 688:
! 689: header = headers;
! 690: while (header) {
! 691: GMimePartEncodingType encoding;
! 692: GMimeDisposition *disposition;
! 693: char *text;
! 694: int i;
! 695:
! 696: for (i = 0; content_headers[i]; i++)
! 697: if (!strcasecmp (content_headers[i], header->name))
! 698: break;
! 699:
! 700: g_strstrip (header->value);
! 701:
! 702: switch (i) {
! 703: case CONTENT_DESCRIPTION:
! 704: text = g_mime_utils_8bit_header_decode (header->value);
! 705: g_strstrip (text);
! 706: g_mime_part_set_content_description (mime_part, text);
! 707: g_free (text);
! 708: break;
! 709: case CONTENT_LOCATION:
! 710: g_mime_part_set_content_location (mime_part, header->value);
! 711: break;
! 712: case CONTENT_MD5:
! 713: g_mime_part_set_content_md5 (mime_part, header->value);
! 714: break;
! 715: case CONTENT_ID:
! 716: g_mime_part_set_content_id (mime_part, header->value);
! 717: break;
! 718: case CONTENT_TRANSFER_ENCODING:
! 719: encoding = g_mime_part_encoding_from_string (header->value);
! 720: g_mime_part_set_encoding (mime_part, encoding);
! 721: break;
! 722: case CONTENT_TYPE:
! 723: /* no-op, this has already been decoded */
! 724: break;
! 725: case CONTENT_DISPOSITION:
! 726: disposition = g_mime_disposition_new (header->value);
! 727: g_mime_part_set_content_disposition_object (mime_part, disposition);
! 728: break;
! 729: default:
! 730: /* possibly save the header */
! 731: if (!strncasecmp ("Content-", header->name, 8))
! 732: g_mime_part_set_content_header (mime_part, header->name, header->value);
! 733: break;
! 734: }
! 735:
! 736: header = header->next;
! 737: }
! 738: }
! 739:
! 740: static GMimePart *
! 741: parser_construct_leaf_part (GMimeParser *parser, GMimeContentType *content_type, int *found)
! 742: {
! 743: GMimePart *mime_part;
! 744:
! 745: /* get the headers */
! 746: while (parser_step (parser) != GMIME_PARSER_STATE_HEADERS_END)
! 747: ;
! 748:
! 749: if (!content_type) {
! 750: content_type = parser_content_type (parser);
! 751: if (!content_type)
! 752: content_type = g_mime_content_type_new ("text", "plain");
! 753: }
! 754:
! 755: mime_part = g_mime_part_new_with_type (content_type->type, content_type->subtype);
! 756: mime_part_set_content_headers (mime_part, parser->headers);
! 757: header_raw_clear (&parser->headers);
! 758:
! 759: g_mime_part_set_content_type (mime_part, content_type);
! 760:
! 761: /* skip empty line after headers */
! 762: parser_skip_line (parser);
! 763:
! 764: parser_scan_mime_part_content (parser, mime_part, found);
! 765:
! 766: return mime_part;
! 767: }
! 768:
! 769: static int
! 770: parser_scan_multipart_face (GMimeParser *parser, GMimePart *multipart, gboolean preface)
! 771: {
! 772: GByteArray *buffer;
! 773: const char *face;
! 774: int len, found;
! 775:
! 776: buffer = g_byte_array_new ();
! 777: found = parser_scan_content (parser, buffer);
! 778:
! 779: /* last '\n' belongs to the boundary */
! 780: if (buffer->len) {
! 781: buffer->data[buffer->len - 1] = '\0';
! 782: face = buffer->data;
! 783: } else
! 784: face = NULL;
! 785:
! 786: #if 0
! 787: /* FIXME: allow setting of these? */
! 788: if (preface)
! 789: g_mime_part_set_preface (multipart, face);
! 790: else
! 791: g_mime_part_set_postface (multipart, face);
! 792: #endif
! 793:
! 794: g_byte_array_free (buffer, TRUE);
! 795:
! 796: return found;
! 797: }
! 798:
! 799: #define parser_scan_multipart_preface(parser, multipart) parser_scan_multipart_face (parser, multipart, TRUE)
! 800: #define parser_scan_multipart_postface(parser, multipart) parser_scan_multipart_face (parser, multipart, FALSE)
! 801:
! 802: static int
! 803: parser_scan_multipart_subparts (GMimeParser *parser, GMimePart *multipart)
! 804: {
! 805: GMimeContentType *content_type;
! 806: struct _header_raw *header;
! 807: GMimePart *subpart;
! 808: int found;
! 809:
! 810: do {
! 811: /* skip over the boundary marker */
! 812: parser_skip_line (parser);
! 813:
! 814: /* get the headers */
! 815: parser_step_headers (parser);
! 816:
! 817: content_type = parser_content_type (parser);
! 818: if (!content_type)
! 819: content_type = g_mime_content_type_new ("text", "plain");
! 820:
! 821: parser_unstep (parser);
! 822: if (g_mime_content_type_is_type (content_type, "multipart", "*")) {
! 823: subpart = parser_construct_multipart (parser, content_type, &found);
! 824: } else {
! 825: subpart = parser_construct_leaf_part (parser, content_type, &found);
! 826: }
! 827:
! 828: g_mime_part_add_subpart (multipart, subpart);
! 829: g_mime_object_unref (GMIME_OBJECT (subpart));
! 830: } while (found == FOUND_BOUNDARY);
! 831:
! 832: return found;
! 833: }
! 834:
! 835: static GMimePart *
! 836: parser_construct_multipart (GMimeParser *parser, GMimeContentType *content_type, int *found)
! 837: {
! 838: struct _header_raw *header;
! 839: GMimePart *multipart;
! 840: const char *boundary;
! 841:
! 842: /* get the headers */
! 843: while (parser_step (parser) != GMIME_PARSER_STATE_HEADERS_END)
! 844: ;
! 845:
! 846: multipart = g_mime_part_new_with_type (content_type->type, content_type->subtype);
! 847: mime_part_set_content_headers (multipart, parser->headers);
! 848: header_raw_clear (&parser->headers);
! 849:
! 850: g_mime_part_set_content_type (multipart, content_type);
! 851:
! 852: /* skip empty line after headers */
! 853: parser_skip_line (parser);
! 854:
! 855: boundary = g_mime_content_type_get_parameter (content_type, "boundary");
! 856: if (boundary) {
! 857: parser_push_boundary (parser, boundary);
! 858:
! 859: *found = parser_scan_multipart_preface (parser, multipart);
! 860:
! 861: if (*found == FOUND_BOUNDARY)
! 862: *found = parser_scan_multipart_subparts (parser, multipart);
! 863: parser_pop_boundary (parser);
! 864:
! 865: /* eat end boundary */
! 866: parser_skip_line (parser);
! 867:
! 868: if (*found == FOUND_END_BOUNDARY)
! 869: *found = parser_scan_multipart_postface (parser, multipart);
! 870: } else {
! 871: g_warning ("multipart without boundary encountered");
! 872: /* this will scan everything into the preface */
! 873: *found = parser_scan_multipart_preface (parser, multipart);
! 874: }
! 875:
! 876: return multipart;
! 877: }
! 878:
! 879: static GMimePart *
! 880: parser_construct_part (GMimeParser *parser)
! 881: {
! 882: GMimeContentType *content_type;
! 883: GMimePart *part;
! 884: int found;
! 885:
! 886: /* get the headers */
! 887: while (parser_step (parser) != GMIME_PARSER_STATE_HEADERS_END)
! 888: ;
! 889:
! 890: content_type = parser_content_type (parser);
! 891: if (!content_type)
! 892: content_type = g_mime_content_type_new ("text", "plain");
! 893:
! 894: parser_unstep (parser);
! 895: if (g_mime_content_type_is_type (content_type, "multipart", "*")) {
! 896: part = parser_construct_multipart (parser, content_type, &found);
! 897: } else {
! 898: part = parser_construct_leaf_part (parser, content_type, &found);
! 899: }
! 900:
! 901: return part;
! 902: }
! 903:
! 904:
! 905: /**
! 906: * g_mime_parser_construct_part:
! 907: * @stream: raw MIME Part stream
! 908: *
! 909: * Constructs a GMimePart object based on @stream.
! 910: *
! 911: * Returns a GMimePart object based on the data.
! 912: **/
! 913: GMimePart *
! 914: g_mime_parser_construct_part (GMimeStream *stream)
! 915: {
! 916: GMimeParser *parser;
! 917: GMimePart *part;
! 918:
! 919: g_return_val_if_fail (stream != NULL, NULL);
! 920:
! 921: parser = parser_new (stream);
! 922: part = parser_construct_part (parser);
! 923: parser_destroy (parser);
! 924:
! 925: return part;
! 926: }
! 927:
! 928:
! 929:
! 930: static int
! 931: content_header (const char *field)
! 932: {
! 933: int i;
! 934:
! 935: for (i = 0; content_headers[i]; i++)
! 936: if (!strcasecmp (field, content_headers[i]))
! 937: return i;
! 938:
! 939: return -1;
! 940: }
! 941:
! 942: enum {
! 943: HEADER_FROM = 0,
! 944: HEADER_REPLY_TO,
! 945: HEADER_TO,
! 946: HEADER_CC,
! 947: HEADER_BCC,
! 948: HEADER_SUBJECT,
! 949: HEADER_DATE,
! 950: HEADER_MESSAGE_ID,
! 951: HEADER_UNKNOWN
! 952: };
! 953:
! 954: static char *message_headers[] = {
! 955: "From",
! 956: "Reply-To",
! 957: "To",
! 958: "Cc",
! 959: "Bcc",
! 960: "Subject",
! 961: "Date",
! 962: "Message-Id",
! 963: NULL
! 964: };
! 965:
! 966: static gboolean
! 967: special_header (const char *header)
! 968: {
! 969: return (!strcasecmp (header, "MIME-Version") || content_header (header) != -1);
! 970: }
! 971:
! 972:
! 973: static GMimeMessage *
! 974: parser_construct_message (GMimeParser *parser)
! 975: {
! 976: GMimeContentType *content_type;
! 977: struct _header_raw *header;
! 978: GMimeMessage *message;
! 979: int i, offset, found;
! 980: GMimePart *part;
! 981: time_t date;
! 982: char *raw;
! 983:
! 984: /* get the headers */
! 985: while (parser_step (parser) != GMIME_PARSER_STATE_HEADERS_END)
! 986: ;
! 987:
! 988: message = g_mime_message_new (FALSE);
! 989: header = parser->headers;
! 990: while (header) {
! 991: for (i = 0; message_headers[i]; i++)
! 992: if (!strcasecmp (message_headers[i], header->name))
! 993: break;
! 994:
! 995: g_strstrip (header->value);
! 996:
! 997: switch (i) {
! 998: case HEADER_FROM:
! 999: raw = g_mime_utils_8bit_header_decode (header->value);
! 1000: g_mime_message_set_sender (message, raw);
! 1001: g_free (raw);
! 1002: break;
! 1003: case HEADER_REPLY_TO:
! 1004: raw = g_mime_utils_8bit_header_decode (header->value);
! 1005: g_mime_message_set_reply_to (message, raw);
! 1006: g_free (raw);
! 1007: break;
! 1008: case HEADER_TO:
! 1009: g_mime_message_add_recipients_from_string (message, GMIME_RECIPIENT_TYPE_TO,
! 1010: header->value);
! 1011: break;
! 1012: case HEADER_CC:
! 1013: g_mime_message_add_recipients_from_string (message, GMIME_RECIPIENT_TYPE_CC,
! 1014: header->value);
! 1015: break;
! 1016: case HEADER_BCC:
! 1017: g_mime_message_add_recipients_from_string (message, GMIME_RECIPIENT_TYPE_BCC,
! 1018: header->value);
! 1019: break;
! 1020: case HEADER_SUBJECT:
! 1021: raw = g_mime_utils_8bit_header_decode (header->value);
! 1022: g_mime_message_set_subject (message, raw);
! 1023: g_free (raw);
! 1024: break;
! 1025: case HEADER_DATE:
! 1026: date = g_mime_utils_header_decode_date (header->value, &offset);
! 1027: g_mime_message_set_date (message, date, offset);
! 1028: break;
! 1029: case HEADER_MESSAGE_ID:
! 1030: raw = g_mime_utils_8bit_header_decode (header->value);
! 1031: g_mime_message_set_message_id (message, raw);
! 1032: g_free (raw);
! 1033: break;
! 1034: case HEADER_UNKNOWN:
! 1035: default:
! 1036: /* save the raw header if it's not a mime-part header */
! 1037: if (!special_header (header->name)) {
! 1038: g_mime_message_add_header (message, header->name, header->value);
! 1039: }
! 1040: break;
! 1041: }
! 1042:
! 1043: header = header->next;
! 1044: }
! 1045:
! 1046: content_type = parser_content_type (parser);
! 1047: if (!content_type)
! 1048: content_type = g_mime_content_type_new ("text", "plain");
! 1049:
! 1050: parser_unstep (parser);
! 1051: if (content_type && g_mime_content_type_is_type (content_type, "multipart", "*")) {
! 1052: part = parser_construct_multipart (parser, content_type, &found);
! 1053: } else {
! 1054: part = parser_construct_leaf_part (parser, content_type, &found);
! 1055: }
! 1056:
! 1057: g_mime_message_set_mime_part (message, part);
! 1058: g_mime_object_unref (GMIME_OBJECT (part));
! 1059:
! 1060: return message;
! 1061: }
! 1062:
! 1063:
! 1064: /**
! 1065: * g_mime_parser_construct_message:
! 1066: * @stream: an rfc0822 message stream
! 1067: *
! 1068: * Constructs a GMimeMessage object based on @stream.
! 1069: *
! 1070: * Returns a GMimeMessage object based on the rfc0822 message stream.
! 1071: **/
! 1072: GMimeMessage *
! 1073: g_mime_parser_construct_message (GMimeStream *stream)
! 1074: {
! 1075: GMimeMessage *message;
! 1076: GMimeParser *parser;
! 1077:
! 1078: g_return_val_if_fail (stream != NULL, NULL);
! 1079:
! 1080: parser = parser_new (stream);
! 1081: message = parser_construct_message (parser);
! 1082: parser_destroy (parser);
! 1083:
! 1084: return message;
! 1085: }
E-mail: