Annotation of libwww/Library/src/HTMIME.c, revision 2.94
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.94 ! frystyk 6: ** @(#) $Id: HTMIME.c,v 2.93 1999/02/07 18:29:05 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.71 frystyk 17: ** 14 Mar 95 HFN Now using response for storing data. No more `\n',
2.18 frystyk 18: ** static buffers etc.
2.1 timbl 19: */
2.17 frystyk 20:
21: /* Library include files */
2.86 frystyk 22: #include "wwwsys.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.64 eric 28: #include "HTWWWStr.h"
2.93 frystyk 29:
30: #ifndef NO_CACHE
31: #include "HTTee.h"
32: #include "HTConLen.h"
33: #include "HTMerge.h"
34: #include "WWWCache.h"
35: #endif
36:
2.14 frystyk 37: #include "HTMIME.h" /* Implemented here */
2.1 timbl 38:
2.70 frystyk 39: typedef enum _HTMIMEMode {
40: HT_MIME_HEADER = 0x1,
2.71 frystyk 41: HT_MIME_FOOTER = 0x2,
2.77 frystyk 42: HT_MIME_PARTIAL = 0x4,
43: HT_MIME_CONT = 0x8
2.70 frystyk 44: } HTMIMEMode;
45:
2.1 timbl 46: struct _HTStream {
2.57 frystyk 47: const HTStreamClass * isa;
2.18 frystyk 48: HTRequest * request;
2.71 frystyk 49: HTResponse * response;
2.32 frystyk 50: HTNet * net;
2.18 frystyk 51: HTStream * target;
2.93 frystyk 52: HTConverter * save_stream;
2.18 frystyk 53: HTFormat target_format;
2.64 eric 54: HTChunk * token;
55: HTChunk * value;
56: int hash;
2.59 frystyk 57: HTEOLState EOLstate;
2.70 frystyk 58: HTMIMEMode mode;
2.18 frystyk 59: BOOL transparent;
2.64 eric 60: BOOL haveToken;
2.78 frystyk 61: BOOL hasBody;
2.1 timbl 62: };
63:
2.93 frystyk 64: PRIVATE HTConverter * LocalSaveStream = NULL; /* Where to save unknown stuff */
65:
2.18 frystyk 66: /* ------------------------------------------------------------------------- */
2.1 timbl 67:
2.64 eric 68: PRIVATE int pumpData (HTStream * me)
2.18 frystyk 69: {
2.64 eric 70: HTRequest * request = me->request;
2.71 frystyk 71: HTResponse * response = me->response;
72: HTFormat format = HTResponse_format(response);
2.83 frystyk 73: HTList * te = HTResponse_transfer(response);
74: HTList * ce = HTResponse_encoding(response);
2.71 frystyk 75: long length = HTResponse_length(response);
2.83 frystyk 76: HTStream * BlackHole = HTBlackHole();
77: BOOL savestream = NO;
2.48 frystyk 78: me->transparent = YES; /* Pump rest of data right through */
2.47 frystyk 79:
2.71 frystyk 80: /*
2.77 frystyk 81: ** Cache the metainformation in the anchor object by copying
2.71 frystyk 82: ** it from the response object. This we do regardless if
83: ** we have a persistent cache or not as the memory cache will
84: ** use it as well. If we are updating a cache entry using
2.77 frystyk 85: ** byte ranges then we already have the metainformation and
2.71 frystyk 86: ** hence we can ignore the new one as it'd better be the same.
87: */
2.90 frystyk 88: if (!(me->mode & HT_MIME_PARTIAL) &&
89: HTResponse_isCachable(me->response) != HT_NO_CACHE)
2.71 frystyk 90: HTAnchor_update(HTRequest_anchor(request), me->response);
91:
92: /*
93: ** If we asked only to read the header or footer or we used a HEAD
94: ** method then we stop here as we don't expect any body part.
95: */
2.70 frystyk 96: if (me->mode & (HT_MIME_HEADER | HT_MIME_FOOTER) ||
2.71 frystyk 97: HTRequest_method(request) == METHOD_HEAD) {
2.88 frystyk 98: HTAlertCallback * cbf = HTAlert_find(HT_PROG_DONE);
99: if (cbf) (*cbf)(request, HT_PROG_DONE, HT_MSG_NULL, NULL, NULL, NULL);
2.78 frystyk 100: return HT_LOADED;
2.70 frystyk 101: }
2.43 frystyk 102:
2.60 frystyk 103: /*
2.77 frystyk 104: ** If we are paring a 1xx response then return HT_CONTINUE
105: */
106: if (me->mode & HT_MIME_CONT)
107: return HT_CONTINUE;
108:
109: /*
2.71 frystyk 110: ** If there is no content-length, no transfer encoding and no
111: ** content type then we assume that there is no body part in
112: ** the message and we can return HT_LOADED
2.68 frystyk 113: */
2.79 frystyk 114: {
115: HTHost * host = HTNet_host(me->net);
2.84 frystyk 116: if (length<0 && te==NULL &&
2.79 frystyk 117: HTHost_isPersistent(host) && !HTHost_closeNotification(host)) {
2.81 frystyk 118: if (format != WWW_UNKNOWN) {
2.94 ! frystyk 119: HTTRACE(STREAM_TRACE, "MIME Parser. BAD - there seems to be a body but no length. This must be an HTTP/1.0 server pretending that it is HTTP/1.1\n");
2.81 frystyk 120: HTHost_setCloseNotification(host, YES);
121: } else {
2.88 frystyk 122: HTAlertCallback * cbf = HTAlert_find(HT_PROG_DONE);
123: if (cbf) (*cbf)(request, HT_PROG_DONE, HT_MSG_NULL, NULL, NULL, NULL);
2.94 ! frystyk 124: HTTRACE(STREAM_TRACE, "MIME Parser. No body in this messsage\n");
2.81 frystyk 125: return HT_LOADED;
126: }
2.79 frystyk 127: }
2.68 frystyk 128: }
129:
130: /*
2.78 frystyk 131: ** Deal with the body
132: */
133: me->hasBody = YES;
134:
135: /*
2.71 frystyk 136: ** Handle any Content Type
2.60 frystyk 137: */
2.71 frystyk 138: if (!(me->mode & HT_MIME_PARTIAL) &&
2.83 frystyk 139: (format != WWW_UNKNOWN || length > 0 || te)) {
140: HTStream * target;
2.94 ! frystyk 141: HTTRACE(STREAM_TRACE, "Building.... C-T stack from %s to %s\n" _
! 142: HTAtom_name(format) _
2.71 frystyk 143: HTAtom_name(me->target_format));
2.83 frystyk 144: if ((target = HTStreamStack(format, me->target_format,
145: me->target, request, YES))==BlackHole) {
146: if (!savestream) {
2.90 frystyk 147: if (me->target) (*me->target->isa->abort)(me->target, NULL);
2.93 frystyk 148: me->target = me->save_stream(request, NULL,
149: format, me->target_format, me->target);
2.90 frystyk 150: savestream = YES;
151: }
152: } else
153: me->target = target;
154: }
155:
156: /*
157: ** Handle any Content Encodings
158: */
2.94 ! frystyk 159: HTTRACE(STREAM_TRACE, "Building.... Content-Decoding stack\n");
2.90 frystyk 160: if (ce) {
161: HTStream * target = HTContentDecodingStack(ce, me->target, request, NULL);
162: if (target == BlackHole) {
163: if (!savestream) {
2.83 frystyk 164: if (me->target) (*me->target->isa->abort)(me->target, NULL);
2.93 frystyk 165: me->target = me->save_stream(request, NULL,
166: format, me->target_format, me->target);
2.83 frystyk 167: savestream = YES;
168: }
169: } else
170: me->target = target;
2.18 frystyk 171: }
2.60 frystyk 172:
2.71 frystyk 173: /*
174: ** Can we cache the data object? If so then create a T stream and hook it
175: ** into the stream pipe. We do it before the transfer decoding so that we
176: ** don't have to deal with that when we retrieve the object from cache.
177: ** If we are appending to a cache entry then use a different stream than
178: ** if creating a new entry.
179: */
2.93 frystyk 180: #ifndef NO_CACHE
2.90 frystyk 181: if (HTCacheMode_enabled()) {
2.71 frystyk 182: if (me->mode & HT_MIME_PARTIAL) {
183: HTStream * append = HTStreamStack(WWW_CACHE_APPEND,
184: me->target_format,
185: me->target, request, NO);
2.90 frystyk 186: me->target = append;
187: } else if (HTResponse_isCachable(me->response) == HT_CACHE_ALL) {
2.71 frystyk 188: HTStream * cache = HTStreamStack(WWW_CACHE, me->target_format,
189: me->target, request, NO);
190: if (cache) me->target = HTTee(me->target, cache, NULL);
191: }
2.70 frystyk 192: }
2.93 frystyk 193: #endif
2.90 frystyk 194:
2.71 frystyk 195: /*
2.83 frystyk 196: ** Handle any Transfer Encodings
2.71 frystyk 197: */
2.94 ! frystyk 198: HTTRACE(STREAM_TRACE, "Building.... Transfer-Decoding stack\n");
2.83 frystyk 199: if (te) {
200: HTStream * target = HTTransferDecodingStack(te, me->target, request, NULL);
201: if (target == BlackHole) {
202: if (!savestream) {
203: if (me->target) (*me->target->isa->abort)(me->target, NULL);
2.93 frystyk 204: me->target = me->save_stream(request, NULL,
205: format, me->target_format, me->target);
2.83 frystyk 206: savestream = YES;
207: }
208: } else
209: me->target = target;
2.61 frystyk 210: }
2.71 frystyk 211:
2.85 frystyk 212:
213: /*
214: ** If we for some reason couldn't find a target stream
215: */
216: if (!me->target) me->target = HTBlackHole();
2.27 frystyk 217: return HT_OK;
2.1 timbl 218: }
219:
2.65 eric 220: /* _dispatchParsers - call request's MIME header parser.
221: ** Use global parser if no appropriate one is found for request.
222: */
223: PRIVATE int _dispatchParsers (HTStream * me)
224: {
225: int status;
226: char * token = HTChunk_data(me->token);
227: char * value = HTChunk_data(me->value);
2.71 frystyk 228: BOOL found = NO;
229: BOOL local = NO;
2.65 eric 230: HTMIMEParseSet * parseSet;
231:
232: /* In case we get an empty header consisting of a CRLF, we fall thru */
2.94 ! frystyk 233: HTTRACE(STREAM_TRACE, "MIME header. %s: %s\n" _
! 234: token ? token : "<null>" _
2.66 frystyk 235: value ? value : "<null>");
236: if (!token) return HT_OK; /* Ignore noop token */
2.65 eric 237:
2.70 frystyk 238: /*
2.71 frystyk 239: ** Remember the original header
240: */
241: HTResponse_addHeader(me->response, token, value);
242:
243: /*
2.70 frystyk 244: ** Search the local set of MIME parsers
245: */
2.65 eric 246: if ((parseSet = HTRequest_MIMEParseSet(me->request, &local)) != NULL) {
247: status = HTMIMEParseSet_dispatch(parseSet, me->request,
2.71 frystyk 248: token, value, &found);
249: if (found) return status;
2.65 eric 250: }
251:
2.70 frystyk 252: /*
253: ** Search the global set of MIME parsers
254: */
2.71 frystyk 255: if (local==NO && (parseSet = HTHeader_MIMEParseSet()) != NULL) {
256: status = HTMIMEParseSet_dispatch(parseSet, me->request,
257: token, value, &found);
258: if (found) return status;
259: }
260:
2.65 eric 261: return HT_OK;
262: }
263:
2.18 frystyk 264: /*
265: ** Header is terminated by CRCR, LFLF, CRLFLF, CRLFCRLF
266: ** Folding is either of CF LWS, LF LWS, CRLF LWS
267: */
2.57 frystyk 268: PRIVATE int HTMIME_put_block (HTStream * me, const char * b, int l)
2.18 frystyk 269: {
2.57 frystyk 270: const char * start = b;
271: const char * end = start;
2.92 frystyk 272: const char * value = HTChunk_size(me->value) >= 0 ? b : NULL;
2.73 frystyk 273: int length = l;
2.64 eric 274: int status;
275:
276: while (!me->transparent) {
2.18 frystyk 277: if (me->EOLstate == EOL_FCR) {
2.64 eric 278: if (*b == CR) /* End of header */
279: me->EOLstate = EOL_END;
280: else if (*b == LF) /* CRLF */
2.18 frystyk 281: me->EOLstate = EOL_FLF;
2.80 frystyk 282: else if (isspace((int) *b)) /* Folding: CR SP */
2.64 eric 283: me->EOLstate = EOL_FOLD;
284: else /* New line */
285: me->EOLstate = EOL_LINE;
2.18 frystyk 286: } else if (me->EOLstate == EOL_FLF) {
287: if (*b == CR) /* LF CR or CR LF CR */
288: me->EOLstate = EOL_SCR;
2.64 eric 289: else if (*b == LF) /* End of header */
290: me->EOLstate = EOL_END;
2.80 frystyk 291: else if (isspace((int) *b)) /* Folding: LF SP or CR LF SP */
2.64 eric 292: me->EOLstate = EOL_FOLD;
293: else /* New line */
294: me->EOLstate = EOL_LINE;
295: } else if (me->EOLstate == EOL_SCR) {
296: if (*b==CR || *b==LF) /* End of header */
297: me->EOLstate = EOL_END;
2.80 frystyk 298: else if (isspace((int) *b)) /* Folding: LF CR SP or CR LF CR SP */
2.64 eric 299: me->EOLstate = EOL_FOLD;
300: else /* New line */
301: me->EOLstate = EOL_LINE;
302: } else if (*b == CR)
303: me->EOLstate = EOL_FCR;
304: else if (*b == LF)
305: me->EOLstate = EOL_FLF; /* Line found */
306: else {
307: if (!me->haveToken) {
2.80 frystyk 308: if (*b == ':' || isspace((int) *b)) {
2.64 eric 309: HTChunk_putb(me->token, start, end-start);
310: HTChunk_putc(me->token, '\0');
311: me->haveToken = YES;
312: } else {
313: unsigned char ch = *(unsigned char *) b;
2.92 frystyk 314: ch = TOLOWER(ch);
2.64 eric 315: me->hash = (me->hash * 3 + ch) % MIME_HASH_SIZE;
316: }
2.80 frystyk 317: } else if (value == NULL && *b != ':' && !isspace((int) *b))
2.64 eric 318: value = b;
319: end++;
320: }
321: switch (me->EOLstate) {
322: case EOL_LINE:
2.73 frystyk 323: case EOL_END:
324: {
2.78 frystyk 325: int ret = HT_ERROR;
2.64 eric 326: HTChunk_putb(me->value, value, end-value);
327: HTChunk_putc(me->value, '\0');
2.75 frystyk 328: ret = _dispatchParsers(me);
2.73 frystyk 329: HTNet_addBytesRead(me->net, b-start);
2.64 eric 330: start=b, end=b;
331: if (me->EOLstate == EOL_END) { /* EOL_END */
2.75 frystyk 332: if (ret == HT_OK) {
2.67 frystyk 333: b++, l--;
2.78 frystyk 334: ret = pumpData(me);
2.73 frystyk 335: HTNet_addBytesRead(me->net, 1);
2.82 frystyk 336: if (me->mode & (HT_MIME_FOOTER | HT_MIME_CONT)) {
2.73 frystyk 337: HTHost_setConsumed(HTNet_host(me->net), length - l);
2.75 frystyk 338: return ret;
2.78 frystyk 339: } else {
2.89 frystyk 340: HTNet_setHeaderBytesRead(me->net, HTNet_bytesRead(me->net));
2.78 frystyk 341: }
2.67 frystyk 342: }
2.64 eric 343: } else { /* EOL_LINE */
344: HTChunk_clear(me->token);
345: HTChunk_clear(me->value);
346: me->haveToken = NO;
347: me->hash = 0;
348: value = NULL;
349: }
2.18 frystyk 350: me->EOLstate = EOL_BEGIN;
2.78 frystyk 351: if (ret != HT_OK && ret != HT_LOADED) return ret;
2.64 eric 352: break;
2.73 frystyk 353: }
2.64 eric 354: case EOL_FOLD:
2.18 frystyk 355: me->EOLstate = EOL_BEGIN;
2.64 eric 356: if (!me->haveToken) {
357: HTChunk_putb(me->token, start, end-start);
358: HTChunk_putc(me->token, '\0');
359: me->haveToken = YES;
360: } else if (value) {
361: HTChunk_putb(me->value, value, end-value);
362: HTChunk_putc(me->value, ' ');
363: }
364: start=b, end=b;
365: break;
366: default:
2.73 frystyk 367: b++, l--;
2.64 eric 368: if (!l) {
2.87 frystyk 369: BOOL stop = NO;
370: if (!me->haveToken) {
371: /* If empty header then prepare to stop */
372: if (end-start)
373: HTChunk_putb(me->token, start, end-start);
374: else
375: stop = YES;
376: } else if (value)
2.64 eric 377: HTChunk_putb(me->value, value, end-value);
2.78 frystyk 378: HTHost_setConsumed(HTNet_host(me->net), length - l);
2.87 frystyk 379: return stop ? pumpData(me) : HT_OK;
2.64 eric 380: }
381: }
2.18 frystyk 382: }
2.32 frystyk 383:
2.78 frystyk 384: if (length != l) HTHost_setConsumed(HTNet_host(me->net), length - l);
385:
2.32 frystyk 386: /*
387: ** Put the rest down the stream without touching the data but make sure
2.73 frystyk 388: ** that we get the correct content length of data. If we have a CL in
389: ** the headers then this stream is responsible for the accountance.
2.32 frystyk 390: */
2.82 frystyk 391: if (me->hasBody) {
2.73 frystyk 392: HTNet * net = me->net;
2.66 frystyk 393: /* Check if CL at all - thanks to jwei@hal.com (John Wei) */
2.73 frystyk 394: long cl = HTResponse_length(me->response);
2.82 frystyk 395: if (cl >= 0) {
2.89 frystyk 396: long bodyRead = HTNet_bytesRead(net) - HTNet_headerBytesRead(net);
2.73 frystyk 397:
398: /*
399: ** If we have more than we need then just take what belongs to us.
400: */
401: if (bodyRead + l >= cl) {
402: int consume = cl - bodyRead;
403: if ((status = (*me->target->isa->put_block)(me->target, b, consume)) < 0)
404: return status;
2.88 frystyk 405: else {
406: HTAlertCallback * cbf = HTAlert_find(HT_PROG_DONE);
407: HTNet_addBytesRead(net, consume);
408: HTHost_setConsumed(HTNet_host(net), consume);
409: if (cbf) (*cbf)(me->request, HT_PROG_DONE, HT_MSG_NULL, NULL, NULL, NULL);
410: return HT_LOADED;
411: }
412: } else {
2.73 frystyk 413: if ((status = (*me->target->isa->put_block)(me->target, b, l)) < 0)
2.78 frystyk 414: return status;
2.73 frystyk 415: HTNet_addBytesRead(net, l);
2.78 frystyk 416: HTHost_setConsumed(HTNet_host(net), l);
2.73 frystyk 417: return status;
418: }
2.78 frystyk 419: }
2.73 frystyk 420: return (*me->target->isa->put_block)(me->target, b, l);
2.88 frystyk 421: } else {
422: HTAlertCallback * cbf = HTAlert_find(HT_PROG_DONE);
423: if (cbf) (*cbf)(me->request, HT_PROG_DONE, HT_MSG_NULL, NULL, NULL, NULL);
2.66 frystyk 424: }
425: return HT_LOADED;
2.18 frystyk 426: }
427:
428:
429: /* Character handling
430: ** ------------------
431: */
2.36 frystyk 432: PRIVATE int HTMIME_put_character (HTStream * me, char c)
2.18 frystyk 433: {
434: return HTMIME_put_block(me, &c, 1);
435: }
436:
2.1 timbl 437:
438: /* String handling
439: ** ---------------
440: */
2.57 frystyk 441: PRIVATE int HTMIME_put_string (HTStream * me, const char * s)
2.1 timbl 442: {
2.18 frystyk 443: return HTMIME_put_block(me, s, (int) strlen(s));
2.1 timbl 444: }
445:
446:
2.18 frystyk 447: /* Flush an stream object
448: ** ---------------------
2.1 timbl 449: */
2.36 frystyk 450: PRIVATE int HTMIME_flush (HTStream * me)
2.1 timbl 451: {
2.47 frystyk 452: return me->target ? (*me->target->isa->flush)(me->target) : HT_OK;
2.1 timbl 453: }
454:
2.18 frystyk 455: /* Free a stream object
456: ** --------------------
2.1 timbl 457: */
2.36 frystyk 458: PRIVATE int HTMIME_free (HTStream * me)
2.1 timbl 459: {
2.18 frystyk 460: int status = HT_OK;
2.64 eric 461: if (!me->transparent)
2.65 eric 462: if (_dispatchParsers(me) == HT_OK)
2.64 eric 463: pumpData(me);
2.25 frystyk 464: if (me->target) {
465: if ((status = (*me->target->isa->_free)(me->target))==HT_WOULD_BLOCK)
466: return HT_WOULD_BLOCK;
467: }
2.94 ! frystyk 468: HTTRACE(PROT_TRACE, "MIME........ FREEING....\n");
2.64 eric 469: HTChunk_delete(me->token);
470: HTChunk_delete(me->value);
2.52 frystyk 471: HT_FREE(me);
2.18 frystyk 472: return status;
2.1 timbl 473: }
474:
475: /* End writing
476: */
2.38 frystyk 477: PRIVATE int HTMIME_abort (HTStream * me, HTList * e)
2.1 timbl 478: {
2.18 frystyk 479: int status = HT_ERROR;
2.41 frystyk 480: if (me->target) status = (*me->target->isa->abort)(me->target, e);
2.94 ! frystyk 481: HTTRACE(PROT_TRACE, "MIME........ ABORTING...\n");
2.64 eric 482: HTChunk_delete(me->token);
483: HTChunk_delete(me->value);
2.52 frystyk 484: HT_FREE(me);
2.18 frystyk 485: return status;
2.1 timbl 486: }
487:
488:
489:
490: /* Structured Object Class
491: ** -----------------------
492: */
2.57 frystyk 493: PRIVATE const HTStreamClass HTMIME =
2.1 timbl 494: {
495: "MIMEParser",
2.18 frystyk 496: HTMIME_flush,
2.1 timbl 497: HTMIME_free,
2.6 timbl 498: HTMIME_abort,
499: HTMIME_put_character,
500: HTMIME_put_string,
2.18 frystyk 501: HTMIME_put_block
2.1 timbl 502: };
503:
504:
2.48 frystyk 505: /* MIME header parser stream.
2.1 timbl 506: ** -------------------------
2.48 frystyk 507: ** This stream parses a complete MIME header and if a content type header
508: ** is found then the stream stack is called. Any left over data is pumped
509: ** right through the stream
2.1 timbl 510: */
2.36 frystyk 511: PUBLIC HTStream* HTMIMEConvert (HTRequest * request,
512: void * param,
513: HTFormat input_format,
514: HTFormat output_format,
515: HTStream * output_stream)
2.1 timbl 516: {
2.62 frystyk 517: HTStream * me;
2.52 frystyk 518: if ((me = (HTStream *) HT_CALLOC(1, sizeof(* me))) == NULL)
519: HT_OUTOFMEM("HTMIMEConvert");
2.1 timbl 520: me->isa = &HTMIME;
2.18 frystyk 521: me->request = request;
2.71 frystyk 522: me->response = HTRequest_response(request);
2.70 frystyk 523: me->net = HTRequest_net(request);
2.49 frystyk 524: me->target = output_stream;
2.18 frystyk 525: me->target_format = output_format;
2.93 frystyk 526: me->save_stream = LocalSaveStream ? LocalSaveStream : HTBlackHoleConverter;
2.64 eric 527: me->token = HTChunk_new(256);
528: me->value = HTChunk_new(256);
529: me->hash = 0;
2.18 frystyk 530: me->EOLstate = EOL_BEGIN;
2.64 eric 531: me->haveToken = NO;
2.1 timbl 532: return me;
533: }
2.32 frystyk 534:
2.48 frystyk 535: /* MIME header ONLY parser stream
536: ** ------------------------------
537: ** This stream parses a complete MIME header and then returnes HT_PAUSE.
538: ** It does not set up any streams and resting data stays in the buffer.
539: ** This can be used if you only want to parse the headers before you
540: ** decide what to do next. This is for example the case in a server app.
541: */
542: PUBLIC HTStream * HTMIMEHeader (HTRequest * request,
543: void * param,
544: HTFormat input_format,
545: HTFormat output_format,
546: HTStream * output_stream)
547: {
2.62 frystyk 548: HTStream * me = HTMIMEConvert(request, param, input_format,
549: output_format, output_stream);
2.70 frystyk 550: me->mode |= HT_MIME_HEADER;
2.48 frystyk 551: return me;
552: }
2.77 frystyk 553:
554: PUBLIC HTStream * HTMIMEContinue (HTRequest * request,
555: void * param,
556: HTFormat input_format,
557: HTFormat output_format,
558: HTStream * output_stream)
559: {
560: HTStream * me = HTMIMEConvert(request, param, input_format,
561: output_format, output_stream);
562: me->mode |= HT_MIME_CONT;
563: return me;
564: }
2.62 frystyk 565:
566: /* MIME footer ONLY parser stream
567: ** ------------------------------
568: ** Parse only a footer, for example after a chunked encoding.
569: */
570: PUBLIC HTStream * HTMIMEFooter (HTRequest * request,
571: void * param,
572: HTFormat input_format,
573: HTFormat output_format,
574: HTStream * output_stream)
575: {
576: HTStream * me = HTMIMEConvert(request, param, input_format,
577: output_format, output_stream);
2.70 frystyk 578: me->mode |= HT_MIME_FOOTER;
2.62 frystyk 579: return me;
580: }
2.71 frystyk 581:
2.93 frystyk 582: #ifndef NO_CACHE
2.72 frystyk 583: /*
584: ** A small BEFORE filter that just finds a cache entry unconditionally
585: ** and loads the entry. All freshness and any other constraints are
586: ** ignored.
587: */
588: PRIVATE int HTCacheLoadFilter (HTRequest * request, void * param, int mode)
589: {
590: HTParentAnchor * anchor = HTRequest_anchor(request);
591: HTCache * cache = HTCache_find(anchor);
2.94 ! frystyk 592: HTTRACE(STREAM_TRACE, "Cache Load.. loading partial cache entry\n");
2.72 frystyk 593: if (cache) {
594: char * name = HTCache_name(cache);
595: HTAnchor_setPhysical(anchor, name);
596: HTCache_addHit(cache);
597: HT_FREE(name);
598: }
599: return HT_OK;
600: }
601:
602: /*
603: ** A small AFTER filter that flushes the PIPE buffer so that we can
604: ** get the rest of the data
605: */
606: PRIVATE int HTCacheFlushFilter (HTRequest * request, HTResponse * response,
607: void * param, int mode)
608: {
609: HTStream * pipe = (HTStream *) param;
610: if (pipe) {
2.94 ! frystyk 611: HTTRACE(STREAM_TRACE, "Cache Flush. Flushing and freeing PIPE buffer\n");
2.72 frystyk 612: (*pipe->isa->flush)(pipe);
613: (*pipe->isa->_free)(pipe);
614: }
615:
616: /*
617: ** We also delete the request obejct and stop more filters from being called.
618: ** As this is our own request, it's OK to do that
619: */
620: HTRequest_delete(request);
621: return HT_ERROR;
622: }
2.93 frystyk 623: #endif
2.72 frystyk 624:
2.71 frystyk 625: /* Partial Response MIME parser stream
626: ** -----------------------------------
627: ** In case we sent a Range conditional GET we may get back a partial
628: ** response. This response must be appended to the already existing
629: ** cache entry before presented to the user.
630: ** We do this by continuing to load the new object into a temporary
631: ** buffer and at the same time start the cache load of the already
632: ** existing object. When we have loaded the cache we merge the two
633: ** buffers.
634: */
635: PUBLIC HTStream * HTMIMEPartial (HTRequest * request,
636: void * param,
637: HTFormat input_format,
638: HTFormat output_format,
639: HTStream * output_stream)
640: {
2.93 frystyk 641: #ifndef NO_CACHE
2.71 frystyk 642: HTParentAnchor * anchor = HTRequest_anchor(request);
2.72 frystyk 643: HTFormat format = HTAnchor_format(anchor);
644: HTStream * pipe = NULL;
645:
2.71 frystyk 646: /*
647: ** The merge stream is a place holder for where we can put data when it
648: ** arrives. We have two feeds: one from the cache and one from the net.
649: ** We call the stream stack already now to get the right output stream.
650: ** We can do this as we already know the content type from when we got the
651: ** first part of the object.
652: */
2.72 frystyk 653: HTStream * merge = HTMerge(HTStreamStack(format,
654: output_format, output_stream,
655: request, YES), 2);
2.71 frystyk 656:
657: /*
2.72 frystyk 658: ** Now we create the MIME parser stream in partial data mode. We also
659: ** set the target to our merge stream.
2.71 frystyk 660: */
661: HTStream * me = HTMIMEConvert(request, param, input_format,
662: output_format, output_stream);
663: me->mode |= HT_MIME_PARTIAL;
2.72 frystyk 664: me->target = merge;
665:
666: /*
667: ** Create the cache append stream, and a Tee stream
668: */
669: {
670: HTStream * append = HTStreamStack(WWW_CACHE_APPEND, output_format,
671: output_stream, request, NO);
672: if (append) me->target = HTTee(me->target, append, NULL);
673: }
674:
675: /*
676: ** Create the pipe buffer stream to buffer the data that we read
677: ** from the network
678: */
2.74 frystyk 679: if ((pipe = HTPipeBuffer(me->target, 0))) me->target = pipe;
2.71 frystyk 680:
681: /*
682: ** Now start the second load from the cache. First we read this data from
683: ** the cache and then we flush the data that we have read from the net.
684: */
685: {
2.72 frystyk 686: HTRequest * cache_request = HTRequest_new();
2.71 frystyk 687:
2.72 frystyk 688: /*
689: ** Set the output format to source and the output stream to the
690: ** merge stream. As we have already set up the stream pipe, we just
691: ** load it as source.
692: */
693: HTRequest_setOutputFormat(cache_request, WWW_SOURCE);
694: HTRequest_setOutputStream(cache_request, merge);
695:
696: /*
697: ** Bind the anchor to the new request and also register a local
698: ** AFTER filter to flush the pipe buffer so that we can get
699: ** rest of the data through.
700: */
701: HTRequest_setAnchor(cache_request, (HTAnchor *) anchor);
702: HTRequest_addBefore(cache_request, HTCacheLoadFilter, NULL, NULL,
703: HT_FILTER_FIRST, YES);
704: HTRequest_addAfter(cache_request, HTCacheFlushFilter, NULL, pipe,
705: HT_ALL, HT_FILTER_FIRST, YES);
2.71 frystyk 706:
2.94 ! frystyk 707: HTTRACE(STREAM_TRACE, "Partial..... Starting cache load\n");
2.72 frystyk 708: HTLoad(cache_request, NO);
2.71 frystyk 709: }
710: return me;
2.93 frystyk 711: #else
712: return NULL;
713: #endif
2.71 frystyk 714: }
715:
2.93 frystyk 716: PUBLIC void HTMIME_setSaveStream (HTConverter * save_stream)
717: {
718: LocalSaveStream = save_stream;
719: }
720:
721: PUBLIC HTConverter * HTMIME_saveStream (void)
722: {
723: return LocalSaveStream;
724: }
Webmaster