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