Annotation of libwww/Library/src/HTMIME.c, revision 2.57

2.15      frystyk     1: /*                                                                    HTMIME.c
                      2: **     MIME MESSAGE PARSE
                      3: **
2.22      frystyk     4: **     (c) COPYRIGHT MIT 1995.
2.15      frystyk     5: **     Please first read the full copyright statement in the file COPYRIGH.
2.1       timbl       6: **
                      7: **     This is RFC 1341-specific code.
                      8: **     The input stream pushed into this parser is assumed to be
                      9: **     stripped on CRs, ie lines end with LF, not CR LF.
                     10: **     (It is easy to change this except for the body part where
                     11: **     conversion can be slow.)
                     12: **
                     13: ** History:
                     14: **        Feb 92       Written Tim Berners-Lee, CERN
2.13      duns       15: **      8 Jul 94  FM   Insulate free() from _free structure element.
2.18      frystyk    16: **     14 Mar 95  HFN  Now using anchor for storing data. No more `\n',
                     17: **                     static buffers etc.
2.1       timbl      18: */
2.17      frystyk    19: 
                     20: /* Library include files */
2.57    ! frystyk    21: #include "sysdep.h"
2.17      frystyk    22: #include "HTUtils.h"
                     23: #include "HTString.h"
2.9       luotonen   24: #include "HTFormat.h"
2.27      frystyk    25: #include "HTCache.h"
                     26: #include "HTAlert.h"
2.42      frystyk    27: #include "HTAncMan.h"
2.18      frystyk    28: #include "HTChunk.h"
2.26      frystyk    29: #include "HTMethod.h"
2.36      frystyk    30: #include "HTHeader.h"
2.24      frystyk    31: #include "HTSocket.h"
2.39      frystyk    32: #include "HTWWWStr.h"
2.17      frystyk    33: #include "HTFWrite.h"
2.32      frystyk    34: #include "HTNetMan.h"
2.31      frystyk    35: #include "HTReqMan.h"
2.14      frystyk    36: #include "HTMIME.h"                                     /* Implemented here */
2.1       timbl      37: 
                     38: /*             MIME Object
                     39: **             -----------
                     40: */
                     41: typedef enum _MIME_state {
2.23      frystyk    42:     BEGINNING_OF_LINE=0,
2.18      frystyk    43:     CHECK,                             /* check against check_pointer */
                     44:     UNKNOWN,                           /* Unknown header */
                     45:     JUNK_LINE,                         /* Ignore rest of header */
                     46: 
2.32      frystyk    47:     CON,                               /* Intermediate states */
                     48:     CONTENT,
2.45      frystyk    49:     FIRSTLETTER_A,
2.18      frystyk    50:     FIRSTLETTER_D,
                     51:     FIRSTLETTER_L,
                     52:     CONTENTLETTER_L,
                     53:     CONTENTLETTER_T,
                     54: 
2.45      frystyk    55:     ACCEPT_TYPE,                       /* Headers supported */
                     56:     ACCEPT_CHARSET,
                     57:     ACCEPT_ENCODING,
                     58:     ACCEPT_LANGUAGE,
                     59:     ALLOW,
2.18      frystyk    60:     AUTHENTICATE,
2.32      frystyk    61:     CONNECTION,
2.18      frystyk    62:     CONTENT_ENCODING,
                     63:     CONTENT_LANGUAGE,
                     64:     CONTENT_LENGTH,
2.14      frystyk    65:     CONTENT_TRANSFER_ENCODING,
                     66:     CONTENT_TYPE,
2.56      frystyk    67:     MESSAGE_DIGEST,
2.23      frystyk    68:     MIME_DATE,
2.18      frystyk    69:     DERIVED_FROM,
                     70:     EXPIRES,
                     71:     LAST_MODIFIED,
                     72:     LINK,
2.14      frystyk    73:     LOCATION,
2.18      frystyk    74:     PUBLIC_METHODS,
                     75:     RETRY_AFTER,
                     76:     TITLE,
                     77:     URI_HEADER,
                     78:     VERSION
2.1       timbl      79: } MIME_state;
                     80: 
                     81: struct _HTStream {
2.57    ! frystyk    82:     const HTStreamClass *      isa;
2.18      frystyk    83:     HTRequest *                        request;
2.32      frystyk    84:     HTNet *                    net;
                     85:     HTParentAnchor *           anchor;
2.18      frystyk    86:     HTStream *                 target;
                     87:     HTFormat                   target_format;
                     88:     HTChunk *                  buffer;
                     89:     HTSocketEOL                        EOLstate;
                     90:     BOOL                       transparent;
2.48      frystyk    91:     BOOL                       head_only;
2.35      frystyk    92:     BOOL                       nntp;
2.1       timbl      93: };
                     94: 
2.18      frystyk    95: /* ------------------------------------------------------------------------- */
2.1       timbl      96: 
2.18      frystyk    97: /*
2.1       timbl      98: **     This is a FSM parser which is tolerant as it can be of all
                     99: **     syntax errors.  It ignores field names it does not understand,
                    100: **     and resynchronises on line beginnings.
                    101: */
2.36      frystyk   102: PRIVATE int parseheader (HTStream * me, HTRequest * request,
                    103:                         HTParentAnchor * anchor)
2.18      frystyk   104: {
                    105:     MIME_state state = BEGINNING_OF_LINE;
                    106:     MIME_state ok_state;                         /* got this state if match */
                    107:     char *ptr = me->buffer->data-1;     /* We dont change the data in length */
                    108:     char *stop = ptr+me->buffer->size;                      /* When to stop */
                    109:     char *header = ptr;                                  /* For diagnostics */
2.57    ! frystyk   110:     const char * check_pointer;                                   /* checking input */
2.18      frystyk   111:     char *value;
2.27      frystyk   112: 
                    113:     /* In case we get an empty header consisting of a CRLF, we fall thru */
2.18      frystyk   114:     while (ptr < stop) {
                    115:        switch (state) {
                    116:          case BEGINNING_OF_LINE:
                    117:            header = ++ptr;
                    118:            switch (TOLOWER(*ptr)) {
2.50      frystyk   119:              case '\0':
                    120:                state = BEGINNING_OF_LINE;                     /* Empty line */
                    121:                continue;
                    122: 
2.18      frystyk   123:              case 'a':
2.45      frystyk   124:                state = FIRSTLETTER_A;
2.18      frystyk   125:                break;
                    126: 
                    127:              case 'c':
2.32      frystyk   128:                check_pointer = "on";
                    129:                ok_state = CON;
2.18      frystyk   130:                state = CHECK;
                    131:                break;
                    132: 
                    133:              case 'd':
                    134:                state = FIRSTLETTER_D;
                    135:                break;
                    136: 
                    137:              case 'e':
                    138:                check_pointer = "xpires";
                    139:                ok_state = EXPIRES;
                    140:                state = CHECK;
                    141:                break;
                    142: 
2.32      frystyk   143:              case 'k':
2.33      frystyk   144:                check_pointer = "eep-alive";
                    145:                ok_state = JUNK_LINE;  /* We don't use this but recognize it */
                    146:                state = CHECK;
2.32      frystyk   147:                break;
                    148: 
2.18      frystyk   149:              case 'l':
                    150:                state = FIRSTLETTER_L;
                    151:                break;
                    152: 
                    153:              case 'm':
                    154:                check_pointer = "ime-version";
                    155:                ok_state = JUNK_LINE;  /* We don't use this but recognize it */
                    156:                state = CHECK;
                    157:                break;
                    158: 
2.35      frystyk   159:              case 'n':
                    160:                check_pointer = "ewsgroups";
                    161:                me->nntp = YES;                  /* Due to news brain damage */
                    162:                ok_state = JUNK_LINE;  /* We don't use this but recognize it */
                    163:                state = CHECK;
                    164:                break;
                    165: 
2.18      frystyk   166:              case 'r':
                    167:                check_pointer = "etry-after";
                    168:                ok_state = RETRY_AFTER;
                    169:                state = CHECK;
                    170:                break;
                    171: 
                    172:              case 's':
                    173:                check_pointer = "erver";
                    174:                ok_state = JUNK_LINE;  /* We don't use this but recognize it */
                    175:                state = CHECK;
                    176:                break;
2.1       timbl     177: 
2.18      frystyk   178:              case 't':
                    179:                check_pointer = "itle";
                    180:                ok_state = TITLE;
                    181:                state = CHECK;
                    182:                break;
                    183: 
                    184:              case 'u':
                    185:                check_pointer = "ri";
                    186:                ok_state = URI_HEADER;
                    187:                state = CHECK;
                    188:                break;
                    189: 
                    190:              case 'v':
                    191:                check_pointer = "ersion";
                    192:                ok_state = VERSION;
                    193:                state = CHECK;
                    194:                break;
                    195: 
                    196:              case 'w':
                    197:                check_pointer = "ww-authenticate";
                    198:                ok_state = AUTHENTICATE;
                    199:                state = CHECK;
                    200:                break;
2.1       timbl     201: 
2.18      frystyk   202:              default:
                    203:                state = UNKNOWN;
                    204:                break;
                    205:            }
                    206:            ptr++;
2.1       timbl     207:            break;
                    208:        
2.45      frystyk   209:          case FIRSTLETTER_A:
                    210:            if (!strncasecomp(ptr, "llow", 4)) {
                    211:                state = ALLOW;
                    212:                ptr += 4;
                    213:            } else if (!strncasecomp(ptr, "ccept-language", 14)) {
                    214:                state = ACCEPT_LANGUAGE;
                    215:                ptr += 14;
                    216:            } else if (!strncasecomp(ptr, "ccept-charset", 13)) {
                    217:                state = ACCEPT_CHARSET;
                    218:                ptr += 13;
                    219:            } else if (!strncasecomp(ptr, "ccept", 5)) {
                    220:                state = ACCEPT_TYPE;
                    221:                ptr += 5;
                    222:            } else
                    223:                state = UNKNOWN;
                    224:            ptr++;
                    225:            break;
                    226: 
2.18      frystyk   227:          case FIRSTLETTER_D:
                    228:            switch (TOLOWER(*ptr)) {
                    229:              case 'a':
                    230:                check_pointer = "te";
2.23      frystyk   231:                ok_state = MIME_DATE;
2.18      frystyk   232:                state = CHECK;
                    233:                break;
                    234: 
                    235:              case 'e':
                    236:                check_pointer = "rived-from";
                    237:                ok_state = DERIVED_FROM;
                    238:                state = CHECK;
                    239:                break;
                    240: 
2.56      frystyk   241:              case 'i':
                    242:                check_pointer = "gest-MessageDigest";
                    243:                ok_state = MESSAGE_DIGEST;
                    244:                state = CHECK;
                    245:                break;
                    246: 
2.18      frystyk   247:              default:
                    248:                state = UNKNOWN;
                    249:                break;
                    250:            }
                    251:            ptr++;
                    252:            break;
                    253: 
                    254:          case FIRSTLETTER_L:
                    255:            switch (TOLOWER(*ptr)) {
                    256:              case 'a':
                    257:                check_pointer = "st-modified";
                    258:                ok_state = LAST_MODIFIED;
                    259:                state = CHECK;
                    260:                break;
                    261: 
                    262:              case 'i':
                    263:                check_pointer = "nk";
                    264:                ok_state = LINK;
                    265:                state = CHECK;
                    266:                break;
                    267: 
                    268:              case 'o':
                    269:                check_pointer = "cation";
                    270:                ok_state = LOCATION;
                    271:                state = CHECK;
                    272:                break;
                    273: 
                    274:              default:
                    275:                state = UNKNOWN;
                    276:                break;
                    277:            }
                    278:            ptr++;
                    279:            break;
                    280: 
2.32      frystyk   281:          case CON:
                    282:            switch (TOLOWER(*ptr)) {
                    283:              case 'n':
                    284:                check_pointer = "ection";
                    285:                ok_state = CONNECTION;
                    286:                state = CHECK;
                    287:                break;
                    288: 
                    289:              case 't':
                    290:                check_pointer = "ent-";
                    291:                ok_state = CONTENT;
                    292:                state = CHECK;
                    293:                break;
                    294: 
                    295:              default:
                    296:                state = UNKNOWN;
                    297:                break;
                    298:            }
                    299:            ptr++;
                    300:            break;
                    301: 
2.18      frystyk   302:          case CONTENT:
                    303:            switch (TOLOWER(*ptr)) {
                    304:              case 'e':
                    305:                check_pointer = "ncoding";
                    306:                ok_state = CONTENT_ENCODING;
                    307:                state = CHECK;
                    308:                break;
                    309: 
                    310:              case 'l':
                    311:                state = CONTENTLETTER_L;
                    312:                break;
                    313: 
                    314:              case 't':
                    315:                state = CONTENTLETTER_T;
                    316:                break;
                    317: 
                    318:              default:
                    319:                state = UNKNOWN;
                    320:                break;
                    321:            }
                    322:            ptr++;
2.1       timbl     323:            break;
2.14      frystyk   324: 
2.18      frystyk   325:          case CONTENTLETTER_L:
                    326:            switch (TOLOWER(*ptr)) {
                    327:              case 'a':
                    328:                check_pointer = "nguage";
                    329:                ok_state = CONTENT_LANGUAGE;
                    330:                state = CHECK;
                    331:                break;
                    332: 
                    333:              case 'e':
                    334:                check_pointer = "ngth";
                    335:                ok_state = CONTENT_LENGTH;
                    336:                state = CHECK;
                    337:                break;
                    338: 
                    339:              default:
                    340:                state = UNKNOWN;
                    341:                break;
                    342:            }
                    343:            ptr++;
2.14      frystyk   344:            break;
                    345: 
2.18      frystyk   346:          case CONTENTLETTER_T:
                    347:            switch (TOLOWER(*ptr)) {
                    348:              case 'r':
                    349:                check_pointer = "ansfer-encoding";
                    350:                ok_state = CONTENT_TRANSFER_ENCODING;
                    351:                state = CHECK;
                    352:                break;
                    353: 
                    354:              case 'y':
                    355:                check_pointer = "pe";
                    356:                ok_state = CONTENT_TYPE;
                    357:                state = CHECK;
                    358:                break;
                    359: 
                    360:              default:
                    361:                state = UNKNOWN;
                    362:                break;
                    363:            }
                    364:            ptr++;
2.14      frystyk   365:            break;
                    366: 
2.18      frystyk   367:          case CHECK:                                /* Check against string */
2.45      frystyk   368:            while (TOLOWER(*ptr) == *check_pointer) ptr++, check_pointer++;
                    369:            if (!*check_pointer) {
2.18      frystyk   370:                state = ok_state;
                    371:                while (*ptr && (WHITE(*ptr) || *ptr==':')) /* Spool to value */
                    372:                    ptr++;
                    373:            } else
                    374:                state = UNKNOWN;
2.14      frystyk   375:            break;
                    376: 
2.45      frystyk   377:          case ACCEPT_TYPE:                     /* @@@ */
                    378:            state = JUNK_LINE;
                    379:            break;
                    380: 
                    381:          case ACCEPT_CHARSET:                  /* @@@ */
                    382:            state = JUNK_LINE;
                    383:            break;
                    384: 
                    385:          case ACCEPT_ENCODING:                 /* @@@ */
                    386:            state = JUNK_LINE;
                    387:            break;
                    388: 
                    389:          case ACCEPT_LANGUAGE:                 /* @@@ */
                    390:            state = JUNK_LINE;
                    391:            break;
                    392: 
                    393:          case ALLOW:
2.20      frystyk   394:            while ((value = HTNextField(&ptr)) != NULL) {
                    395:                HTMethod new_method;
2.26      frystyk   396:                /* We treat them as case-insensitive! */
2.20      frystyk   397:                if ((new_method = HTMethod_enum(value)) != METHOD_INVALID)
                    398:                    anchor->methods += new_method;
2.1       timbl     399:            }
2.18      frystyk   400:            if (STREAM_TRACE)
2.55      eric      401:                HTTrace("MIMEParser.. Methods allowed: %d\n",
2.18      frystyk   402:                        anchor->methods);
                    403:            state = JUNK_LINE;
2.1       timbl     404:            break;
2.18      frystyk   405: 
                    406:          case AUTHENTICATE:
2.56      frystyk   407:            if (!request->challenge) request->challenge = HTAssocList_new();
                    408: 
                    409:            StrAllocCopy(request->scheme, "basic");
2.20      frystyk   410: 
2.56      frystyk   411:            HTAssocList_add(request->challenge, "WWW-authenticate", ptr);
2.18      frystyk   412:            state = JUNK_LINE;
                    413:            break;
                    414: 
2.32      frystyk   415:          case CONNECTION:
                    416:            if ((value = HTNextField(&ptr)) != NULL) {
                    417:                if (!strcasecomp(value, "keep-alive")) {
                    418:                    if (STREAM_TRACE)
2.55      eric      419:                        HTTrace("MIMEParser.. Persistent Connection\n");
2.50      frystyk   420:                    me->net->persistent = YES;
2.32      frystyk   421:                    HTDNS_setSocket(me->net->dns, me->net->sockfd);
                    422:                }
                    423:            }
                    424:            state = JUNK_LINE;
                    425:            break;
                    426: 
2.18      frystyk   427:          case CONTENT_ENCODING:
                    428:            if ((value = HTNextField(&ptr)) != NULL) {
                    429:                char *lc = value;
2.20      frystyk   430:                while ((*lc = TOLOWER(*lc))) lc++;
2.18      frystyk   431:                anchor->content_encoding = HTAtom_for(value);
                    432:            }
                    433:            state = JUNK_LINE;
                    434:            break;
                    435: 
2.21      frystyk   436:          case CONTENT_LANGUAGE:                 /* @@@ SHOULD BE A LIST @@@ */
                    437:            if ((value = HTNextField(&ptr)) != NULL) {
                    438:                char *lc = value;
                    439:                while ((*lc = TOLOWER(*lc))) lc++;
                    440:                anchor->content_language = HTAtom_for(value);
                    441:            }
                    442:            state = JUNK_LINE;
2.18      frystyk   443:            break;
                    444: 
                    445:          case CONTENT_LENGTH:
                    446:            if ((value = HTNextField(&ptr)) != NULL)
                    447:                anchor->content_length = atol(value);
                    448:            state = JUNK_LINE;
                    449:            break;
                    450: 
                    451:          case CONTENT_TRANSFER_ENCODING:
                    452:            if ((value = HTNextField(&ptr)) != NULL) {
                    453:                char *lc = value;
2.20      frystyk   454:                while ((*lc = TOLOWER(*lc))) lc++;
2.18      frystyk   455:                anchor->cte = HTAtom_for(value);
                    456:            }
                    457:            state = JUNK_LINE;
                    458:            break;
                    459: 
                    460:          case CONTENT_TYPE:
                    461:            if ((value = HTNextField(&ptr)) != NULL) {
                    462:                char *lc = value;
                    463:                while ((*lc = TOLOWER(*lc))) lc++; 
                    464:                anchor->content_type = HTAtom_for(value);
2.38      frystyk   465:                while ((value = HTNextField(&ptr)) != NULL) {
2.20      frystyk   466:                    if (!strcasecomp(value, "charset")) {
                    467:                        if ((value = HTNextField(&ptr)) != NULL) {
                    468:                            lc = value;
                    469:                            while ((*lc = TOLOWER(*lc))) lc++;
                    470:                            anchor->charset = HTAtom_for(value);
                    471:                        }
2.38      frystyk   472:                    } else if (!strcasecomp(value, "level")) {
2.20      frystyk   473:                        if ((value = HTNextField(&ptr)) != NULL) {
                    474:                            lc = value;
                    475:                            while ((*lc = TOLOWER(*lc))) lc++;
                    476:                            anchor->level = HTAtom_for(value);
                    477:                        }
2.38      frystyk   478:                    } else if (!strcasecomp(value, "boundary")) {
                    479:                        if ((value = HTNextField(&ptr)) != NULL) {
                    480:                            StrAllocCopy(request->boundary, value);
                    481:                        }
2.20      frystyk   482:                    }
                    483:                }
2.1       timbl     484:            }
2.20      frystyk   485:            state = JUNK_LINE;
2.18      frystyk   486:            break;
                    487: 
2.56      frystyk   488:          case MESSAGE_DIGEST:
                    489:            if (!request->challenge) request->challenge = HTAssocList_new();
                    490:            HTAssocList_add(request->challenge, "Digest-MessageDigest", ptr);
                    491:            state = JUNK_LINE;
                    492:            break;
                    493: 
2.23      frystyk   494:          case MIME_DATE:
2.18      frystyk   495:            anchor->date = HTParseTime(ptr);
                    496:            state = JUNK_LINE;
                    497:            break;
                    498: 
                    499:          case DERIVED_FROM:
                    500:            if ((value = HTNextField(&ptr)) != NULL)
                    501:                StrAllocCopy(anchor->derived_from, value);
                    502:            state = JUNK_LINE;
                    503:            break;
                    504: 
                    505:          case EXPIRES:
                    506:            anchor->expires = HTParseTime(ptr);
                    507:            state = JUNK_LINE;
                    508:            break;
                    509: 
                    510:          case LAST_MODIFIED:
                    511:            anchor->last_modified = HTParseTime(ptr);
                    512:            state = JUNK_LINE;
                    513:            break;
                    514: 
                    515:          case LINK:
2.20      frystyk   516:            state = UNKNOWN;                            /* @@@@@@@@@@@ */
2.18      frystyk   517:            break;
                    518: 
                    519:          case LOCATION:
2.46      frystyk   520:            request->redirectionAnchor = HTAnchor_findAddress(HTStrip(ptr));
2.18      frystyk   521:            state = JUNK_LINE;
                    522:            break;
                    523: 
                    524:          case PUBLIC_METHODS:
2.20      frystyk   525:            state = UNKNOWN;                            /* @@@@@@@@@@@ */
2.18      frystyk   526:            break;
                    527: 
                    528:          case RETRY_AFTER:
2.19      frystyk   529:            request->retry_after = HTParseTime(ptr);
                    530:            state = JUNK_LINE;
2.18      frystyk   531:            break;
                    532: 
                    533:          case TITLE:     /* Can't reuse buffer as HTML version might differ */
                    534:            if ((value = HTNextField(&ptr)) != NULL)
                    535:                StrAllocCopy(anchor->title, value);
                    536:            state = JUNK_LINE;
                    537:            break;
                    538: 
                    539:          case URI_HEADER:
                    540:            state = LOCATION;                   /* @@@ Need extended parsing */
                    541:            break;
                    542: 
                    543:          case VERSION:
                    544:            if ((value = HTNextField(&ptr)) != NULL)
                    545:                StrAllocCopy(anchor->version, value);
                    546:            state = JUNK_LINE;
                    547:            break;
                    548: 
                    549:          case UNKNOWN:
2.40      frystyk   550:            {
2.36      frystyk   551:                HTList * list;
                    552:                HTParserCallback *cbf;
                    553:                int status;
                    554:                BOOL override;
                    555:                if (STREAM_TRACE)
2.55      eric      556:                    HTTrace("MIMEParser.. Unknown `%s\'\n", header);
2.36      frystyk   557:                if ((list = HTRequest_parser(request, &override)) &&
2.40      frystyk   558:                    (cbf = HTParser_find(list, header)) &&
                    559:                    (status = (*cbf)(request, header) != HT_OK)) {
2.36      frystyk   560:                    return status;
                    561:                } else if (!override &&
                    562:                           (list = HTHeader_parser()) &&
2.40      frystyk   563:                           (cbf = HTParser_find(list, header)) &&
                    564:                           (status = (*cbf)(request, header) != HT_OK)) {
2.36      frystyk   565:                    return status;
                    566:                }
                    567:            }
2.18      frystyk   568: 
                    569:          case JUNK_LINE:
                    570:            while (*ptr) ptr++;
                    571:            state = BEGINNING_OF_LINE;
                    572:            break;
2.1       timbl     573:        }
2.18      frystyk   574:     }
2.48      frystyk   575:     me->transparent = YES;               /* Pump rest of data right through */
2.50      frystyk   576: #if 0
                    577:     HTChunk_clear(me->buffer);                 /* Get ready for next header */
                    578: #endif
2.27      frystyk   579: 
2.48      frystyk   580:     /* If this request us a source in PostWeb then pause here */
                    581:     if (me->head_only || HTRequest_isSource(request)) return HT_PAUSE;
2.47      frystyk   582: 
2.48      frystyk   583:     /* If HEAD method then we just stop here */
2.47      frystyk   584:     if (request->method == METHOD_HEAD) return HT_LOADED;
2.43      frystyk   585: 
2.35      frystyk   586:     /* News server almost never send content type or content length */
                    587:     if (anchor->content_type != WWW_UNKNOWN || me->nntp) {
2.55      eric      588:        if (STREAM_TRACE) HTTrace("MIMEParser.. Convert %s to %s\n",
2.48      frystyk   589:                                   HTAtom_name(anchor->content_type),
                    590:                                   HTAtom_name(me->target_format));
                    591:        me->target = HTStreamStack(anchor->content_type, me->target_format,
                    592:                                   me->target, request, YES);
2.18      frystyk   593:     }
2.49      frystyk   594:     if (!me->target) me->target = HTErrorStream();
2.27      frystyk   595:     return HT_OK;
2.1       timbl     596: }
                    597: 
                    598: 
