123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- #import "MultipartMessageHeaderField.h"
- #import "HTTPLogging.h"
- //-----------------------------------------------------------------
- #pragma mark log level
- #ifdef DEBUG
- static const int httpLogLevel = HTTP_LOG_LEVEL_WARN;
- #else
- static const int httpLogLevel = HTTP_LOG_LEVEL_WARN;
- #endif
- // helpers
- int findChar(const char* str,NSUInteger length, char c);
- NSString* extractParamValue(const char* bytes, NSUInteger length, NSStringEncoding encoding);
- //-----------------------------------------------------------------
- // interface MultipartMessageHeaderField (private)
- //-----------------------------------------------------------------
- @interface MultipartMessageHeaderField (private)
- -(BOOL) parseHeaderValueBytes:(char*) bytes length:(NSUInteger) length encoding:(NSStringEncoding) encoding;
- @end
- //-----------------------------------------------------------------
- // implementation MultipartMessageHeaderField
- //-----------------------------------------------------------------
- @implementation MultipartMessageHeaderField
- @synthesize name,value,params;
- - (id) initWithData:(NSData *)data contentEncoding:(NSStringEncoding)encoding {
- params = [[NSMutableDictionary alloc] initWithCapacity:1];
- char* bytes = (char*)data.bytes;
- NSUInteger length = data.length;
- int separatorOffset = findChar(bytes, length, ':');
- if( (-1 == separatorOffset) || (separatorOffset >= length-2) ) {
- HTTPLogError(@"MultipartFormDataParser: Bad format.No colon in field header.");
- // tear down
- return nil;
- }
-
- // header name is always ascii encoded;
- name = [[NSString alloc] initWithBytes: bytes length: separatorOffset encoding: NSASCIIStringEncoding];
- if( nil == name ) {
- HTTPLogError(@"MultipartFormDataParser: Bad MIME header name.");
- // tear down
- return nil;
- }
-
- // skip the separator and the next ' ' symbol
- bytes += separatorOffset + 2;
- length -= separatorOffset + 2;
- separatorOffset = findChar(bytes, length, ';');
- if( separatorOffset == -1 ) {
- // couldn't find ';', means we don't have extra params here.
- value = [[NSString alloc] initWithBytes:bytes length: length encoding:encoding];
- if( nil == value ) {
- HTTPLogError(@"MultipartFormDataParser: Bad MIME header value for header name: '%@'",name);
- // tear down
- return nil;
- }
- return self;
- }
-
- value = [[NSString alloc] initWithBytes:bytes length: separatorOffset encoding:encoding];
- HTTPLogVerbose(@"MultipartFormDataParser: Processing header field '%@' : '%@'",name,value);
- // skipe the separator and the next ' ' symbol
- bytes += separatorOffset + 2;
- length -= separatorOffset + 2;
- // parse the "params" part of the header
- if( ![self parseHeaderValueBytes:bytes length:length encoding:encoding] ) {
- NSString* paramsStr = [[NSString alloc] initWithBytes:bytes length:length encoding:NSASCIIStringEncoding];
- HTTPLogError(@"MultipartFormDataParser: Bad params for header with name '%@' and value '%@'",name,value);
- HTTPLogError(@"MultipartFormDataParser: Params str: %@",paramsStr);
- return nil;
- }
- return self;
- }
- -(BOOL) parseHeaderValueBytes:(char*) bytes length:(NSUInteger) length encoding:(NSStringEncoding) encoding {
- int offset = 0;
- NSString* currentParam = nil;
- BOOL insideQuote = NO;
- while( offset < length ) {
- if( bytes[offset] == '\"' ) {
- if( !offset || bytes[offset-1] != '\\' ) {
- insideQuote = !insideQuote;
- }
- }
- // skip quoted symbols
- if( insideQuote ) {
- ++ offset;
- continue;
- }
- if( bytes[offset] == '=' ) {
- if( currentParam ) {
- // found '=' before terminating previous param.
- return NO;
- }
- currentParam = [[NSString alloc] initWithBytes:bytes length:offset encoding:NSASCIIStringEncoding];
- bytes+=offset + 1;
- length -= offset + 1;
- offset = 0;
- continue;
- }
- if( bytes[offset] == ';' ) {
- if( !currentParam ) {
- // found ; before stating '='.
- HTTPLogError(@"MultipartFormDataParser: Unexpected ';' when parsing header");
- return NO;
- }
- NSString* paramValue = extractParamValue(bytes, offset,encoding);
- if( nil == paramValue ) {
- HTTPLogWarn(@"MultipartFormDataParser: Failed to exctract paramValue for key %@ in header %@",currentParam,name);
- }
- else {
- #ifdef DEBUG
- if( [params objectForKey:currentParam] ) {
- HTTPLogWarn(@"MultipartFormDataParser: param %@ mentioned more then once in header %@",currentParam,name);
- }
- #endif
- [params setObject:paramValue forKey:currentParam];
- HTTPLogVerbose(@"MultipartFormDataParser: header param: %@ = %@",currentParam,paramValue);
- }
- currentParam = nil;
- // ';' separator has ' ' following, skip them.
- bytes+=offset + 2;
- length -= offset + 2;
- offset = 0;
- }
- ++ offset;
- }
- // add last param
- if( insideQuote ) {
- HTTPLogWarn(@"MultipartFormDataParser: unterminated quote in header %@",name);
- // return YES;
- }
- if( currentParam ) {
- NSString* paramValue = extractParamValue(bytes, length, encoding);
- if( nil == paramValue ) {
- HTTPLogError(@"MultipartFormDataParser: Failed to exctract paramValue for key %@ in header %@",currentParam,name);
- }
- #ifdef DEBUG
- if( [params objectForKey:currentParam] ) {
- HTTPLogWarn(@"MultipartFormDataParser: param %@ mentioned more then once in one header",currentParam);
- }
- #endif
- [params setObject:paramValue forKey:currentParam];
- HTTPLogVerbose(@"MultipartFormDataParser: header param: %@ = %@",currentParam,paramValue);
- currentParam = nil;
- }
-
- return YES;
- }
- - (NSString *)description {
- return [NSString stringWithFormat:@"%@:%@\n params: %@",name,value,params];
- }
- @end
- int findChar(const char* str, NSUInteger length, char c) {
- int offset = 0;
- while( offset < length ) {
- if( str[offset] == c )
- return offset;
- ++ offset;
- }
- return -1;
- }
- NSString* extractParamValue(const char* bytes, NSUInteger length, NSStringEncoding encoding) {
- if( !length )
- return nil;
- NSMutableString* value = nil;
-
- if( bytes[0] == '"' ) {
- // values may be quoted. Strip the quotes to get what we need.
- value = [[NSMutableString alloc] initWithBytes:bytes + 1 length: length - 2 encoding:encoding];
- }
- else {
- value = [[NSMutableString alloc] initWithBytes:bytes length: length encoding:encoding];
- }
- // restore escaped symbols
- NSRange range= [value rangeOfString:@"\\"];
- while ( range.length ) {
- [value deleteCharactersInRange:range];
- range.location ++;
- range = [value rangeOfString:@"\\" options:NSLiteralSearch range: range];
- }
- return value;
- }
|