OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "chrome/browser/extensions/api/web_request/post_data_parser.h" |
| 6 |
| 7 #include "base/values.h" |
| 8 #include "net/base/escape.h" |
| 9 #include "net/base/upload_data.h" |
| 10 #include "net/url_request/url_request.h" |
| 11 |
| 12 namespace { |
| 13 const char kContentDisposition[] = "Content-Disposition:"; |
| 14 |
| 15 // Takes |dictionary| of <string, list of strings> pairs, and gets the list |
| 16 // for |key|, creating it if necessary. |
| 17 ListValue* GetOrCreateList(DictionaryValue* dictionary, |
| 18 const std::string& key) { |
| 19 ListValue* list = NULL; |
| 20 if (!dictionary->GetList(key, &list)) { |
| 21 list = new ListValue(); |
| 22 dictionary->Set(key, list); |
| 23 } |
| 24 return list; |
| 25 } |
| 26 } |
| 27 |
| 28 namespace extensions { |
| 29 |
| 30 // Implementation of PostDataParser and PostDataParser::Result . |
| 31 |
| 32 PostDataParser::Result::Result() {} |
| 33 PostDataParser::Result::~Result() {} |
| 34 |
| 35 void PostDataParser::Result::Reset() { |
| 36 key_.erase(); |
| 37 val_.erase(); |
| 38 } |
| 39 |
| 40 void PostDataParser::Result::SetKey(const base::StringPiece& str) { |
| 41 key_.replace(0, std::string::npos, str.data(), str.size()); |
| 42 } |
| 43 |
| 44 void PostDataParser::Result::SetVal(const base::StringPiece& str) { |
| 45 val_.replace(0, std::string::npos, str.data(), str.size()); |
| 46 } |
| 47 |
| 48 void PostDataParser::Result::SetKey(const std::string& str) { |
| 49 key_ = str; |
| 50 } |
| 51 |
| 52 void PostDataParser::Result::SetVal(const std::string& str) { |
| 53 val_ = str; |
| 54 } |
| 55 |
| 56 PostDataParser::~PostDataParser() {} |
| 57 |
| 58 // static |
| 59 scoped_ptr<PostDataParser> PostDataParser::CreatePostDataParser( |
| 60 const net::URLRequest* request) { |
| 61 std::string value; |
| 62 const bool found = request->extra_request_headers().GetHeader( |
| 63 net::HttpRequestHeaders::kContentType, &value); |
| 64 return CreatePostDataParser(found ? &value : NULL); |
| 65 } |
| 66 |
| 67 // static |
| 68 scoped_ptr<PostDataParser> PostDataParser::CreatePostDataParser( |
| 69 const std::string* content_type_header) { |
| 70 enum ParserChoice {kUrlEncoded, kMultipart, kError}; |
| 71 ParserChoice choice = kError; |
| 72 std::string boundary; |
| 73 |
| 74 if (content_type_header == NULL) { |
| 75 choice = kUrlEncoded; |
| 76 } else { |
| 77 const std::string content_type( |
| 78 content_type_header->substr(0, content_type_header->find(';'))); |
| 79 if (content_type == "application/x-www-form-urlencoded") { |
| 80 choice = kUrlEncoded; |
| 81 } else if (content_type == "multipart/form-data") { |
| 82 const char kBoundaryString[] = "boundary="; |
| 83 size_t offset = content_type_header->find(kBoundaryString); |
| 84 if (offset == std::string::npos) { |
| 85 // Malformed header. |
| 86 return scoped_ptr<PostDataParser>(); |
| 87 } |
| 88 offset += strlen(kBoundaryString); |
| 89 boundary = content_type_header->substr( |
| 90 offset, content_type_header->find(';', offset)); |
| 91 choice = kMultipart; |
| 92 } |
| 93 } |
| 94 // Other cases are unparseable, including when |content_type| is "text/plain". |
| 95 |
| 96 switch (choice) { |
| 97 case kUrlEncoded: |
| 98 return scoped_ptr<PostDataParser>(new PostDataParserUrlEncoded()); |
| 99 case kMultipart: |
| 100 return scoped_ptr<PostDataParser>(new PostDataParserMultipart(boundary)); |
| 101 default: // In other words, case kError: |
| 102 return scoped_ptr<PostDataParser>(); |
| 103 } |
| 104 } |
| 105 |
| 106 // static |
| 107 scoped_ptr<base::DictionaryValue> PostDataParser::ParseURLRequestData( |
| 108 const net::URLRequest* request) { |
| 109 if (request->method() != "POST") |
| 110 return scoped_ptr<base::DictionaryValue>(); |
| 111 const std::vector<net::UploadData::Element>* elements = |
| 112 request->get_upload()->elements(); |
| 113 scoped_ptr<PostDataParser> parser = CreatePostDataParser(request); |
| 114 if (parser.get() == NULL) { |
| 115 // No parser means most probably unsupported form encoding. |
| 116 return scoped_ptr<base::DictionaryValue>(); |
| 117 } |
| 118 scoped_ptr<base::DictionaryValue> form_data(new base::DictionaryValue); |
| 119 std::vector<net::UploadData::Element>::const_iterator element; |
| 120 bool data_valid = true; |
| 121 for (element = elements->begin(); |
| 122 data_valid && element != elements->end(); ++element) { |
| 123 if (element->type() != net::UploadData::TYPE_BYTES) { |
| 124 // We do not handle data including blobs or chunks. |
| 125 if (element->type() != net::UploadData::TYPE_FILE) |
| 126 data_valid = false; |
| 127 continue; |
| 128 } |
| 129 if (!parser->SetSource(&(element->bytes()))) |
| 130 continue; |
| 131 Result result; |
| 132 while (parser->GetNextPair(&result)) { |
| 133 GetOrCreateList(form_data.get(), result.key())->Append( |
| 134 new StringValue(result.val())); |
| 135 } |
| 136 } |
| 137 if (data_valid && parser->AllDataReadOK()) |
| 138 return form_data.Pass(); |
| 139 else |
| 140 return scoped_ptr<base::DictionaryValue>(); |
| 141 } |
| 142 |
| 143 // Implementation of PostDataParserUrlEncoded. |
| 144 |
| 145 PostDataParserUrlEncoded::PostDataParserUrlEncoded() : source_(NULL) {} |
| 146 |
| 147 PostDataParserUrlEncoded::~PostDataParserUrlEncoded() {} |
| 148 |
| 149 bool PostDataParserUrlEncoded::AllDataReadOK() { |
| 150 return source_ != NULL && offset_ == source_->end(); |
| 151 } |
| 152 |
| 153 bool PostDataParserUrlEncoded::GetNextPair(Result* result) { |
| 154 result->Reset(); |
| 155 if (source_ == NULL) |
| 156 return false; |
| 157 if (offset_ == source_->end()) |
| 158 return false; |
| 159 std::vector<char>::const_iterator seek = offset_; |
| 160 // (*) Now we have |seek| >= |offset_| until the end of this function: |
| 161 while (seek != source_->end() && *seek != '=') |
| 162 ++seek; |
| 163 if (seek == source_->end()) { |
| 164 // This means the data is malformed. |
| 165 offset_ = seek; |
| 166 return false; |
| 167 } |
| 168 std::string encoded_key(&(*offset_), seek - offset_); // Safe, see (*). |
| 169 const net::UnescapeRule::Type unescape_rules = |
| 170 net::UnescapeRule::URL_SPECIAL_CHARS | net::UnescapeRule::CONTROL_CHARS | |
| 171 net::UnescapeRule::SPACES | net::UnescapeRule::REPLACE_PLUS_WITH_SPACE; |
| 172 result->SetKey(net::UnescapeURLComponent(encoded_key, unescape_rules)); |
| 173 offset_ = ++seek; |
| 174 while (seek != source_->end() && *seek != '&') |
| 175 ++seek; |
| 176 std::string encoded_val(&(*offset_), seek - offset_); // Safe, see (*). |
| 177 result->SetVal(net::UnescapeURLComponent(encoded_val, unescape_rules)); |
| 178 offset_ = (seek == source_->end()) ? seek : seek + 1; |
| 179 return true; |
| 180 } |
| 181 |
| 182 bool PostDataParserUrlEncoded::SetSource(const std::vector<char>* source) { |
| 183 if (source_ != NULL) |
| 184 return false; |
| 185 source_ = source; |
| 186 offset_ = source_->begin(); |
| 187 return true; |
| 188 } |
| 189 |
| 190 // Implementation of PostDataParserMultipart. |
| 191 |
| 192 PostDataParserMultipart::PostDataParserMultipart( |
| 193 const std::string& boundary_separator) |
| 194 : source_(NULL), |
| 195 length_(0), // Dummy value. |
| 196 line_start_(0), // Dummy value. |
| 197 line_end_(0), // Dummy value. |
| 198 next_line_(0), // Dummy value. |
| 199 boundary_("--" + boundary_separator), |
| 200 final_boundary_(boundary_ + "--"), |
| 201 state_(kInit), |
| 202 line_type_(kEmpty) // Dummy value. |
| 203 {} |
| 204 |
| 205 PostDataParserMultipart::~PostDataParserMultipart() {} |
| 206 |
| 207 bool PostDataParserMultipart::AllDataReadOK() { |
| 208 return source_ != NULL && next_line_ >= length_ && state_ == kFinal; |
| 209 } |
| 210 |
| 211 // This function reads one block of the data, between two boundaries. |
| 212 // First it reads the header to learn the key, and possibly also the |
| 213 // value, if this block is for a file input element. |
| 214 // Otherwise it then reads the value from the body. |
| 215 bool PostDataParserMultipart::GetNextPair(Result* result) { |
| 216 result->Reset(); |
| 217 if (state_ == kError) |
| 218 return false; |
| 219 while (state_ != kHeadRead) { |
| 220 if (!DoStep()) |
| 221 return false; |
| 222 } |
| 223 bool val_extracted = false; |
| 224 bool name_parsed = ParseHead(result, &val_extracted); |
| 225 while (state_ != kBody) { |
| 226 if (!DoStep()) |
| 227 return false; |
| 228 } |
| 229 size_t val_start; |
| 230 size_t val_end = 0; // Dummy value, replaced below, see (*). |
| 231 // There may not be more to read from |source_| if the current result comes |
| 232 // from a "file" input element. But then |result| is complete already. |
| 233 if (!DoStep()) |
| 234 return val_extracted; |
| 235 val_start = line_start_; |
| 236 // (*) Now state_ == kBody, so val_end gets updated below. |
| 237 while (state_ != kHeadStart && state_ != kFinal) { |
| 238 val_end = line_end_; |
| 239 if (!DoStep()) break; |
| 240 } |
| 241 if (name_parsed && !val_extracted) { |
| 242 result->SetVal(base::StringPiece(source_ + val_start, val_end - val_start)); |
| 243 } |
| 244 return name_parsed; |
| 245 } |
| 246 |
| 247 bool PostDataParserMultipart::SetSource(const std::vector<char>* source) { |
| 248 if (state_ == kError) |
| 249 return false; |
| 250 if (source_ != NULL && next_line_ < length_) |
| 251 return false; |
| 252 source_ = &(source->front()); |
| 253 length_ = source->size(); |
| 254 next_line_ = 0; |
| 255 return true; |
| 256 } |
| 257 |
| 258 bool PostDataParserMultipart::DoStep() { |
| 259 if (!SeekNextLine()) |
| 260 return false; |
| 261 switch (state_) { |
| 262 case kInit: |
| 263 if (line_type_ == kBoundary) |
| 264 state_ = kHeadStart; |
| 265 else |
| 266 state_ = kError; |
| 267 break; |
| 268 case kHeadStart: |
| 269 if (line_type_ == kDisposition) |
| 270 state_ = kHeadRead; |
| 271 else |
| 272 state_ = kHead; |
| 273 break; |
| 274 case kHead: |
| 275 if (line_type_ == kDisposition) |
| 276 state_ = kHeadRead; |
| 277 break; |
| 278 case kHeadRead: |
| 279 if (line_type_ == kEmpty) |
| 280 state_ = kBody; |
| 281 break; |
| 282 case kBody: |
| 283 if (line_type_ == kBoundary) |
| 284 state_ = kHeadStart; |
| 285 else if (line_type_ == kEndBoundary) |
| 286 state_ = kFinal; |
| 287 break; |
| 288 case kFinal: |
| 289 if (line_type_ != kEmpty) |
| 290 state_ = kError; |
| 291 case kError: |
| 292 break; |
| 293 } |
| 294 return true; |
| 295 } |
| 296 |
| 297 PostDataParserMultipart::LineType PostDataParserMultipart::GetLineType() { |
| 298 const size_t line_length = line_end_ - line_start_; |
| 299 const base::StringPiece line(source_ + line_start_, line_length); |
| 300 if (line == boundary_) |
| 301 return kBoundary; |
| 302 else if (line == final_boundary_) |
| 303 return kEndBoundary; |
| 304 else if (line.starts_with(kContentDisposition)) |
| 305 return kDisposition; |
| 306 else if (line_start_ == line_end_) |
| 307 return kEmpty; |
| 308 else |
| 309 return kOther; |
| 310 } |
| 311 |
| 312 // Contract: only to be called from DoStep(). |
| 313 bool PostDataParserMultipart::SeekNextLine() { |
| 314 if (source_ == NULL || state_ == kError) |
| 315 return false; |
| 316 if (next_line_ >= length_) |
| 317 return false; |
| 318 line_start_ = next_line_; |
| 319 size_t seek = line_start_; |
| 320 while (seek < length_ && *(source_ + seek) != '\r') |
| 321 ++seek; |
| 322 line_end_ = seek; |
| 323 line_type_ = GetLineType(); |
| 324 if ((seek+1) < length_ && strncmp(source_ + seek, "\r\n", 2) != 0) |
| 325 return false; |
| 326 next_line_ = seek + 2; |
| 327 return true; |
| 328 } |
| 329 |
| 330 // Contract: line_type_ == kDisposition. |
| 331 bool PostDataParserMultipart::ParseHead(Result* result, bool* val_extracted) { |
| 332 DCHECK_EQ(kDisposition, line_type_); |
| 333 base::StringPiece line(source_ + line_start_, line_end_ - line_start_); |
| 334 const char kNameEquals[] = " name=\""; |
| 335 const char kFilenameEquals[] = " filename=\""; |
| 336 size_t key_offset = line.find(kNameEquals); |
| 337 if (key_offset == base::StringPiece::npos) |
| 338 return false; |
| 339 key_offset += strlen(kNameEquals); |
| 340 result->SetKey(base::StringPiece(source_ + line_start_ + key_offset, |
| 341 line.find('"', key_offset) - key_offset)); |
| 342 size_t val_offset = line.find(kFilenameEquals); |
| 343 if (val_offset == std::string::npos) { |
| 344 *val_extracted = false; |
| 345 } else { |
| 346 *val_extracted = true; |
| 347 val_offset += strlen(kFilenameEquals); |
| 348 result->SetVal(base::StringPiece(source_ + line_start_ + val_offset, |
| 349 line.find('"', val_offset) - val_offset)); |
| 350 } |
| 351 return true; |
| 352 } |
| 353 |
| 354 } // namespace extensions |
OLD | NEW |