📄 htmime.c
字号:
/* HTMIME.c** MIME MESSAGE PARSE**** (c) COPYRIGHT MIT 1995.** Please first read the full copyright statement in the file COPYRIGH.** @(#) $Id: HTMIME.c,v 2.101 2000/12/18 17:00:56 kahan Exp $**** This is RFC 1341-specific code.** The input stream pushed into this parser is assumed to be** stripped on CRs, ie lines end with LF, not CR LF.** (It is easy to change this except for the body part where** conversion can be slow.)**** History:** Feb 92 Written Tim Berners-Lee, CERN** 8 Jul 94 FM Insulate free() from _free structure element.** 14 Mar 95 HFN Now using response for storing data. No more `\n',** static buffers etc.*//* Library include files */#include "wwwsys.h"#include "WWWUtil.h"#include "WWWCore.h"#include "HTReqMan.h"#include "HTNetMan.h"#include "HTHeader.h"#include "HTWWWStr.h"#ifndef NO_CACHE#include "HTTee.h"#include "HTConLen.h"#include "HTMerge.h"#include "WWWCache.h"#endif#include "HTMIME.h" /* Implemented here */typedef enum _HTMIMEMode { HT_MIME_HEADER = 0x1, HT_MIME_FOOTER = 0x2, HT_MIME_PARTIAL = 0x4, HT_MIME_CONT = 0x8, HT_MIME_UPGRADE = 0x10} HTMIMEMode;struct _HTStream { const HTStreamClass * isa; HTRequest * request; HTResponse * response; HTNet * net; HTStream * target; HTConverter * save_stream; HTFormat target_format; HTChunk * token; HTChunk * value; int hash; HTEOLState EOLstate; HTMIMEMode mode; BOOL transparent; BOOL haveToken; BOOL hasBody;};PRIVATE HTConverter * LocalSaveStream = NULL; /* Where to save unknown stuff *//* ------------------------------------------------------------------------- */PRIVATE int pumpData (HTStream * me){ HTRequest * request = me->request; HTResponse * response = me->response; HTFormat format = HTResponse_format(response); HTList * te = HTResponse_transfer(response); HTList * ce = HTResponse_encoding(response); long length = HTResponse_length(response); HTStream * BlackHole = HTBlackHole(); BOOL savestream = NO; me->transparent = YES; /* Pump rest of data right through */ /* ** Cache the metainformation in the anchor object by copying ** it from the response object. This we do regardless if ** we have a persistent cache or not as the memory cache will ** use it as well. If we are updating a cache entry using ** byte ranges then we already have the metainformation and ** hence we can ignore the new one as it'd better be the same. */ if (!(me->mode & HT_MIME_PARTIAL) && HTResponse_isCachable(me->response) != HT_NO_CACHE) HTAnchor_update(HTRequest_anchor(request), me->response); /* ** If we asked only to read the header or footer or we used a HEAD ** method then we stop here as we don't expect any body part. */ if (me->mode & (HT_MIME_HEADER | HT_MIME_FOOTER) || HTRequest_method(request) == METHOD_HEAD) { HTAlertCallback * cbf = HTAlert_find(HT_PROG_DONE); if (cbf) (*cbf)(request, HT_PROG_DONE, HT_MSG_NULL, NULL, NULL, NULL); return HT_LOADED; } /* ** If we are paring a 1xx response then return HT_CONTINUE */ if (me->mode & HT_MIME_CONT) return HT_CONTINUE; /* ** If we get a 101 Protocol Switch then we are done here ** but not done with the response (which we don't know ** how to go about parsing */ if (me->mode & HT_MIME_UPGRADE) { me->hasBody = YES; return HT_OK; } /* ** If there is no content-length, no transfer encoding and no ** content type then we assume that there is no body part in ** the message and we can return HT_LOADED */ { HTHost * host = HTNet_host(me->net); if (length<0 && te==NULL && HTHost_isPersistent(host) && !HTHost_closeNotification(host)) { if (format != WWW_UNKNOWN) { 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"); HTHost_setCloseNotification(host, YES); } else { HTAlertCallback * cbf = HTAlert_find(HT_PROG_DONE); if (cbf) (*cbf)(request, HT_PROG_DONE, HT_MSG_NULL, NULL, NULL, NULL); HTTRACE(STREAM_TRACE, "MIME Parser. No body in this message\n"); return HT_LOADED; } } } /* ** Deal with the body */ me->hasBody = YES; /* ** Handle any Content Type */ if (!(me->mode & HT_MIME_PARTIAL) && (format != WWW_UNKNOWN || length > 0 || te)) { HTStream * target; HTTRACE(STREAM_TRACE, "Building.... C-T stack from %s to %s\n" _ HTAtom_name(format) _ HTAtom_name(me->target_format)); if ((target = HTStreamStack(format, me->target_format, me->target, request, YES))==BlackHole) { if (!savestream) { if (me->target) (*me->target->isa->abort)(me->target, NULL); me->target = me->save_stream(request, NULL, format, me->target_format, me->target); savestream = YES; } } else me->target = target; } /* ** Handle any Content Encodings */ HTTRACE(STREAM_TRACE, "Building.... Content-Decoding stack\n"); if (ce) { HTStream * target = HTContentDecodingStack(ce, me->target, request, NULL); if (target == BlackHole) { if (!savestream) { if (me->target) (*me->target->isa->abort)(me->target, NULL); me->target = me->save_stream(request, NULL, format, me->target_format, me->target); savestream = YES; } } else me->target = target; } /* ** Can we cache the data object? If so then create a T stream and hook it ** into the stream pipe. We do it before the transfer decoding so that we ** don't have to deal with that when we retrieve the object from cache. ** If we are appending to a cache entry then use a different stream than ** if creating a new entry. */#ifndef NO_CACHE if (HTCacheMode_enabled()) { if (me->mode & HT_MIME_PARTIAL) { HTStream * append = HTStreamStack(WWW_CACHE_APPEND, me->target_format, me->target, request, NO); if (append) me->target = HTTee(me->target, append, NULL);#if 0 /* @@ JK: change */ if (append) me->target = append;#endif } else if (HTResponse_isCachable(me->response) == HT_CACHE_ALL) { HTStream * cache = HTStreamStack(WWW_CACHE, me->target_format, me->target, request, NO); if (cache) me->target = HTTee(me->target, cache, NULL); } }#endif /* ** Handle any Transfer Encodings */ HTTRACE(STREAM_TRACE, "Building.... Transfer-Decoding stack\n"); if (te) { HTStream * target = HTTransferDecodingStack(te, me->target, request, NULL); if (target == BlackHole) { if (!savestream) { if (me->target) (*me->target->isa->abort)(me->target, NULL); me->target = me->save_stream(request, NULL, format, me->target_format, me->target); savestream = YES; } } else me->target = target; } /* ** If we for some reason couldn't find a target stream */ if (!me->target) me->target = HTBlackHole(); return HT_OK;}/* _dispatchParsers * call request's MIME header parser. Use global parser if no * appropriate one is found for request.*/PRIVATE int _dispatchParsers (HTRequest * req, char * token, char * value){ int status; BOOL found = NO; BOOL local = NO; HTMIMEParseSet * parseSet; /* In case we get an empty header consisting of a CRLF, we fall thru */ HTTRACE(STREAM_TRACE, "MIME header. %s: %s\n" _ token ? token : "<null>" _ value ? value : "<null>"); if (!token) return HT_OK; /* Ignore noop token */ /* ** Search the local set of MIME parsers */ if ((parseSet = HTRequest_MIMEParseSet(req, &local)) != NULL) { status = HTMIMEParseSet_dispatch(parseSet, req, token, value, &found); if (found) return status; } /* ** Search the global set of MIME parsers */ if (local==NO && (parseSet = HTHeader_MIMEParseSet()) != NULL) { status = HTMIMEParseSet_dispatch(parseSet, req, token, value, &found); if (found) return status; } return HT_OK;}/* _stream2dispatchParsers - extracts the arguments from a * MIME stream before calling the generic _dispatchParser * function. */PRIVATE int _stream2dispatchParsers (HTStream * me){ char * token = HTChunk_data(me->token); char * value = HTChunk_data(me->value); /* In case we get an empty header consisting of a CRLF, we fall thru */ HTTRACE(STREAM_TRACE, "MIME header. %s: %s\n" _ token ? token : "<null>" _ value ? value : "<null>"); if (!token) return HT_OK; /* Ignore noop token */ /* ** Remember the original header */ HTResponse_addHeader(me->response, token, value); /* call the parsers to set the headers */ return (_dispatchParsers (me->request, token, value));}/*** Header is terminated by CRCR, LFLF, CRLFLF, CRLFCRLF** Folding is either of CF LWS, LF LWS, CRLF LWS*/PRIVATE int HTMIME_put_block (HTStream * me, const char * b, int l){ const char * start = b; const char * end = start; const char * value = HTChunk_size(me->value) > 0 ? b : NULL; int length = l; int status; while (!me->transparent) { if (me->EOLstate == EOL_FCR) { if (*b == CR) /* End of header */ me->EOLstate = EOL_END; else if (*b == LF) /* CRLF */ me->EOLstate = EOL_FLF; else if (isspace((int) *b)) /* Folding: CR SP */ me->EOLstate = EOL_FOLD; else /* New line */ me->EOLstate = EOL_LINE; } else if (me->EOLstate == EOL_FLF) { if (*b == CR) /* LF CR or CR LF CR */ me->EOLstate = EOL_SCR; else if (*b == LF) /* End of header */ me->EOLstate = EOL_END; else if (isspace((int) *b)) /* Folding: LF SP or CR LF SP */ me->EOLstate = EOL_FOLD; else /* New line */ me->EOLstate = EOL_LINE; } else if (me->EOLstate == EOL_SCR) { if (*b==CR || *b==LF) /* End of header */ me->EOLstate = EOL_END; else if (isspace((int) *b)) /* Folding: LF CR SP or CR LF CR SP */ me->EOLstate = EOL_FOLD; else /* New line */ me->EOLstate = EOL_LINE; } else if (*b == CR) me->EOLstate = EOL_FCR; else if (*b == LF) me->EOLstate = EOL_FLF; /* Line found */ else { if (!me->haveToken) { if (*b == ':' || isspace((int) *b)) { HTChunk_putb(me->token, start, end-start); HTChunk_putc(me->token, '\0'); me->haveToken = YES; } else { unsigned char ch = *(unsigned char *) b; ch = TOLOWER(ch); me->hash = (me->hash * 3 + ch) % MIME_HASH_SIZE; } } else if (value == NULL && *b != ':' && !isspace((int) *b)) value = b; end++; } switch (me->EOLstate) { case EOL_LINE: case EOL_END: { int ret = HT_ERROR; HTChunk_putb(me->value, value, end-value); HTChunk_putc(me->value, '\0'); ret = _stream2dispatchParsers(me); HTNet_addBytesRead(me->net, b-start); start=b, end=b; if (me->EOLstate == EOL_END) { /* EOL_END */ if (ret == HT_OK) { b++, l--; ret = pumpData(me); HTNet_addBytesRead(me->net, 1); if (me->mode & (HT_MIME_FOOTER | HT_MIME_CONT)) { HTHost_setConsumed(HTNet_host(me->net), length - l); return ret; } else { HTNet_setHeaderBytesRead(me->net, HTNet_bytesRead(me->net)); } } } else { /* EOL_LINE */ HTChunk_truncate(me->token,0); HTChunk_truncate(me->value,0); me->haveToken = NO; me->hash = 0; value = NULL; } me->EOLstate = EOL_BEGIN; if (ret != HT_OK && ret != HT_LOADED) return ret; break; } case EOL_FOLD: me->EOLstate = EOL_BEGIN; if (!me->haveToken) { HTChunk_putb(me->token, start, end-start); HTChunk_putc(me->token, '\0'); me->haveToken = YES; } else if (value) { HTChunk_putb(me->value, value, end-value); HTChunk_putc(me->value, ' '); } start=b, end=b; break; default: b++, l--; if (!l) { BOOL stop = NO; if (!me->haveToken) { /* If empty header then prepare to stop */ if (end-start) HTChunk_putb(me->token, start, end-start); else stop = YES; } else if (value) HTChunk_putb(me->value, value, end-value); HTHost_setConsumed(HTNet_host(me->net), length - l); return stop ? pumpData(me) : HT_OK; } } } if (length != l) HTHost_setConsumed(HTNet_host(me->net), length - l); /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -