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 29 matching lines...) Expand all Loading... | |
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |