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: