Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(250)

Side by Side Diff: net/cookies/parsed_cookie.cc

Issue 10697035: Add a mutable version of CookieMonster::ParsedCookie (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Merged with ToT Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « net/cookies/parsed_cookie.h ('k') | net/cookies/parsed_cookie_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 29 matching lines...) Expand all
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_util.h" 48 #include "base/string_util.h"
49 49
50 namespace {
51
52 const char kPathTokenName[] = "path";
53 const char kDomainTokenName[] = "domain";
54 const char kMACKeyTokenName[] = "mac-key";
55 const char kMACAlgorithmTokenName[] = "mac-algorithm";
56 const char kExpiresTokenName[] = "expires";
57 const char kMaxAgeTokenName[] = "max-age";
58 const char kSecureTokenName[] = "secure";
59 const char kHttpOnlyTokenName[] = "httponly";
60
61 const char kTerminator[] = "\n\r\0";
62 const int kTerminatorLen = sizeof(kTerminator) - 1;
63 const char kWhitespace[] = " \t";
64 const char kValueSeparator[] = ";";
65 const char kTokenSeparator[] = ";=";
66
67 // Returns true if |c| occurs in |chars|
68 // TODO(erikwright): maybe make this take an iterator, could check for end also?
69 inline bool CharIsA(const char c, const char* chars) {
70 return strchr(chars, c) != NULL;
71 }
72 // Seek the iterator to the first occurrence of a character in |chars|.
73 // Returns true if it hit the end, false otherwise.
74 inline bool SeekTo(std::string::const_iterator* it,
75 const std::string::const_iterator& end,
76 const char* chars) {
77 for (; *it != end && !CharIsA(**it, chars); ++(*it)) {}
78 return *it == end;
79 }
80 // Seek the iterator to the first occurrence of a character not in |chars|.
81 // Returns true if it hit the end, false otherwise.
82 inline bool SeekPast(std::string::const_iterator* it,
83 const std::string::const_iterator& end,
84 const char* chars) {
85 for (; *it != end && CharIsA(**it, chars); ++(*it)) {}
86 return *it == end;
87 }
88 inline bool SeekBackPast(std::string::const_iterator* it,
89 const std::string::const_iterator& end,
90 const char* chars) {
91 for (; *it != end && CharIsA(**it, chars); --(*it)) {}
92 return *it == end;
93 }
94
95 // Validate whether |value| is a valid token according to [RFC2616],
96 // Section 2.2.
97 bool IsValidToken(const std::string& value) {
98 if (value.empty())
99 return false;
100
101 // Check that |value| has no separators.
102 std::string separators = "()<>@,;:\\\"/[]?={} \t";
103 if (value.find_first_of(separators) != std::string::npos)
104 return false;
105
106 // Check that |value| has no CTLs.
107 for (std::string::const_iterator i = value.begin(); i != value.end(); ++i) {
108 if ((*i >= 0 && *i <= 31) || *i >= 127)
109 return false;
110 }
111
112 return true;
113 }
114
115 // Validate value, which may be according to RFC 6265
116 // cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE )
117 // cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
118 // ; US-ASCII characters excluding CTLs,
119 // ; whitespace DQUOTE, comma, semicolon,
120 // ; and backslash
121 bool IsValidCookieValue(const std::string& value) {
122 // Number of characters to skip in validation at beginning and end of string.
123 size_t skip = 0;
124 if (value.size() >= 2 && *value.begin() == '"' && *(value.end()-1) == '"')
125 skip = 1;
126 for (std::string::const_iterator i = value.begin() + skip;
127 i != value.end() - skip; ++i) {
128 bool valid_octet =
129 (*i == 0x21 ||
130 (*i >= 0x23 && *i <= 0x2B) ||
131 (*i >= 0x2D && *i <= 0x3A) ||
132 (*i >= 0x3C && *i <= 0x5B) ||
133 (*i >= 0x5D && *i <= 0x7E));
134 if (!valid_octet)
135 return false;
136 }
137 return true;
138 }
139
140 bool IsValidCookieAttributeValue(const std::string& value) {
141 // The greatest common denominator of cookie attribute values is
142 // <any CHAR except CTLs or ";"> according to RFC 6265.
143 for (std::string::const_iterator i = value.begin(); i != value.end(); ++i) {
144 if ((*i >= 0 && *i <= 31) || *i == ';')
145 return false;
146 }
147 return true;
148 }
149
150 } // namespace
151
50 namespace net { 152 namespace net {
51 153
52 ParsedCookie::ParsedCookie(const std::string& cookie_line) 154 ParsedCookie::ParsedCookie(const std::string& cookie_line)
53 : is_valid_(false), 155 : path_index_(0),
54 path_index_(0),
55 domain_index_(0), 156 domain_index_(0),
56 mac_key_index_(0), 157 mac_key_index_(0),
57 mac_algorithm_index_(0), 158 mac_algorithm_index_(0),
58 expires_index_(0), 159 expires_index_(0),
59 maxage_index_(0), 160 maxage_index_(0),
60 secure_index_(0), 161 secure_index_(0),
61 httponly_index_(0) { 162 httponly_index_(0) {
62 163
63 if (cookie_line.size() > kMaxCookieSize) { 164 if (cookie_line.size() > kMaxCookieSize) {
64 VLOG(1) << "Not parsing cookie, too large: " << cookie_line.size(); 165 VLOG(1) << "Not parsing cookie, too large: " << cookie_line.size();
65 return; 166 return;
66 } 167 }
67 168
68 ParseTokenValuePairs(cookie_line); 169 ParseTokenValuePairs(cookie_line);
69 if (!pairs_.empty()) { 170 if (!pairs_.empty())
70 is_valid_ = true;
71 SetupAttributes(); 171 SetupAttributes();
72 }
73 } 172 }
74 173
75 ParsedCookie::~ParsedCookie() { 174 ParsedCookie::~ParsedCookie() {
76 } 175 }
77 176
78 // Returns true if |c| occurs in |chars| 177 bool ParsedCookie::IsValid() const {
79 // TODO(erikwright): maybe make this take an iterator, could check for end also? 178 return !pairs_.empty();
80 static inline bool CharIsA(const char c, const char* chars) {
81 return strchr(chars, c) != NULL;
82 }
83 // Seek the iterator to the first occurrence of a character in |chars|.
84 // Returns true if it hit the end, false otherwise.
85 static inline bool SeekTo(std::string::const_iterator* it,
86 const std::string::const_iterator& end,
87 const char* chars) {
88 for (; *it != end && !CharIsA(**it, chars); ++(*it)) {}
89 return *it == end;
90 }
91 // Seek the iterator to the first occurrence of a character not in |chars|.
92 // Returns true if it hit the end, false otherwise.
93 static inline bool SeekPast(std::string::const_iterator* it,
94 const std::string::const_iterator& end,
95 const char* chars) {
96 for (; *it != end && CharIsA(**it, chars); ++(*it)) {}
97 return *it == end;
98 }
99 static inline bool SeekBackPast(std::string::const_iterator* it,
100 const std::string::const_iterator& end,
101 const char* chars) {
102 for (; *it != end && CharIsA(**it, chars); --(*it)) {}
103 return *it == end;
104 } 179 }
105 180
106 const char ParsedCookie::kTerminator[] = "\n\r\0"; 181 bool ParsedCookie::SetName(const std::string& name) {
107 const int ParsedCookie::kTerminatorLen = sizeof(kTerminator) - 1; 182 if (!IsValidToken(name))
108 const char ParsedCookie::kWhitespace[] = " \t"; 183 return false;
109 const char ParsedCookie::kValueSeparator[] = ";"; 184 if (pairs_.empty())
110 const char ParsedCookie::kTokenSeparator[] = ";="; 185 pairs_.push_back(std::make_pair("", ""));
186 pairs_[0].first = name;
187 return true;
188 }
111 189
112 // Create a cookie-line for the cookie. For debugging only! 190 bool ParsedCookie::SetValue(const std::string& value) {
113 // If we want to use this for something more than debugging, we 191 if (!IsValidCookieValue(value))
114 // should rewrite it better... 192 return false;
115 std::string ParsedCookie::DebugString() const { 193 if (pairs_.empty())
194 pairs_.push_back(std::make_pair("", ""));
195 pairs_[0].second = value;
196 return true;
197 }
198
199 bool ParsedCookie::SetPath(const std::string& path) {
erikwright (departed) 2012/07/18 17:20:32 If you're concerned about these being ugly/repetit
200 if (path.empty()) {
201 ClearAttributePair(path_index_);
202 return true;
203 } else {
204 return SetAttributePair(&path_index_, kPathTokenName, path);
205 }
206 }
207
208 bool ParsedCookie::SetDomain(const std::string& domain) {
209 if (domain.empty()) {
210 ClearAttributePair(domain_index_);
211 return true;
212 } else {
213 return SetAttributePair(&domain_index_, kDomainTokenName, domain);
214 }
215 }
216
217 bool ParsedCookie::SetMACKey(const std::string& mac_key) {
218 if (mac_key.empty()) {
219 ClearAttributePair(mac_key_index_);
220 return true;
221 } else {
222 return SetAttributePair(&mac_key_index_, kMACKeyTokenName, mac_key);
223 }
224 }
225
226 bool ParsedCookie::SetMACAlgorithm(const std::string& mac_algorithm) {
227 if (mac_algorithm.empty()) {
228 ClearAttributePair(mac_algorithm_index_);
229 return true;
230 } else {
231 return SetAttributePair(&mac_algorithm_index_, kMACAlgorithmTokenName,
232 mac_algorithm);
233 }
234 }
235
236 bool ParsedCookie::SetExpires(const std::string& expires) {
237 if (expires.empty()) {
238 ClearAttributePair(expires_index_);
239 return true;
240 } else {
241 return SetAttributePair(&expires_index_, kExpiresTokenName, expires);
242 }
243 }
244
245 bool ParsedCookie::SetMaxAge(const std::string& maxage) {
246 if (maxage.empty()) {
247 ClearAttributePair(maxage_index_);
248 return true;
249 } else {
250 return SetAttributePair(&maxage_index_, kMaxAgeTokenName, maxage);
251 }
252 }
253
254 bool ParsedCookie::SetIsSecure(bool is_secure) {
255 if (is_secure) {
256 return SetAttributePair(&secure_index_, kSecureTokenName, "");
257 } else {
258 ClearAttributePair(secure_index_);
259 return true;
260 }
261 }
262
263 bool ParsedCookie::SetIsHttpOnly(bool is_http_only) {
264 if (is_http_only) {
265 return SetAttributePair(&httponly_index_, kHttpOnlyTokenName, "");
266 } else {
267 ClearAttributePair(httponly_index_);
268 return true;
269 }
270 }
271
272 std::string ParsedCookie::ToCookieLine() const {
116 std::string out; 273 std::string out;
117 for (PairList::const_iterator it = pairs_.begin(); 274 for (PairList::const_iterator it = pairs_.begin();
118 it != pairs_.end(); ++it) { 275 it != pairs_.end(); ++it) {
276 if (!out.empty())
277 out.append("; ");
119 out.append(it->first); 278 out.append(it->first);
120 out.append("="); 279 if (it->first != kSecureTokenName && it->first != kHttpOnlyTokenName) {
121 out.append(it->second); 280 out.append("=");
122 out.append("; "); 281 out.append(it->second);
282 }
123 } 283 }
124 return out; 284 return out;
125 } 285 }
126 286
127 std::string::const_iterator ParsedCookie::FindFirstTerminator( 287 std::string::const_iterator ParsedCookie::FindFirstTerminator(
128 const std::string& s) { 288 const std::string& s) {
129 std::string::const_iterator end = s.end(); 289 std::string::const_iterator end = s.end();
130 size_t term_pos = 290 size_t term_pos =
131 s.find_first_of(std::string(kTerminator, kTerminatorLen)); 291 s.find_first_of(std::string(kTerminator, kTerminatorLen));
132 if (term_pos != std::string::npos) { 292 if (term_pos != std::string::npos) {
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after
272 pairs_.push_back(pair); 432 pairs_.push_back(pair);
273 433
274 // We've processed a token/value pair, we're either at the end of 434 // 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. 435 // the string or a ValueSeparator like ';', which we want to skip.
276 if (it != end) 436 if (it != end)
277 ++it; 437 ++it;
278 } 438 }
279 } 439 }
280 440
281 void ParsedCookie::SetupAttributes() { 441 void ParsedCookie::SetupAttributes() {
282 static const char kPathTokenName[] = "path";
283 static const char kDomainTokenName[] = "domain";
284 static const char kMACKeyTokenName[] = "mac-key";
285 static const char kMACAlgorithmTokenName[] = "mac-algorithm";
286 static const char kExpiresTokenName[] = "expires";
287 static const char kMaxAgeTokenName[] = "max-age";
288 static const char kSecureTokenName[] = "secure";
289 static const char kHttpOnlyTokenName[] = "httponly";
290
291 // We skip over the first token/value, the user supplied one. 442 // We skip over the first token/value, the user supplied one.
292 for (size_t i = 1; i < pairs_.size(); ++i) { 443 for (size_t i = 1; i < pairs_.size(); ++i) {
293 if (pairs_[i].first == kPathTokenName) { 444 if (pairs_[i].first == kPathTokenName) {
294 path_index_ = i; 445 path_index_ = i;
295 } else if (pairs_[i].first == kDomainTokenName) { 446 } else if (pairs_[i].first == kDomainTokenName) {
296 domain_index_ = i; 447 domain_index_ = i;
297 } else if (pairs_[i].first == kMACKeyTokenName) { 448 } else if (pairs_[i].first == kMACKeyTokenName) {
298 mac_key_index_ = i; 449 mac_key_index_ = i;
299 } else if (pairs_[i].first == kMACAlgorithmTokenName) { 450 } else if (pairs_[i].first == kMACAlgorithmTokenName) {
300 mac_algorithm_index_ = i; 451 mac_algorithm_index_ = i;
301 } else if (pairs_[i].first == kExpiresTokenName) { 452 } else if (pairs_[i].first == kExpiresTokenName) {
302 expires_index_ = i; 453 expires_index_ = i;
303 } else if (pairs_[i].first == kMaxAgeTokenName) { 454 } else if (pairs_[i].first == kMaxAgeTokenName) {
304 maxage_index_ = i; 455 maxage_index_ = i;
305 } else if (pairs_[i].first == kSecureTokenName) { 456 } else if (pairs_[i].first == kSecureTokenName) {
306 secure_index_ = i; 457 secure_index_ = i;
307 } else if (pairs_[i].first == kHttpOnlyTokenName) { 458 } else if (pairs_[i].first == kHttpOnlyTokenName) {
308 httponly_index_ = i; 459 httponly_index_ = i;
309 } else { 460 } else {
310 /* some attribute we don't know or don't care about. */ 461 /* some attribute we don't know or don't care about. */
311 } 462 }
312 } 463 }
313 } 464 }
465 bool ParsedCookie::SetAttributePair(size_t* index,
466 const std::string& key,
467 const std::string& value) {
468 if (!IsValidToken(key) || !IsValidCookieAttributeValue(value))
469 return false;
470 if (!IsValid())
471 return false;
472 if (*index) {
473 pairs_[*index].second = value;
474 } else {
475 pairs_.push_back(std::make_pair(key, value));
476 *index = pairs_.size() - 1;
477 }
478 return true;
479 }
480
481 void ParsedCookie::ClearAttributePair(size_t index) {
482 // The first pair (name/value of cookie at pairs_[0]) cannot be cleared.
483 // Cookie attributes that don't have a value at the moment, are represented
484 // with an index being equal to 0.
485 if (index == 0)
486 return;
487
488 size_t* indexes[] = {&path_index_, &domain_index_, &mac_key_index_,
489 &mac_algorithm_index_, &expires_index_, &maxage_index_, &secure_index_,
490 &httponly_index_};
491 for (size_t i = 0; i < arraysize(indexes); ++i) {
492 if (*indexes[i] == index)
493 *indexes[i] = 0;
494 else if (*indexes[i] > index)
495 --*indexes[i];
496 }
497 pairs_.erase(pairs_.begin() + index);
498 }
314 499
315 } // namespace 500 } // namespace
OLDNEW
« no previous file with comments | « net/cookies/parsed_cookie.h ('k') | net/cookies/parsed_cookie_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698