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