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