2.18      frystyk   599: /*
                    600: **     Header is terminated by CRCR, LFLF, CRLFLF, CRLFCRLF
                    601: **     Folding is either of CF LWS, LF LWS, CRLF LWS
                    602: */
2.57    ! frystyk   603: PRIVATE int HTMIME_put_block (HTStream * me, const char * b, int l)
2.18      frystyk   604: {
2.57    ! frystyk   605:     const char * start = b;
        !           606:     const char * end = start;
2.18      frystyk   607:     while (!me->transparent && l-- > 0) {
                    608:        if (me->EOLstate == EOL_FCR) {
2.27      frystyk   609:            if (*b == CR) {                                 /* End of header */
2.53      frystyk   610:                int status;
                    611:                HTChunk_putb(me->buffer, start, end-start);
                    612:                status = parseheader(me, me->request, me->anchor);
2.46      frystyk   613:                HTNet_setBytesRead(me->net, l);
2.27      frystyk   614:                if (status != HT_OK)
                    615:                    return status;
                    616:            } else if (*b == LF)                                     /* CRLF */
2.18      frystyk   617:                me->EOLstate = EOL_FLF;
                    618:            else if (WHITE(*b)) {                          /* Folding: CR SP */
                    619:                me->EOLstate = EOL_BEGIN;
2.53      frystyk   620:                HTChunk_putb(me->buffer, start, end-start);
2.44      frystyk   621:                HTChunk_putc(me->buffer, ' ');
2.53      frystyk   622:                start=b, end=b+1;
2.18      frystyk   623:            } else {                                             /* New line */
                    624:                me->EOLstate = EOL_BEGIN;
2.53      frystyk   625:                HTChunk_putb(me->buffer, start, end-start);
2.44      frystyk   626:                HTChunk_putc(me->buffer, '\0');
2.53      frystyk   627:                start=b, end=b+1;
2.18      frystyk   628:            }
                    629:        } else if (me->EOLstate == EOL_FLF) {
                    630:            if (*b == CR)                               /* LF CR or CR LF CR */
                    631:                me->EOLstate = EOL_SCR;
2.27      frystyk   632:            else if (*b == LF) {                            /* End of header */
2.53      frystyk   633:                int status;
                    634:                HTChunk_putb(me->buffer, start, end-start);
2.54      frystyk   635:                status = parseheader(me, me->request, me->anchor);
2.46      frystyk   636:                HTNet_setBytesRead(me->net, l);
2.27      frystyk   637:                if (status != HT_OK)
                    638:                    return status;
                    639:            } else if (WHITE(*b)) {            /* Folding: LF SP or CR LF SP */
2.18      frystyk   640:                me->EOLstate = EOL_BEGIN;
2.53      frystyk   641:                HTChunk_putb(me->buffer, start, end-start);
2.44      frystyk   642:                HTChunk_putc(me->buffer, ' ');
2.53      frystyk   643:                start=b, end=b+1;
2.18      frystyk   644:            } else {                                            /* New line */
                    645:                me->EOLstate = EOL_BEGIN;
2.53      frystyk   646:                HTChunk_putb(me->buffer, start, end-start);
2.44      frystyk   647:                HTChunk_putc(me->buffer, '\0');
2.53      frystyk   648:                start=b, end=b+1;
2.18      frystyk   649:            }
                    650:        } else if (me->EOLstate == EOL_SCR) {
2.27      frystyk   651:            if (*b==CR || *b==LF) {                         /* End of header */
2.53      frystyk   652:                int status;
                    653:                HTChunk_putb(me->buffer, start, end-start);
                    654:                status = parseheader(me, me->request, me->anchor);
2.46      frystyk   655:                HTNet_setBytesRead(me->net, l);
2.27      frystyk   656:                if (status != HT_OK)
                    657:                    return status;
                    658:            } else if (WHITE(*b)) {      /* Folding: LF CR SP or CR LF CR SP */
2.18      frystyk   659:                me->EOLstate = EOL_BEGIN;
2.53      frystyk   660:                HTChunk_putb(me->buffer, start, end-start);
2.44      frystyk   661:                HTChunk_putc(me->buffer, ' ');
2.53      frystyk   662:                start=b, end=b+1;
2.18      frystyk   663:            } else {                                            /* New line */
                    664:                me->EOLstate = EOL_BEGIN;
2.53      frystyk   665:                HTChunk_putb(me->buffer, start, end-start);
2.44      frystyk   666:                HTChunk_putc(me->buffer, '\0');
2.53      frystyk   667:                start=b, end=b+1;
2.18      frystyk   668:            }
                    669:        } else if (*b == CR) {
                    670:            me->EOLstate = EOL_FCR;
                    671:        } else if (*b == LF) {
                    672:            me->EOLstate = EOL_FLF;                            /* Line found */
                    673:        } else
2.53      frystyk   674:            end++;
2.18      frystyk   675:        b++;
                    676:     }
2.32      frystyk   677: 
                    678:     /* 
                    679:     ** Put the rest down the stream without touching the data but make sure
                    680:     ** that we get the correct content length of data
                    681:     */
2.48      frystyk   682:     if (me->transparent) {
2.47      frystyk   683:        int status = (*me->target->isa->put_block)(me->target, b, l);
2.48      frystyk   684:        if (status == HT_OK) {
2.47      frystyk   685:            /* Check if CL at all - thanks to jwei@hal.com (John Wei) */
2.48      frystyk   686:            long cl = HTAnchor_length(me->anchor);
                    687:            return (cl>=0 && HTNet_bytesRead(me->net)>=cl) ? HT_LOADED : HT_OK;
                    688:        } else
2.47      frystyk   689:            return status;
2.48      frystyk   690:     }
                    691:     return HT_OK;
2.18      frystyk   692: }
                    693: 
                    694: 
                    695: /*     Character handling
                    696: **     ------------------
                    697: */
