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