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