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

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
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 } // namespace
68
50 namespace net { 69 namespace net {
51 70
52 ParsedCookie::ParsedCookie(const std::string& cookie_line) 71 ParsedCookie::ParsedCookie(const std::string& cookie_line)
53 : is_valid_(false), 72 : is_valid_(false),
54 path_index_(0), 73 path_index_(0),
55 domain_index_(0), 74 domain_index_(0),
56 mac_key_index_(0), 75 mac_key_index_(0),
57 mac_algorithm_index_(0), 76 mac_algorithm_index_(0),
58 expires_index_(0), 77 expires_index_(0),
59 maxage_index_(0), 78 maxage_index_(0),
(...skipping 10 matching lines...) Expand all
70 is_valid_ = true; 89 is_valid_ = true;
71 SetupAttributes(); 90 SetupAttributes();
72 } 91 }
73 } 92 }
74 93
75 ParsedCookie::~ParsedCookie() { 94 ParsedCookie::~ParsedCookie() {
76 } 95 }
77 96
78 // Returns true if |c| occurs in |chars| 97 // Returns true if |c| occurs in |chars|
79 // TODO(erikwright): maybe make this take an iterator, could check for end also? 98 // TODO(erikwright): maybe make this take an iterator, could check for end also?
80 static inline bool CharIsA(const char c, const char* chars) { 99 static inline bool CharIsA(const char c, const char* chars) {
erikwright (departed) 2012/07/12 15:50:57 I guess all of these should go into an anonymous n
battre 2012/07/12 18:03:10 Done.
81 return strchr(chars, c) != NULL; 100 return strchr(chars, c) != NULL;
82 } 101 }
83 // Seek the iterator to the first occurrence of a character in |chars|. 102 // Seek the iterator to the first occurrence of a character in |chars|.
84 // Returns true if it hit the end, false otherwise. 103 // Returns true if it hit the end, false otherwise.
85 static inline bool SeekTo(std::string::const_iterator* it, 104 static inline bool SeekTo(std::string::const_iterator* it,
86 const std::string::const_iterator& end, 105 const std::string::const_iterator& end,
87 const char* chars) { 106 const char* chars) {
88 for (; *it != end && !CharIsA(**it, chars); ++(*it)) {} 107 for (; *it != end && !CharIsA(**it, chars); ++(*it)) {}
89 return *it == end; 108 return *it == end;
90 } 109 }
91 // Seek the iterator to the first occurrence of a character not in |chars|. 110 // Seek the iterator to the first occurrence of a character not in |chars|.
92 // Returns true if it hit the end, false otherwise. 111 // Returns true if it hit the end, false otherwise.
93 static inline bool SeekPast(std::string::const_iterator* it, 112 static inline bool SeekPast(std::string::const_iterator* it,
94 const std::string::const_iterator& end, 113 const std::string::const_iterator& end,
95 const char* chars) { 114 const char* chars) {
96 for (; *it != end && CharIsA(**it, chars); ++(*it)) {} 115 for (; *it != end && CharIsA(**it, chars); ++(*it)) {}
97 return *it == end; 116 return *it == end;
98 } 117 }
99 static inline bool SeekBackPast(std::string::const_iterator* it, 118 static inline bool SeekBackPast(std::string::const_iterator* it,
100 const std::string::const_iterator& end, 119 const std::string::const_iterator& end,
101 const char* chars) { 120 const char* chars) {
102 for (; *it != end && CharIsA(**it, chars); --(*it)) {} 121 for (; *it != end && CharIsA(**it, chars); --(*it)) {}
103 return *it == end; 122 return *it == end;
104 } 123 }
105 124
106 const char ParsedCookie::kTerminator[] = "\n\r\0"; 125 // Validate whether |value| is a valid token according to [RFC2616],
107 const int ParsedCookie::kTerminatorLen = sizeof(kTerminator) - 1; 126 // Section 2.2.
108 const char ParsedCookie::kWhitespace[] = " \t"; 127 static bool IsValidToken(const std::string& value) {
109 const char ParsedCookie::kValueSeparator[] = ";"; 128 if (value.empty())
110 const char ParsedCookie::kTokenSeparator[] = ";="; 129 return false;
111 130
112 // Create a cookie-line for the cookie. For debugging only! 131 // Check that |value| has no separators.
113 // If we want to use this for something more than debugging, we 132 std::string separators = "()<>@,;:\\\"/[]?={} \t";
114 // should rewrite it better... 133 if (value.find_first_of(separators) != std::string::npos)
115 std::string ParsedCookie::DebugString() const { 134 return false;
135
136 // Check that |value| has no CTLs.
137 for (std::string::const_iterator i = value.begin(); i != value.end(); ++i) {
138 if ((*i >= 0 && *i <= 31) || *i >= 127)
139 return false;
140 }
141
142 return true;
143 }
144
145 bool ParsedCookie::SetName(const std::string& name) {
146 if (!IsValidToken(name))
147 return false;
148 if (pairs_.empty())
149 pairs_.push_back(std::make_pair("", ""));
150 pairs_[0].first = name;
151 is_valid_ = true;
152 return true;
153 }
154
155 // Validate value, which may be according to RFC 6265
156 // cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE )
157 // cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
158 // ; US-ASCII characters excluding CTLs,
159 // ; whitespace DQUOTE, comma, semicolon,
160 // ; and backslash
161 static bool IsValidCookieValue(const std::string& value) {
162 // Number of characters to skip in validation at beginning and end of string.
163 size_t skip = 0;
164 if (value.size() >= 2 && *value.begin() == '"' && *(value.end()-1) == '"')
165 skip = 1;
166 for (std::string::const_iterator i = value.begin() + skip;
167 i != value.end() - skip; ++i) {
168 bool valid_octet =
169 (*i == 0x21 ||
170 (*i >= 0x23 && *i <= 0x2B) ||
171 (*i >= 0x2D && *i <= 0x3A) ||
172 (*i >= 0x3C && *i <= 0x5B) ||
173 (*i >= 0x5D && *i <= 0x7E));
174 if (!valid_octet)
175 return false;
176 }
177 return true;
178 }
179
180 bool ParsedCookie::SetValue(const std::string& value) {
181 if (!IsValidCookieValue(value))
182 return false;
183 if (pairs_.empty())
184 pairs_.push_back(std::make_pair("", ""));
185 pairs_[0].second = value;
186 is_valid_ = true;
187 return true;
188 }
189
190 bool ParsedCookie::SetPath(const std::string& path) {
191 if (path.empty())
192 return ClearAttributePair(path_index_);
193 else
194 return SetAttributePair(&path_index_, kPathTokenName, path);
195 }
196
197 bool ParsedCookie::SetDomain(const std::string& domain) {
198 if (domain.empty())
199 return ClearAttributePair(domain_index_);
200 else
201 return SetAttributePair(&domain_index_, kDomainTokenName, domain);
202 }
203
204 bool ParsedCookie::SetMACKey(const std::string& mac_key) {
205 if (mac_key.empty())
206 return ClearAttributePair(mac_key_index_);
207 else
208 return SetAttributePair(&mac_key_index_, kMACKeyTokenName, mac_key);
209 }
210
211 bool ParsedCookie::SetMACAlgorithm(const std::string& mac_algorithm) {
212 if (mac_algorithm.empty()) {
213 return ClearAttributePair(mac_algorithm_index_);
214 } else {
215 return SetAttributePair(&mac_algorithm_index_, kMACAlgorithmTokenName,
216 mac_algorithm);
217 }
218 }
219
220 bool ParsedCookie::SetExpires(const std::string& expires) {
221 if (expires.empty())
222 return ClearAttributePair(expires_index_);
223 else
224 return SetAttributePair(&expires_index_, kExpiresTokenName, expires);
225 }
226
227 bool ParsedCookie::SetMaxAge(const std::string& maxage) {
228 if (maxage.empty())
229 return ClearAttributePair(maxage_index_);
230 else
231 return SetAttributePair(&maxage_index_, kMaxAgeTokenName, maxage);
232 }
233
234 bool ParsedCookie::SetIsSecure(bool is_secure) {
235 if (is_secure)
236 return SetAttributePair(&secure_index_, kSecureTokenName, "");
237 else
238 return ClearAttributePair(secure_index_);
239 }
240
241 bool ParsedCookie::SetIsHttpOnly(bool is_http_only) {
242 if (is_http_only)
243 return SetAttributePair(&httponly_index_, kHttpOnlyTokenName, "");
244 else
245 return ClearAttributePair(httponly_index_);
246 }
247
248 std::string ParsedCookie::ToCookieLine() const {
116 std::string out; 249 std::string out;
117 for (PairList::const_iterator it = pairs_.begin(); 250 for (PairList::const_iterator it = pairs_.begin();
118 it != pairs_.end(); ++it) { 251 it != pairs_.end(); ++it) {
252 if (!out.empty())
253 out.append("; ");
119 out.append(it->first); 254 out.append(it->first);
120 out.append("="); 255 if (it->first != kSecureTokenName && it->first != kHttpOnlyTokenName) {
121 out.append(it->second); 256 out.append("=");
122 out.append("; "); 257 out.append(it->second);
258 }
123 } 259 }
124 return out; 260 return out;
125 } 261 }
126 262
127 std::string::const_iterator ParsedCookie::FindFirstTerminator( 263 std::string::const_iterator ParsedCookie::FindFirstTerminator(
128 const std::string& s) { 264 const std::string& s) {
129 std::string::const_iterator end = s.end(); 265 std::string::const_iterator end = s.end();
130 size_t term_pos = 266 size_t term_pos =
131 s.find_first_of(std::string(kTerminator, kTerminatorLen)); 267 s.find_first_of(std::string(kTerminator, kTerminatorLen));
132 if (term_pos != std::string::npos) { 268 if (term_pos != std::string::npos) {
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after
272 pairs_.push_back(pair); 408 pairs_.push_back(pair);
273 409
274 // We've processed a token/value pair, we're either at the end of 410 // 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. 411 // the string or a ValueSeparator like ';', which we want to skip.
276 if (it != end) 412 if (it != end)
277 ++it; 413 ++it;
278 } 414 }
279 } 415 }
280 416
281 void ParsedCookie::SetupAttributes() { 417 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. 418 // We skip over the first token/value, the user supplied one.
292 for (size_t i = 1; i < pairs_.size(); ++i) { 419 for (size_t i = 1; i < pairs_.size(); ++i) {
293 if (pairs_[i].first == kPathTokenName) { 420 if (pairs_[i].first == kPathTokenName) {
294 path_index_ = i; 421 path_index_ = i;
295 } else if (pairs_[i].first == kDomainTokenName) { 422 } else if (pairs_[i].first == kDomainTokenName) {
296 domain_index_ = i; 423 domain_index_ = i;
297 } else if (pairs_[i].first == kMACKeyTokenName) { 424 } else if (pairs_[i].first == kMACKeyTokenName) {
298 mac_key_index_ = i; 425 mac_key_index_ = i;
299 } else if (pairs_[i].first == kMACAlgorithmTokenName) { 426 } else if (pairs_[i].first == kMACAlgorithmTokenName) {
300 mac_algorithm_index_ = i; 427 mac_algorithm_index_ = i;
301 } else if (pairs_[i].first == kExpiresTokenName) { 428 } else if (pairs_[i].first == kExpiresTokenName) {
302 expires_index_ = i; 429 expires_index_ = i;
303 } else if (pairs_[i].first == kMaxAgeTokenName) { 430 } else if (pairs_[i].first == kMaxAgeTokenName) {
304 maxage_index_ = i; 431 maxage_index_ = i;
305 } else if (pairs_[i].first == kSecureTokenName) { 432 } else if (pairs_[i].first == kSecureTokenName) {
306 secure_index_ = i; 433 secure_index_ = i;
307 } else if (pairs_[i].first == kHttpOnlyTokenName) { 434 } else if (pairs_[i].first == kHttpOnlyTokenName) {
308 httponly_index_ = i; 435 httponly_index_ = i;
309 } else { 436 } else {
310 /* some attribute we don't know or don't care about. */ 437 /* some attribute we don't know or don't care about. */
311 } 438 }
312 } 439 }
313 } 440 }
314 441
442 static bool IsValidCookieAttributeValue(const std::string& value) {
443 // The greatest common denominator of cookie attribute values is
444 // <any CHAR except CTLs or ";"> according to RFC 6265.
445 for (std::string::const_iterator i = value.begin(); i != value.end(); ++i) {
446 if ((*i >= 0 && *i <= 31) || *i == ';')
447 return false;
448 }
449 return true;
450 }
451
452 bool ParsedCookie::SetAttributePair(size_t* index,
453 const std::string& key,
454 const std::string& value) {
455 if (!IsValidToken(key) || !IsValidCookieAttributeValue(value))
456 return false;
457 if (*index) {
458 pairs_[*index].second = value;
459 } else {
460 // Reserve first spot for cooie name and value.
erikwright (departed) 2012/07/12 15:50:57 Seems to me it would be better to fail if there is
battre 2012/07/12 18:03:10 Done.
461 if (pairs_.empty())
462 pairs_.push_back(std::make_pair("", ""));
463 pairs_.push_back(std::make_pair(key, value));
464 *index = pairs_.size() - 1;
465 }
466 is_valid_ = true;
467 return true;
468 }
469
470 bool ParsedCookie::ClearAttributePair(size_t index) {
471 // The first pair (name/value of cookie at pairs_[0]) cannot be cleared.
472 // Cookie attributes that don't have a value at the moment, are represented
473 // with an index being equal to 0.
474 if (index == 0)
475 return true;
476
477 size_t* indexes[] = {&path_index_, &domain_index_, &mac_key_index_,
478 &mac_algorithm_index_, &expires_index_, &maxage_index_, &secure_index_,
479 &httponly_index_};
480 for (size_t i = 0; i < arraysize(indexes); ++i) {
481 if (*indexes[i] == index)
482 *indexes[i] = 0;
483 else if (*indexes[i] > index)
484 --*indexes[i];
485 }
486 pairs_.erase(pairs_.begin() + index);
487 is_valid_ = pairs_.empty();
erikwright (departed) 2012/07/12 15:50:57 It seems that is_valid_ can never be mutated by th
battre 2012/07/12 18:03:10 I have redefined IsValid() as you have suggested.
488 return true;
erikwright (departed) 2012/07/12 15:50:57 If you won't ever return false, change return type
battre 2012/07/12 18:03:10 Done, even thought this comes at the cost of makin
489 }
490
315 } // namespace 491 } // namespace
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698