Annotation of libwww/Library/src/HTMIME.c, revision 2.93
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.93 ! frystyk 6: ** @(#) $Id: HTMIME.c,v 2.92 1999/02/05 17:31:28 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) {
119: if (STREAM_TRACE) HTTrace("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");
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.81 frystyk 124: if (STREAM_TRACE) HTTrace("MIME Parser. No body in this messsage\n");
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.71 frystyk 141: if (STREAM_TRACE) HTTrace("Building.... C-T stack from %s to %s\n",
142: HTAtom_name(format),
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: */
159: if (STREAM_TRACE) HTTrace("Building.... Content-Decoding stack\n");
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.83 frystyk 198: if (STREAM_TRACE) HTTrace("Building.... Transfer-Decoding stack\n");
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.66 frystyk 233: if (STREAM_TRACE) HTTrace("MIME header. %s: %s\n",
234: token ? token : "<null>",
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.26 frystyk 468: if (PROT_TRACE)
2.55 eric 469: HTTrace("MIME........ FREEING....\n");
2.64 eric 470: HTChunk_delete(me->token);
471: HTChunk_delete(me->value);
2.52 frystyk 472: HT_FREE(me);
2.18 frystyk 473: return status;
2.1 timbl 474: }
475:
476: /* End writing
477: */
2.38 frystyk 478: PRIVATE int HTMIME_abort (HTStream * me, HTList * e)
2.1 timbl 479: {
2.18 frystyk 480: int status = HT_ERROR;
2.41 frystyk 481: if (me->target) status = (*me->target->isa->abort)(me->target, e);
2.26 frystyk 482: if (PROT_TRACE)
2.55 eric 483: HTTrace("MIME........ ABORTING...\n");
2.64 eric 484: HTChunk_delete(me->token);
485: HTChunk_delete(me->value);
2.52 frystyk 486: HT_FREE(me);
2.18 frystyk 487: return status;
2.1 timbl 488: }
489:
490:
491:
492: /* Structured Object Class
493: ** -----------------------
494: */
2.57 frystyk 495: PRIVATE const HTStreamClass HTMIME =
2.1 timbl 496: {
497: "MIMEParser",
2.18 frystyk 498: HTMIME_flush,
2.1 timbl 499: HTMIME_free,
2.6 timbl 500: HTMIME_abort,
501: HTMIME_put_character,
502: HTMIME_put_string,
2.18 frystyk 503: HTMIME_put_block
2.1 timbl 504: };
505:
506:
2.48 frystyk 507: /* MIME header parser stream.
2.1 timbl 508: ** -------------------------
2.48 frystyk 509: ** This stream parses a complete MIME header and if a content type header
510: ** is found then the stream stack is called. Any left over data is pumped
511: ** right through the stream
2.1 timbl 512: */
2.36 frystyk 513: PUBLIC HTStream* HTMIMEConvert (HTRequest * request,
514: void * param,
515: HTFormat input_format,
516: HTFormat output_format,
517: HTStream * output_stream)
2.1 timbl 518: {
2.62 frystyk 519: HTStream * me;
2.52 frystyk 520: if ((me = (HTStream *) HT_CALLOC(1, sizeof(* me))) == NULL)
521: HT_OUTOFMEM("HTMIMEConvert");
2.1 timbl 522: me->isa = &HTMIME;
2.18 frystyk 523: me->request = request;
2.71 frystyk 524: me->response = HTRequest_response(request);
2.70 frystyk 525: me->net = HTRequest_net(request);
2.49 frystyk 526: me->target = output_stream;
2.18 frystyk 527: me->target_format = output_format;
2.93 ! frystyk 528: me->save_stream = LocalSaveStream ? LocalSaveStream : HTBlackHoleConverter;
2.64 eric 529: me->token = HTChunk_new(256);
530: me->value = HTChunk_new(256);
531: me->hash = 0;
2.18 frystyk 532: me->EOLstate = EOL_BEGIN;
2.64 eric 533: me->haveToken = NO;
2.1 timbl 534: return me;
535: }
2.32 frystyk 536:
2.48 frystyk 537: /* MIME header ONLY parser stream
538: ** ------------------------------
539: ** This stream parses a complete MIME header and then returnes HT_PAUSE.
540: ** It does not set up any streams and resting data stays in the buffer.
541: ** This can be used if you only want to parse the headers before you
542: ** decide what to do next. This is for example the case in a server app.
543: */
544: PUBLIC HTStream * HTMIMEHeader (HTRequest * request,
545: void * param,
546: HTFormat input_format,
547: HTFormat output_format,
548: HTStream * output_stream)
549: {
2.62 frystyk 550: HTStream * me = HTMIMEConvert(request, param, input_format,
551: output_format, output_stream);
2.70 frystyk 552: me->mode |= HT_MIME_HEADER;
2.48 frystyk 553: return me;
554: }
2.77 frystyk 555:
556: PUBLIC HTStream * HTMIMEContinue (HTRequest * request,
557: void * param,
558: HTFormat input_format,
559: HTFormat output_format,
560: HTStream * output_stream)
561: {
562: HTStream * me = HTMIMEConvert(request, param, input_format,
563: output_format, output_stream);
564: me->mode |= HT_MIME_CONT;
565: return me;
566: }
2.62 frystyk 567:
568: /* MIME footer ONLY parser stream
569: ** ------------------------------
570: ** Parse only a footer, for example after a chunked encoding.
571: */
572: PUBLIC HTStream * HTMIMEFooter (HTRequest * request,
573: void * param,
574: HTFormat input_format,
575: HTFormat output_format,
576: HTStream * output_stream)
577: {
578: HTStream * me = HTMIMEConvert(request, param, input_format,
579: output_format, output_stream);
2.70 frystyk 580: me->mode |= HT_MIME_FOOTER;
2.62 frystyk 581: return me;
582: }
2.71 frystyk 583:
2.93 ! frystyk 584: #ifndef NO_CACHE
2.72 frystyk 585: /*
586: ** A small BEFORE filter that just finds a cache entry unconditionally
587: ** and loads the entry. All freshness and any other constraints are
588: ** ignored.
589: */
590: PRIVATE int HTCacheLoadFilter (HTRequest * request, void * param, int mode)
591: {
592: HTParentAnchor * anchor = HTRequest_anchor(request);
593: HTCache * cache = HTCache_find(anchor);
594: if (STREAM_TRACE) HTTrace("Cache Load.. loading partial cache entry\n");
595: if (cache) {
596: char * name = HTCache_name(cache);
597: HTAnchor_setPhysical(anchor, name);
598: HTCache_addHit(cache);
599: HT_FREE(name);
600: }
601: return HT_OK;
602: }
603:
604: /*
605: ** A small AFTER filter that flushes the PIPE buffer so that we can
606: ** get the rest of the data
607: */
608: PRIVATE int HTCacheFlushFilter (HTRequest * request, HTResponse * response,
609: void * param, int mode)
610: {
611: HTStream * pipe = (HTStream *) param;
612: if (pipe) {
613: if (STREAM_TRACE) HTTrace("Cache Flush. Flushing and freeing PIPE buffer\n");
614: (*pipe->isa->flush)(pipe);
615: (*pipe->isa->_free)(pipe);
616: }
617:
618: /*
619: ** We also delete the request obejct and stop more filters from being called.
620: ** As this is our own request, it's OK to do that
621: */
622: HTRequest_delete(request);
623: return HT_ERROR;
624: }
2.93 ! frystyk 625: #endif
2.72 frystyk 626:
2.71 frystyk 627: /* Partial Response MIME parser stream
628: ** -----------------------------------
629: ** In case we sent a Range conditional GET we may get back a partial
630: ** response. This response must be appended to the already existing
631: ** cache entry before presented to the user.
632: ** We do this by continuing to load the new object into a temporary
633: ** buffer and at the same time start the cache load of the already
634: ** existing object. When we have loaded the cache we merge the two
635: ** buffers.
636: */
637: PUBLIC HTStream * HTMIMEPartial (HTRequest * request,
638: void * param,
639: HTFormat input_format,
640: HTFormat output_format,
641: HTStream * output_stream)
642: {
2.93 ! frystyk 643: #ifndef NO_CACHE
2.71 frystyk 644: HTParentAnchor * anchor = HTRequest_anchor(request);
2.72 frystyk 645: HTFormat format = HTAnchor_format(anchor);
646: HTStream * pipe = NULL;
647:
2.71 frystyk 648: /*
649: ** The merge stream is a place holder for where we can put data when it
650: ** arrives. We have two feeds: one from the cache and one from the net.
651: ** We call the stream stack already now to get the right output stream.
652: ** We can do this as we already know the content type from when we got the
653: ** first part of the object.
654: */
2.72 frystyk 655: HTStream * merge = HTMerge(HTStreamStack(format,
656: output_format, output_stream,
657: request, YES), 2);
2.71 frystyk 658:
659: /*
2.72 frystyk 660: ** Now we create the MIME parser stream in partial data mode. We also
661: ** set the target to our merge stream.
2.71 frystyk 662: */
663: HTStream * me = HTMIMEConvert(request, param, input_format,
664: output_format, output_stream);
665: me->mode |= HT_MIME_PARTIAL;
2.72 frystyk 666: me->target = merge;
667:
668: /*
669: ** Create the cache append stream, and a Tee stream
670: */
671: {
672: HTStream * append = HTStreamStack(WWW_CACHE_APPEND, output_format,
673: output_stream, request, NO);
674: if (append) me->target = HTTee(me->target, append, NULL);
675: }
676:
677: /*
678: ** Create the pipe buffer stream to buffer the data that we read
679: ** from the network
680: */
2.74 frystyk 681: if ((pipe = HTPipeBuffer(me->target, 0))) me->target = pipe;
2.71 frystyk 682:
683: /*
684: ** Now start the second load from the cache. First we read this data from
685: ** the cache and then we flush the data that we have read from the net.
686: */
687: {
2.72 frystyk 688: HTRequest * cache_request = HTRequest_new();
2.71 frystyk 689:
2.72 frystyk 690: /*
691: ** Set the output format to source and the output stream to the
692: ** merge stream. As we have already set up the stream pipe, we just
693: ** load it as source.
694: */
695: HTRequest_setOutputFormat(cache_request, WWW_SOURCE);
696: HTRequest_setOutputStream(cache_request, merge);
697:
698: /*
699: ** Bind the anchor to the new request and also register a local
700: ** AFTER filter to flush the pipe buffer so that we can get
701: ** rest of the data through.
702: */
703: HTRequest_setAnchor(cache_request, (HTAnchor *) anchor);
704: HTRequest_addBefore(cache_request, HTCacheLoadFilter, NULL, NULL,
705: HT_FILTER_FIRST, YES);
706: HTRequest_addAfter(cache_request, HTCacheFlushFilter, NULL, pipe,
707: HT_ALL, HT_FILTER_FIRST, YES);
2.71 frystyk 708:
2.72 frystyk 709: if (STREAM_TRACE) HTTrace("Partial..... Starting cache load\n");
710: HTLoad(cache_request, NO);
2.71 frystyk 711: }
712: return me;
2.93 ! frystyk 713: #else
! 714: return NULL;
! 715: #endif
2.71 frystyk 716: }
717:
2.93 ! frystyk 718: PUBLIC void HTMIME_setSaveStream (HTConverter * save_stream)
! 719: {
! 720: LocalSaveStream = save_stream;
! 721: }
! 722:
! 723: PUBLIC HTConverter * HTMIME_saveStream (void)
! 724: {
! 725: return LocalSaveStream;
! 726: }
Webmaster