| Index: chrome/browser/extensions/api/web_request/post_data_parser.cc
|
| diff --git a/chrome/browser/extensions/api/web_request/post_data_parser.cc b/chrome/browser/extensions/api/web_request/post_data_parser.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..34e1714d26829e157bebe02eaf9d7477bac8070d
|
| --- /dev/null
|
| +++ b/chrome/browser/extensions/api/web_request/post_data_parser.cc
|
| @@ -0,0 +1,303 @@
|
| +// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "chrome/browser/extensions/api/web_request/post_data_parser.h"
|
| +
|
| +#include "net/base/escape.h"
|
| +#include "net/url_request/url_request.h"
|
| +
|
| +namespace {
|
| +const char kContentDisposition[] = "Content-Disposition:";
|
| +}
|
| +
|
| +namespace extensions {
|
| +
|
| +// Implementation of PostDataParser and PostDataParser::Result .
|
| +
|
| +PostDataParser::Result::Result() {}
|
| +PostDataParser::Result::~Result() {}
|
| +
|
| +void PostDataParser::Result::Reset() {
|
| + key_.erase();
|
| + val_.erase();
|
| +}
|
| +
|
| +void PostDataParser::Result::SetKey(const char* s, size_t n) {
|
| + key_.replace(0, std::string::npos, s, n);
|
| +}
|
| +
|
| +void PostDataParser::Result::SetVal(const char* s, size_t n) {
|
| + val_.replace(0, std::string::npos, s, n);
|
| +}
|
| +
|
| +void PostDataParser::Result::SetKey(const std::string& str) {
|
| + key_ = str;
|
| +}
|
| +
|
| +void PostDataParser::Result::SetVal(const std::string& str) {
|
| + val_ = str;
|
| +}
|
| +
|
| +PostDataParser::~PostDataParser() {}
|
| +
|
| +// static
|
| +scoped_ptr<PostDataParser> PostDataParser::CreatePostDataParser(
|
| + net::URLRequest* request) {
|
| + std::string value;
|
| + const bool found = request->extra_request_headers().GetHeader(
|
| + "Content-Type", &value);
|
| + return CreatePostDataParser(found ? &value : NULL);
|
| +}
|
| +
|
| +// static
|
| +scoped_ptr<PostDataParser> PostDataParser::CreatePostDataParser(
|
| + const std::string* content_type_header) {
|
| + enum ParserChoice {kUrlEncoded, kMultipart, kError};
|
| + ParserChoice choice = kError;
|
| + std::string boundary;
|
| +
|
| + if (content_type_header == NULL) {
|
| + choice = kUrlEncoded;
|
| + } else {
|
| + const std::string content_type(
|
| + content_type_header->substr(0, content_type_header->find(';')));
|
| + if (content_type == "application/x-www-form-urlencoded") {
|
| + choice = kUrlEncoded;
|
| + } else if (content_type == "multipart/form-data") {
|
| + const char kBoundaryString[] = "boundary=";
|
| + size_t offset = content_type_header->find(kBoundaryString);
|
| + if (offset == std::string::npos) {
|
| + // Malformed header.
|
| + return scoped_ptr<PostDataParser>();
|
| + }
|
| + offset += strlen(kBoundaryString);
|
| + boundary = content_type_header->substr(
|
| + offset, content_type_header->find(';', offset));
|
| + choice = kMultipart;
|
| + }
|
| + }
|
| + // Other cases are unparseable, including when |content_type| is "text/plain".
|
| +
|
| + switch (choice) {
|
| + case kUrlEncoded:
|
| + return scoped_ptr<PostDataParser>(new PostDataParserUrlEncoded());
|
| + case kMultipart:
|
| + return scoped_ptr<PostDataParser>(new PostDataParserMultipart(boundary));
|
| + default: // In other words, case kError:
|
| + return scoped_ptr<PostDataParser>();
|
| + }
|
| +}
|
| +
|
| +// Implementation of PostDataParserUrlEncoded.
|
| +
|
| +PostDataParserUrlEncoded::PostDataParserUrlEncoded() : source_(NULL) {}
|
| +
|
| +PostDataParserUrlEncoded::~PostDataParserUrlEncoded() {}
|
| +
|
| +bool PostDataParserUrlEncoded::AllDataReadOK() {
|
| + return source_ != NULL && offset_ == source_->end();
|
| +}
|
| +
|
| +bool PostDataParserUrlEncoded::GetNextPair(Result* result) {
|
| + result->Reset();
|
| + if (source_ == NULL)
|
| + return false;
|
| + if (offset_ == source_->end())
|
| + return false;
|
| + std::vector<char>::const_iterator seek = offset_;
|
| + // (*) Now we have |seek| >= |offset_| until the end of this function:
|
| + while (seek != source_->end() && *seek != '=')
|
| + ++seek;
|
| + if (seek == source_->end()) {
|
| + // This means the data is malformed.
|
| + offset_ = seek;
|
| + return false;
|
| + }
|
| + std::string encoded_key(&(*offset_), seek - offset_); // Safe, see (*).
|
| + const net::UnescapeRule::Type unescape_rules =
|
| + net::UnescapeRule::URL_SPECIAL_CHARS | net::UnescapeRule::CONTROL_CHARS |
|
| + net::UnescapeRule::SPACES | net::UnescapeRule::REPLACE_PLUS_WITH_SPACE;
|
| + result->SetKey(net::UnescapeURLComponent(encoded_key, unescape_rules));
|
| + offset_ = ++seek;
|
| + while (seek != source_->end() && *seek != '&')
|
| + ++seek;
|
| + std::string encoded_val(&(*offset_), seek - offset_); // Safe, see (*).
|
| + result->SetVal(net::UnescapeURLComponent(encoded_val, unescape_rules));
|
| + offset_ = (seek == source_->end()) ? seek : seek + 1;
|
| + return true;
|
| +}
|
| +
|
| +bool PostDataParserUrlEncoded::SetSource(const std::vector<char>* source) {
|
| + if (source_ != NULL)
|
| + return false;
|
| + source_ = source;
|
| + offset_ = source_->begin();
|
| + return true;
|
| +}
|
| +
|
| +// Implementation of PostDataParserMultipart.
|
| +
|
| +PostDataParserMultipart::PostDataParserMultipart(
|
| + const std::string& boundary_separator)
|
| + : source_(NULL),
|
| + length_(0), // Dummy value.
|
| + line_start_(0), // Dummy value.
|
| + line_end_(0), // Dummy value.
|
| + next_line_(0), // Dummy value.
|
| + boundary_("--" + boundary_separator),
|
| + final_boundary_(boundary_ + "--"),
|
| + state_(kInit),
|
| + line_type_(kEmpty) // Dummy value.
|
| +{}
|
| +
|
| +PostDataParserMultipart::~PostDataParserMultipart() {}
|
| +
|
| +bool PostDataParserMultipart::AllDataReadOK() {
|
| + return source_ != NULL && next_line_ >= length_ && state_ == kFinal;
|
| +}
|
| +
|
| +// This function reads one block of the data, between two boundaries.
|
| +// First it reads the header to learn the key, and possibly also the
|
| +// value, if this block is for a file input element.
|
| +// Otherwise it then reads the value from the body.
|
| +bool PostDataParserMultipart::GetNextPair(Result* result) {
|
| + result->Reset();
|
| + if (state_ == kError)
|
| + return false;
|
| + while (state_ != kHeadRead) {
|
| + if (!DoStep())
|
| + return false;
|
| + }
|
| + bool val_extracted = false;
|
| + bool name_parsed = ParseHead(result, &val_extracted);
|
| + while (state_ != kBody) {
|
| + if (!DoStep())
|
| + return false;
|
| + }
|
| + size_t val_start;
|
| + size_t val_end = 0; // Dummy value, replaced below, see (*).
|
| + // There may not be more to read from |source_| if the current result comes
|
| + // from a "file" input element. But then |result| is complete already.
|
| + if (!DoStep())
|
| + return val_extracted;
|
| + val_start = line_start_;
|
| + // (*) Now state_ == kBody, so val_end gets updated below.
|
| + while (state_ != kHeadStart && state_ != kFinal) {
|
| + val_end = line_end_;
|
| + if (!DoStep()) break;
|
| + }
|
| + if (name_parsed && !val_extracted) {
|
| + result->SetVal(source_ + val_start, val_end - val_start);
|
| + }
|
| + return name_parsed;
|
| +}
|
| +
|
| +bool PostDataParserMultipart::SetSource(const std::vector<char>* source) {
|
| + if (state_ == kError)
|
| + return false;
|
| + if (source_ != NULL && next_line_ < length_)
|
| + return false;
|
| + source_ = &(source->front());
|
| + length_ = source->size();
|
| + next_line_ = 0;
|
| + return true;
|
| +}
|
| +
|
| +bool PostDataParserMultipart::DoStep() {
|
| + if (!SeekNextLine())
|
| + return false;
|
| + switch (state_) {
|
| + case kInit:
|
| + if (line_type_ == kBoundary)
|
| + state_ = kHeadStart;
|
| + else
|
| + state_ = kError;
|
| + break;
|
| + case kHeadStart:
|
| + if (line_type_ == kDisposition)
|
| + state_ = kHeadRead;
|
| + else
|
| + state_ = kHead;
|
| + break;
|
| + case kHead:
|
| + if (line_type_ == kDisposition)
|
| + state_ = kHeadRead;
|
| + break;
|
| + case kHeadRead:
|
| + if (line_type_ == kEmpty)
|
| + state_ = kBody;
|
| + break;
|
| + case kBody:
|
| + if (line_type_ == kBoundary)
|
| + state_ = kHeadStart;
|
| + else if (line_type_ == kEndBoundary)
|
| + state_ = kFinal;
|
| + break;
|
| + case kFinal:
|
| + if (line_type_ != kEmpty)
|
| + state_ = kError;
|
| + case kError:
|
| + break;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +PostDataParserMultipart::LineType PostDataParserMultipart::GetLineType() {
|
| + const size_t line_length = line_end_ - line_start_;
|
| + const base::StringPiece line(source_ + line_start_, line_length);
|
| + if (line == boundary_)
|
| + return kBoundary;
|
| + else if (line == final_boundary_)
|
| + return kEndBoundary;
|
| + else if (line.starts_with(kContentDisposition))
|
| + return kDisposition;
|
| + else if (line_start_ == line_end_)
|
| + return kEmpty;
|
| + else
|
| + return kOther;
|
| +}
|
| +
|
| +// Contract: only to be called from DoStep().
|
| +bool PostDataParserMultipart::SeekNextLine() {
|
| + if (source_ == NULL || state_ == kError)
|
| + return false;
|
| + if (next_line_ >= length_)
|
| + return false;
|
| + line_start_ = next_line_;
|
| + size_t seek = line_start_;
|
| + while (seek < length_ && *(source_ + seek) != '\r')
|
| + ++seek;
|
| + line_end_ = seek;
|
| + line_type_ = GetLineType();
|
| + if ((seek+1) < length_ && strncmp(source_ + seek, "\r\n", 2) != 0)
|
| + return false;
|
| + next_line_ = seek + 2;
|
| + return true;
|
| +}
|
| +
|
| +// Contract: line_type_ == kDisposition.
|
| +bool PostDataParserMultipart::ParseHead(Result* result, bool* val_extracted) {
|
| + DCHECK_EQ(kDisposition, line_type_);
|
| + base::StringPiece line(source_ + line_start_, line_end_ - line_start_);
|
| + const char kNameEquals[] = " name=\"";
|
| + const char kFilenameEquals[] = " filename=\"";
|
| + size_t key_offset = line.find(kNameEquals);
|
| + if (key_offset == base::StringPiece::npos)
|
| + return false;
|
| + key_offset += strlen(kNameEquals);
|
| + result->SetKey(source_ + line_start_ + key_offset,
|
| + line.find('"', key_offset) - key_offset);
|
| + size_t val_offset = line.find(kFilenameEquals);
|
| + if (val_offset == std::string::npos) {
|
| + *val_extracted = false;
|
| + } else {
|
| + *val_extracted = true;
|
| + val_offset += strlen(kFilenameEquals);
|
| + result->SetVal(source_ + line_start_ + val_offset,
|
| + line.find('"', val_offset) - val_offset);
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +} // namespace extensions
|
|
|