212 lines
6.3 KiB
Objective-C
212 lines
6.3 KiB
Objective-C
|
|
#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;
|
|
}
|
|
|