2.36      frystyk   698: PRIVATE int HTMIME_put_character (HTStream * me, char c)
2.18      frystyk   699: {
                    700:     return HTMIME_put_block(me, &c, 1);
                    701: }
                    702: 
2.1       timbl     703: 
                    704: /*     String handling
                    705: **     ---------------
                    706: */
2.57    ! frystyk   707: PRIVATE int HTMIME_put_string (HTStream * me, const char * s)
2.1       timbl     708: {
2.18      frystyk   709:     return HTMIME_put_block(me, s, (int) strlen(s));
2.1       timbl     710: }
                    711: 
                    712: 
2.18      frystyk   713: /*     Flush an stream object
                    714: **     ---------------------
2.1       timbl     715: */
2.36      frystyk   716: PRIVATE int HTMIME_flush (HTStream * me)
2.1       timbl     717: {
2.47      frystyk   718:     return me->target ? (*me->target->isa->flush)(me->target) : HT_OK;
2.1       timbl     719: }
                    720: 
2.18      frystyk   721: /*     Free a stream object
                    722: **     --------------------
2.1       timbl     723: */
2.36      frystyk   724: PRIVATE int HTMIME_free (HTStream * me)
2.1       timbl     725: {
2.18      frystyk   726:     int status = HT_OK;
2.51      frystyk   727:     if (!me->transparent) parseheader(me, me->request, me->anchor);
2.25      frystyk   728:     if (me->target) {
                    729:        if ((status = (*me->target->isa->_free)(me->target))==HT_WOULD_BLOCK)
                    730:            return HT_WOULD_BLOCK;
                    731:     }
2.26      frystyk   732:     if (PROT_TRACE)
2.55      eric      733:        HTTrace("MIME........ FREEING....\n");
2.44      frystyk   734:     HTChunk_delete(me->buffer);
2.52      frystyk   735:     HT_FREE(me);
2.18      frystyk   736:     return status;
2.1       timbl     737: }
                    738: 
                    739: /*     End writing
                    740: */
2.38      frystyk   741: PRIVATE int HTMIME_abort (HTStream * me, HTList * e)
2.1       timbl     742: {
2.18      frystyk   743:     int status = HT_ERROR;
2.41      frystyk   744:     if (me->target) status = (*me->target->isa->abort)(me->target, e);
2.26      frystyk   745:     if (PROT_TRACE)
2.55      eric      746:        HTTrace("MIME........ ABORTING...\n");
2.44      frystyk   747:     HTChunk_delete(me->buffer);
2.52      frystyk   748:     HT_FREE(me);
2.18      frystyk   749:     return status;
2.1       timbl     750: }
                    751: 
                    752: 
                    753: 
                    754: /*     Structured Object Class
                    755: **     -----------------------
                    756: */
2.57    ! frystyk   757: PRIVATE const HTStreamClass HTMIME =
2.1       timbl     758: {              
                    759:        "MIMEParser",
2.18      frystyk   760:        HTMIME_flush,
2.1       timbl     761:        HTMIME_free,
2.6       timbl     762:        HTMIME_abort,
                    763:        HTMIME_put_character,
                    764:        HTMIME_put_string,
2.18      frystyk   765:        HTMIME_put_block
2.1       timbl     766: }; 
                    767: 
                    768: 
2.48      frystyk   769: /*     MIME header parser stream.
2.1       timbl     770: **     -------------------------
2.48      frystyk   771: **     This stream parses a complete MIME header and if a content type header
                    772: **     is found then the stream stack is called. Any left over data is pumped
                    773: **     right through the stream
2.1       timbl     774: */
2.36      frystyk   775: PUBLIC HTStream* HTMIMEConvert (HTRequest *    request,
                    776:                                void *          param,
                    777:                                HTFormat        input_format,
                    778:                                HTFormat        output_format,
                    779:                                HTStream *      output_stream)
