MultipartMessageHeaderField.m 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. #import "MultipartMessageHeaderField.h"
  2. #import "HTTPLogging.h"
  3. //-----------------------------------------------------------------
  4. #pragma mark log level
  5. #ifdef DEBUG
  6. static const int httpLogLevel = HTTP_LOG_LEVEL_WARN;
  7. #else
  8. static const int httpLogLevel = HTTP_LOG_LEVEL_WARN;
  9. #endif
  10. // helpers
  11. int findChar(const char* str,NSUInteger length, char c);
  12. NSString* extractParamValue(const char* bytes, NSUInteger length, NSStringEncoding encoding);
  13. //-----------------------------------------------------------------
  14. // interface MultipartMessageHeaderField (private)
  15. //-----------------------------------------------------------------
  16. @interface MultipartMessageHeaderField (private)
  17. -(BOOL) parseHeaderValueBytes:(char*) bytes length:(NSUInteger) length encoding:(NSStringEncoding) encoding;
  18. @end
  19. //-----------------------------------------------------------------
  20. // implementation MultipartMessageHeaderField
  21. //-----------------------------------------------------------------
  22. @implementation MultipartMessageHeaderField
  23. @synthesize name,value,params;
  24. - (id) initWithData:(NSData *)data contentEncoding:(NSStringEncoding)encoding {
  25. params = [[NSMutableDictionary alloc] initWithCapacity:1];
  26. char* bytes = (char*)data.bytes;
  27. NSUInteger length = data.length;
  28. int separatorOffset = findChar(bytes, length, ':');
  29. if( (-1 == separatorOffset) || (separatorOffset >= length-2) ) {
  30. HTTPLogError(@"MultipartFormDataParser: Bad format.No colon in field header.");
  31. // tear down
  32. return nil;
  33. }
  34. // header name is always ascii encoded;
  35. name = [[NSString alloc] initWithBytes: bytes length: separatorOffset encoding: NSASCIIStringEncoding];
  36. if( nil == name ) {
  37. HTTPLogError(@"MultipartFormDataParser: Bad MIME header name.");
  38. // tear down
  39. return nil;
  40. }
  41. // skip the separator and the next ' ' symbol
  42. bytes += separatorOffset + 2;
  43. length -= separatorOffset + 2;
  44. separatorOffset = findChar(bytes, length, ';');
  45. if( separatorOffset == -1 ) {
  46. // couldn't find ';', means we don't have extra params here.
  47. value = [[NSString alloc] initWithBytes:bytes length: length encoding:encoding];
  48. if( nil == value ) {
  49. HTTPLogError(@"MultipartFormDataParser: Bad MIME header value for header name: '%@'",name);
  50. // tear down
  51. return nil;
  52. }
  53. return self;
  54. }
  55. value = [[NSString alloc] initWithBytes:bytes length: separatorOffset encoding:encoding];
  56. HTTPLogVerbose(@"MultipartFormDataParser: Processing header field '%@' : '%@'",name,value);
  57. // skipe the separator and the next ' ' symbol
  58. bytes += separatorOffset + 2;
  59. length -= separatorOffset + 2;
  60. // parse the "params" part of the header
  61. if( ![self parseHeaderValueBytes:bytes length:length encoding:encoding] ) {
  62. NSString* paramsStr = [[NSString alloc] initWithBytes:bytes length:length encoding:NSASCIIStringEncoding];
  63. HTTPLogError(@"MultipartFormDataParser: Bad params for header with name '%@' and value '%@'",name,value);
  64. HTTPLogError(@"MultipartFormDataParser: Params str: %@",paramsStr);
  65. return nil;
  66. }
  67. return self;
  68. }
  69. -(BOOL) parseHeaderValueBytes:(char*) bytes length:(NSUInteger) length encoding:(NSStringEncoding) encoding {
  70. int offset = 0;
  71. NSString* currentParam = nil;
  72. BOOL insideQuote = NO;
  73. while( offset < length ) {
  74. if( bytes[offset] == '\"' ) {
  75. if( !offset || bytes[offset-1] != '\\' ) {
  76. insideQuote = !insideQuote;
  77. }
  78. }
  79. // skip quoted symbols
  80. if( insideQuote ) {
  81. ++ offset;
  82. continue;
  83. }
  84. if( bytes[offset] == '=' ) {
  85. if( currentParam ) {
  86. // found '=' before terminating previous param.
  87. return NO;
  88. }
  89. currentParam = [[NSString alloc] initWithBytes:bytes length:offset encoding:NSASCIIStringEncoding];
  90. bytes+=offset + 1;
  91. length -= offset + 1;
  92. offset = 0;
  93. continue;
  94. }
  95. if( bytes[offset] == ';' ) {
  96. if( !currentParam ) {
  97. // found ; before stating '='.
  98. HTTPLogError(@"MultipartFormDataParser: Unexpected ';' when parsing header");
  99. return NO;
  100. }
  101. NSString* paramValue = extractParamValue(bytes, offset,encoding);
  102. if( nil == paramValue ) {
  103. HTTPLogWarn(@"MultipartFormDataParser: Failed to exctract paramValue for key %@ in header %@",currentParam,name);
  104. }
  105. else {
  106. #ifdef DEBUG
  107. if( [params objectForKey:currentParam] ) {
  108. HTTPLogWarn(@"MultipartFormDataParser: param %@ mentioned more then once in header %@",currentParam,name);
  109. }
  110. #endif
  111. [params setObject:paramValue forKey:currentParam];
  112. HTTPLogVerbose(@"MultipartFormDataParser: header param: %@ = %@",currentParam,paramValue);
  113. }
  114. currentParam = nil;
  115. // ';' separator has ' ' following, skip them.
  116. bytes+=offset + 2;
  117. length -= offset + 2;
  118. offset = 0;
  119. }
  120. ++ offset;
  121. }
  122. // add last param
  123. if( insideQuote ) {
  124. HTTPLogWarn(@"MultipartFormDataParser: unterminated quote in header %@",name);
  125. // return YES;
  126. }
  127. if( currentParam ) {
  128. NSString* paramValue = extractParamValue(bytes, length, encoding);
  129. if( nil == paramValue ) {
  130. HTTPLogError(@"MultipartFormDataParser: Failed to exctract paramValue for key %@ in header %@",currentParam,name);
  131. }
  132. #ifdef DEBUG
  133. if( [params objectForKey:currentParam] ) {
  134. HTTPLogWarn(@"MultipartFormDataParser: param %@ mentioned more then once in one header",currentParam);
  135. }
  136. #endif
  137. [params setObject:paramValue forKey:currentParam];
  138. HTTPLogVerbose(@"MultipartFormDataParser: header param: %@ = %@",currentParam,paramValue);
  139. currentParam = nil;
  140. }
  141. return YES;
  142. }
  143. - (NSString *)description {
  144. return [NSString stringWithFormat:@"%@:%@\n params: %@",name,value,params];
  145. }
  146. @end
  147. int findChar(const char* str, NSUInteger length, char c) {
  148. int offset = 0;
  149. while( offset < length ) {
  150. if( str[offset] == c )
  151. return offset;
  152. ++ offset;
  153. }
  154. return -1;
  155. }
  156. NSString* extractParamValue(const char* bytes, NSUInteger length, NSStringEncoding encoding) {
  157. if( !length )
  158. return nil;
  159. NSMutableString* value = nil;
  160. if( bytes[0] == '"' ) {
  161. // values may be quoted. Strip the quotes to get what we need.
  162. value = [[NSMutableString alloc] initWithBytes:bytes + 1 length: length - 2 encoding:encoding];
  163. }
  164. else {
  165. value = [[NSMutableString alloc] initWithBytes:bytes length: length encoding:encoding];
  166. }
  167. // restore escaped symbols
  168. NSRange range= [value rangeOfString:@"\\"];
  169. while ( range.length ) {
  170. [value deleteCharactersInRange:range];
  171. range.location ++;
  172. range = [value rangeOfString:@"\\" options:NSLiteralSearch range: range];
  173. }
  174. return value;
  175. }