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 char* s, size_t n) { | |
41 key_.replace(0, std::string::npos, s, n); | |
42 } | |
43 | |
44 void PostDataParser::Result::SetVal(const char* s, size_t n) { | |
45 val_.replace(0, std::string::npos, s, n); | |
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()))) continue; | |
battre
2012/07/30 17:54:25
nit: could you move the continue to the next line?
vabr (Chromium)
2012/07/31 09:03:18
Done.
| |
130 Result result; | |
131 while (parser->GetNextPair(&result)) { | |
132 GetOrCreateList(form_data.get(), result.get_key())->Append( | |
133 new StringValue(result.get_val())); | |
134 } | |
135 } | |
136 if (data_valid && parser->AllDataReadOK()) | |
137 return form_data.Pass(); | |
138 else | |
139 return scoped_ptr<base::DictionaryValue>(); | |
140 } | |
141 | |
142 // Implementation of PostDataParserUrlEncoded. | |
143 | |
144 PostDataParserUrlEncoded::PostDataParserUrlEncoded() : source_(NULL) {} | |
145 | |
146 PostDataParserUrlEncoded::~PostDataParserUrlEncoded() {} | |
147 | |
148 bool PostDataParserUrlEncoded::AllDataReadOK() { | |
149 return source_ != NULL && offset_ == source_->end(); | |
150 } | |
151 | |
152 bool PostDataParserUrlEncoded::GetNextPair(Result* result) { | |
153 result->Reset(); | |
154 if (source_ == NULL) | |
155 return false; | |
156 if (offset_ == source_->end()) | |
157 return false; | |
158 std::vector<char>::const_iterator seek = offset_; | |
159 // (*) Now we have |seek| >= |offset_| until the end of this function: | |
160 while (seek != source_->end() && *seek != '=') | |
161 ++seek; | |
162 if (seek == source_->end()) { | |
163 // This means the data is malformed. | |
164 offset_ = seek; | |
165 return false; | |
166 } | |
167 std::string encoded_key(&(*offset_), seek - offset_); // Safe, see (*). | |
168 const net::UnescapeRule::Type unescape_rules = | |
169 net::UnescapeRule::URL_SPECIAL_CHARS | net::UnescapeRule::CONTROL_CHARS | | |
170 net::UnescapeRule::SPACES | net::UnescapeRule::REPLACE_PLUS_WITH_SPACE; | |
171 result->SetKey(net::UnescapeURLComponent(encoded_key, unescape_rules)); | |
172 offset_ = ++seek; | |
173 while (seek != source_->end() && *seek != '&') | |
174 ++seek; | |
175 std::string encoded_val(&(*offset_), seek - offset_); // Safe, see (*). | |
176 result->SetVal(net::UnescapeURLComponent(encoded_val, unescape_rules)); | |
177 offset_ = (seek == source_->end()) ? seek : seek + 1; | |
178 return true; | |
179 } | |
180 | |
181 bool PostDataParserUrlEncoded::SetSource(const std::vector<char>* source) { | |
182 if (source_ != NULL) | |
183 return false; | |
184 source_ = source; | |
185 offset_ = source_->begin(); | |
186 return true; | |
187 } | |
188 | |
189 // Implementation of PostDataParserMultipart. | |
190 | |
191 PostDataParserMultipart::PostDataParserMultipart( | |
192 const std::string& boundary_separator) | |
193 : source_(NULL), | |
194 length_(0), // Dummy value. | |
195 line_start_(0), // Dummy value. | |
196 line_end_(0), // Dummy value. | |
197 next_line_(0), // Dummy value. | |
198 boundary_("--" + boundary_separator), | |
199 final_boundary_(boundary_ + "--"), | |
200 state_(kInit), | |
201 line_type_(kEmpty) // Dummy value. | |
202 {} | |
203 | |
204 PostDataParserMultipart::~PostDataParserMultipart() {} | |
205 | |
206 bool PostDataParserMultipart::AllDataReadOK() { | |
207 return source_ != NULL && next_line_ >= length_ && state_ == kFinal; | |
208 } | |
209 | |
210 // This function reads one block of the data, between two boundaries. | |
211 // First it reads the header to learn the key, and possibly also the | |
212 // value, if this block is for a file input element. | |
213 // Otherwise it then reads the value from the body. | |
214 bool PostDataParserMultipart::GetNextPair(Result* result) { | |
215 result->Reset(); | |
216 if (state_ == kError) | |
217 return false; | |
218 while (state_ != kHeadRead) { | |
219 if (!DoStep()) | |
220 return false; | |
221 } | |
222 bool val_extracted = false; | |
223 bool name_parsed = ParseHead(result, &val_extracted); | |
224 while (state_ != kBody) { | |
225 if (!DoStep()) | |
226 return false; | |
227 } | |
228 size_t val_start; | |
229 size_t val_end = 0; // Dummy value, replaced below, see (*). | |
230 // There may not be more to read from |source_| if the current result comes | |
231 // from a "file" input element. But then |result| is complete already. | |
232 if (!DoStep()) | |
233 return val_extracted; | |
234 val_start = line_start_; | |
235 // (*) Now state_ == kBody, so val_end gets updated below. | |
236 while (state_ != kHeadStart && state_ != kFinal) { | |
237 val_end = line_end_; | |
238 if (!DoStep()) break; | |
239 } | |
240 if (name_parsed && !val_extracted) { | |
241 result->SetVal(source_ + val_start, val_end - val_start); | |
242 } | |
243 return name_parsed; | |
244 } | |
245 | |
246 bool PostDataParserMultipart::SetSource(const std::vector<char>* source) { | |
247 if (state_ == kError) | |
248 return false; | |
249 if (source_ != NULL && next_line_ < length_) | |
250 return false; | |
251 source_ = &(source->front()); | |
252 length_ = source->size(); | |
253 next_line_ = 0; | |
254 return true; | |
255 } | |
256 | |
257 bool PostDataParserMultipart::DoStep() { | |
258 if (!SeekNextLine()) | |
259 return false; | |
260 switch (state_) { | |
261 case kInit: | |
262 if (line_type_ == kBoundary) | |
263 state_ = kHeadStart; | |
264 else | |
265 state_ = kError; | |
266 break; | |
267 case kHeadStart: | |
268 if (line_type_ == kDisposition) | |
269 state_ = kHeadRead; | |
270 else | |
271 state_ = kHead; | |
272 break; | |
273 case kHead: | |
274 if (line_type_ == kDisposition) | |
275 state_ = kHeadRead; | |
276 break; | |
277 case kHeadRead: | |
278 if (line_type_ == kEmpty) | |
279 state_ = kBody; | |
280 break; | |
281 case kBody: | |
282 if (line_type_ == kBoundary) | |
283 state_ = kHeadStart; | |
284 else if (line_type_ == kEndBoundary) | |
285 state_ = kFinal; | |
286 break; | |
287 case kFinal: | |
288 if (line_type_ != kEmpty) | |
289 state_ = kError; | |
290 case kError: | |
291 break; | |
292 } | |
293 return true; | |
294 } | |
295 | |
296 PostDataParserMultipart::LineType PostDataParserMultipart::GetLineType() { | |
297 const size_t line_length = line_end_ - line_start_; | |
298 const base::StringPiece line(source_ + line_start_, line_length); | |
299 if (line == boundary_) | |
300 return kBoundary; | |
301 else if (line == final_boundary_) | |
302 return kEndBoundary; | |
303 else if (line.starts_with(kContentDisposition)) | |
304 return kDisposition; | |
305 else if (line_start_ == line_end_) | |
306 return kEmpty; | |
307 else | |
308 return kOther; | |
309 } | |
310 | |
311 // Contract: only to be called from DoStep(). | |
312 bool PostDataParserMultipart::SeekNextLine() { | |
313 if (source_ == NULL || state_ == kError) | |
314 return false; | |
315 if (next_line_ >= length_) | |
316 return false; | |
317 line_start_ = next_line_; | |
318 size_t seek = line_start_; | |
319 while (seek < length_ && *(source_ + seek) != '\r') | |
320 ++seek; | |
321 line_end_ = seek; | |
322 line_type_ = GetLineType(); | |
323 if ((seek+1) < length_ && strncmp(source_ + seek, "\r\n", 2) != 0) | |
324 return false; | |
325 next_line_ = seek + 2; | |
326 return true; | |
327 } | |
328 | |
329 // Contract: line_type_ == kDisposition. | |
330 bool PostDataParserMultipart::ParseHead(Result* result, bool* val_extracted) { | |
331 DCHECK_EQ(kDisposition, line_type_); | |
332 base::StringPiece line(source_ + line_start_, line_end_ - line_start_); | |
333 const char kNameEquals[] = " name=\""; | |
334 const char kFilenameEquals[] = " filename=\""; | |
335 size_t key_offset = line.find(kNameEquals); | |
336 if (key_offset == base::StringPiece::npos) | |
337 return false; | |
338 key_offset += strlen(kNameEquals); | |
339 result->SetKey(source_ + line_start_ + key_offset, | |
340 line.find('"', key_offset) - key_offset); | |
341 size_t val_offset = line.find(kFilenameEquals); | |
342 if (val_offset == std::string::npos) { | |
343 *val_extracted = false; | |
344 } else { | |
345 *val_extracted = true; | |
346 val_offset += strlen(kFilenameEquals); | |
347 result->SetVal(source_ + line_start_ + val_offset, | |
348 line.find('"', val_offset) - val_offset); | |
349 } | |
350 return true; | |
351 } | |
352 | |
353 } // namespace extensions | |
OLD | NEW |