2.1       timbl     780: {
                    781:     HTStream* me;
2.52      frystyk   782:     if ((me = (HTStream *) HT_CALLOC(1, sizeof(* me))) == NULL)
                    783:         HT_OUTOFMEM("HTMIMEConvert");
2.1       timbl     784:     me->isa = &HTMIME;       
2.18      frystyk   785:     me->request = request;
2.32      frystyk   786:     me->anchor = request->anchor;
                    787:     me->net = request->net;
2.49      frystyk   788:     me->target = output_stream;
2.18      frystyk   789:     me->target_format = output_format;
2.44      frystyk   790:     me->buffer = HTChunk_new(512);
2.18      frystyk   791:     me->EOLstate = EOL_BEGIN;
2.1       timbl     792:     return me;
                    793: }
2.32      frystyk   794: 
2.48      frystyk   795: /*     MIME header ONLY parser stream
                    796: **     ------------------------------
                    797: **     This stream parses a complete MIME header and then returnes HT_PAUSE.
                    798: **     It does not set up any streams and resting data stays in the buffer.
                    799: **     This can be used if you only want to parse the headers before you
                    800: **     decide what to do next. This is for example the case in a server app.
                    801: */
                    802: PUBLIC HTStream * HTMIMEHeader (HTRequest *    request,
                    803:                                void *          param,
                    804:                                HTFormat        input_format,
                    805:                                HTFormat        output_format,
                    806:                                HTStream *      output_stream)
                    807: {
                    808:     HTStream * me;
2.52      frystyk   809:     if ((me = (HTStream *) HT_CALLOC(1, sizeof(HTStream))) == NULL)
                    810:         HT_OUTOFMEM("HTMIMEConvert");
2.48      frystyk   811:     me->isa = &HTMIME;       
                    812:     me->request = request;
                    813:     me->anchor = request->anchor;
                    814:     me->net = request->net;
2.49      frystyk   815:     me->target = output_stream;
2.48      frystyk   816:     me->target_format = output_format;
                    817:     me->buffer = HTChunk_new(512);
                    818:     me->EOLstate = EOL_BEGIN;
                    819:     me->head_only = YES;                   /* We want to pause after header */
                    820:     return me;
                    821: }

Webmaster