OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // Portions of this code based on Mozilla: | 5 // Portions of this code based on Mozilla: |
6 // (netwerk/cookie/src/nsCookieService.cpp) | 6 // (netwerk/cookie/src/nsCookieService.cpp) |
7 /* ***** BEGIN LICENSE BLOCK ***** | 7 /* ***** BEGIN LICENSE BLOCK ***** |
8 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 | 8 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
9 * | 9 * |
10 * The contents of this file are subject to the Mozilla Public License Version | 10 * The contents of this file are subject to the Mozilla Public License Version |
(...skipping 27 matching lines...) Expand all Loading... | |
38 * decision by deleting the provisions above and replace them with the notice | 38 * decision by deleting the provisions above and replace them with the notice |
39 * and other provisions required by the GPL or the LGPL. If you do not delete | 39 * and other provisions required by the GPL or the LGPL. If you do not delete |
40 * the provisions above, a recipient may use your version of this file under | 40 * the provisions above, a recipient may use your version of this file under |
41 * the terms of any one of the MPL, the GPL or the LGPL. | 41 * the terms of any one of the MPL, the GPL or the LGPL. |
42 * | 42 * |
43 * ***** END LICENSE BLOCK ***** */ | 43 * ***** END LICENSE BLOCK ***** */ |
44 | 44 |
45 #include "net/cookies/parsed_cookie.h" | 45 #include "net/cookies/parsed_cookie.h" |
46 | 46 |
47 #include "base/logging.h" | 47 #include "base/logging.h" |
48 #include "base/string_tokenizer.h" | |
48 #include "base/string_util.h" | 49 #include "base/string_util.h" |
49 | 50 |
51 using base::Time; | |
52 | |
50 namespace net { | 53 namespace net { |
51 | 54 |
52 ParsedCookie::ParsedCookie(const std::string& cookie_line) | 55 ParsedCookie::ParsedCookie(const std::string& cookie_line) |
53 : is_valid_(false), | 56 : is_valid_(false), |
54 path_index_(0), | 57 path_index_(0), |
55 domain_index_(0), | 58 domain_index_(0), |
56 mac_key_index_(0), | 59 mac_key_index_(0), |
57 mac_algorithm_index_(0), | 60 mac_algorithm_index_(0), |
58 expires_index_(0), | 61 expires_index_(0), |
59 maxage_index_(0), | 62 maxage_index_(0), |
(...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
271 StringToLowerASCII(&pair.first); | 274 StringToLowerASCII(&pair.first); |
272 pairs_.push_back(pair); | 275 pairs_.push_back(pair); |
273 | 276 |
274 // We've processed a token/value pair, we're either at the end of | 277 // We've processed a token/value pair, we're either at the end of |
275 // the string or a ValueSeparator like ';', which we want to skip. | 278 // the string or a ValueSeparator like ';', which we want to skip. |
276 if (it != end) | 279 if (it != end) |
277 ++it; | 280 ++it; |
278 } | 281 } |
279 } | 282 } |
280 | 283 |
284 // Parse a cookie expiration time. We try to be lenient, but we need to | |
erikwright (departed)
2012/07/16 21:13:20
I think cookie_util.{h,cc} would be a better place
battre
2012/07/17 15:01:54
Done.
| |
285 // assume some order to distinguish the fields. The basic rules: | |
286 // - The month name must be present and prefix the first 3 letters of the | |
287 // full month name (jan for January, jun for June). | |
288 // - If the year is <= 2 digits, it must occur after the day of month. | |
289 // - The time must be of the format hh:mm:ss. | |
290 // An average cookie expiration will look something like this: | |
291 // Sat, 15-Apr-17 21:01:22 GMT | |
292 // | |
293 // static | |
294 Time ParsedCookie::ParseCookieTime(const std::string& time_string) { | |
295 static const char* kMonths[] = { "jan", "feb", "mar", "apr", "may", "jun", | |
296 "jul", "aug", "sep", "oct", "nov", "dec" }; | |
297 static const int kMonthsLen = arraysize(kMonths); | |
298 // We want to be pretty liberal, and support most non-ascii and non-digit | |
299 // characters as a delimiter. We can't treat : as a delimiter, because it | |
300 // is the delimiter for hh:mm:ss, and we want to keep this field together. | |
301 // We make sure to include - and +, since they could prefix numbers. | |
302 // If the cookie attribute came in in quotes (ex expires="XXX"), the quotes | |
303 // will be preserved, and we will get them here. So we make sure to include | |
304 // quote characters, and also \ for anything that was internally escaped. | |
305 static const char* kDelimiters = "\t !\"#$%&'()*+,-./;<=>?@[\\]^_`{|}~"; | |
306 | |
307 Time::Exploded exploded = {0}; | |
308 | |
309 StringTokenizer tokenizer(time_string, kDelimiters); | |
310 | |
311 bool found_day_of_month = false; | |
312 bool found_month = false; | |
313 bool found_time = false; | |
314 bool found_year = false; | |
315 | |
316 while (tokenizer.GetNext()) { | |
317 const std::string token = tokenizer.token(); | |
318 DCHECK(!token.empty()); | |
319 bool numerical = IsAsciiDigit(token[0]); | |
320 | |
321 // String field | |
322 if (!numerical) { | |
323 if (!found_month) { | |
324 for (int i = 0; i < kMonthsLen; ++i) { | |
325 // Match prefix, so we could match January, etc | |
326 if (base::strncasecmp(token.c_str(), kMonths[i], 3) == 0) { | |
327 exploded.month = i + 1; | |
328 found_month = true; | |
329 break; | |
330 } | |
331 } | |
332 } else { | |
333 // If we've gotten here, it means we've already found and parsed our | |
334 // month, and we have another string, which we would expect to be the | |
335 // the time zone name. According to the RFC and my experiments with | |
336 // how sites format their expirations, we don't have much of a reason | |
337 // to support timezones. We don't want to ever barf on user input, | |
338 // but this DCHECK should pass for well-formed data. | |
339 // DCHECK(token == "GMT"); | |
340 } | |
341 // Numeric field w/ a colon | |
342 } else if (token.find(':') != std::string::npos) { | |
343 if (!found_time && | |
344 #ifdef COMPILER_MSVC | |
345 sscanf_s( | |
346 #else | |
347 sscanf( | |
348 #endif | |
349 token.c_str(), "%2u:%2u:%2u", &exploded.hour, | |
350 &exploded.minute, &exploded.second) == 3) { | |
351 found_time = true; | |
352 } else { | |
353 // We should only ever encounter one time-like thing. If we're here, | |
354 // it means we've found a second, which shouldn't happen. We keep | |
355 // the first. This check should be ok for well-formed input: | |
356 // NOTREACHED(); | |
357 } | |
358 // Numeric field | |
359 } else { | |
360 // Overflow with atoi() is unspecified, so we enforce a max length. | |
361 if (!found_day_of_month && token.length() <= 2) { | |
362 exploded.day_of_month = atoi(token.c_str()); | |
363 found_day_of_month = true; | |
364 } else if (!found_year && token.length() <= 5) { | |
365 exploded.year = atoi(token.c_str()); | |
366 found_year = true; | |
367 } else { | |
368 // If we're here, it means we've either found an extra numeric field, | |
369 // or a numeric field which was too long. For well-formed input, the | |
370 // following check would be reasonable: | |
371 // NOTREACHED(); | |
372 } | |
373 } | |
374 } | |
375 | |
376 if (!found_day_of_month || !found_month || !found_time || !found_year) { | |
377 // We didn't find all of the fields we need. For well-formed input, the | |
378 // following check would be reasonable: | |
379 // NOTREACHED() << "Cookie parse expiration failed: " << time_string; | |
380 return Time(); | |
381 } | |
382 | |
383 // Normalize the year to expand abbreviated years to the full year. | |
384 if (exploded.year >= 69 && exploded.year <= 99) | |
385 exploded.year += 1900; | |
386 if (exploded.year >= 0 && exploded.year <= 68) | |
387 exploded.year += 2000; | |
388 | |
389 // If our values are within their correct ranges, we got our time. | |
390 if (exploded.day_of_month >= 1 && exploded.day_of_month <= 31 && | |
391 exploded.month >= 1 && exploded.month <= 12 && | |
392 exploded.year >= 1601 && exploded.year <= 30827 && | |
393 exploded.hour <= 23 && exploded.minute <= 59 && exploded.second <= 59) { | |
394 return Time::FromUTCExploded(exploded); | |
395 } | |
396 | |
397 // One of our values was out of expected range. For well-formed input, | |
398 // the following check would be reasonable: | |
399 // NOTREACHED() << "Cookie exploded expiration failed: " << time_string; | |
400 | |
401 return Time(); | |
402 } | |
403 | |
281 void ParsedCookie::SetupAttributes() { | 404 void ParsedCookie::SetupAttributes() { |
282 static const char kPathTokenName[] = "path"; | 405 static const char kPathTokenName[] = "path"; |
283 static const char kDomainTokenName[] = "domain"; | 406 static const char kDomainTokenName[] = "domain"; |
284 static const char kMACKeyTokenName[] = "mac-key"; | 407 static const char kMACKeyTokenName[] = "mac-key"; |
285 static const char kMACAlgorithmTokenName[] = "mac-algorithm"; | 408 static const char kMACAlgorithmTokenName[] = "mac-algorithm"; |
286 static const char kExpiresTokenName[] = "expires"; | 409 static const char kExpiresTokenName[] = "expires"; |
287 static const char kMaxAgeTokenName[] = "max-age"; | 410 static const char kMaxAgeTokenName[] = "max-age"; |
288 static const char kSecureTokenName[] = "secure"; | 411 static const char kSecureTokenName[] = "secure"; |
289 static const char kHttpOnlyTokenName[] = "httponly"; | 412 static const char kHttpOnlyTokenName[] = "httponly"; |
290 | 413 |
(...skipping 15 matching lines...) Expand all Loading... | |
306 secure_index_ = i; | 429 secure_index_ = i; |
307 } else if (pairs_[i].first == kHttpOnlyTokenName) { | 430 } else if (pairs_[i].first == kHttpOnlyTokenName) { |
308 httponly_index_ = i; | 431 httponly_index_ = i; |
309 } else { | 432 } else { |
310 /* some attribute we don't know or don't care about. */ | 433 /* some attribute we don't know or don't care about. */ |
311 } | 434 } |
312 } | 435 } |
313 } | 436 } |
314 | 437 |
315 } // namespace | 438 } // namespace |
OLD | NEW |