diff options
Diffstat (limited to 'linden/indra/llplugin/llplugincookiestore.cpp')
-rw-r--r-- | linden/indra/llplugin/llplugincookiestore.cpp | 671 |
1 files changed, 671 insertions, 0 deletions
diff --git a/linden/indra/llplugin/llplugincookiestore.cpp b/linden/indra/llplugin/llplugincookiestore.cpp new file mode 100644 index 0000000..283ba35 --- /dev/null +++ b/linden/indra/llplugin/llplugincookiestore.cpp | |||
@@ -0,0 +1,671 @@ | |||
1 | /** | ||
2 | * @file llplugincookiestore.cpp | ||
3 | * @brief LLPluginCookieStore provides central storage for http cookies used by plugins | ||
4 | * | ||
5 | * @cond | ||
6 | * $LicenseInfo:firstyear=2010&license=viewergpl$ | ||
7 | * | ||
8 | * Copyright (c) 2010, Linden Research, Inc. | ||
9 | * | ||
10 | * Second Life Viewer Source Code | ||
11 | * The source code in this file ("Source Code") is provided by Linden Lab | ||
12 | * to you under the terms of the GNU General Public License, version 2.0 | ||
13 | * ("GPL"), unless you have obtained a separate licensing agreement | ||
14 | * ("Other License"), formally executed by you and Linden Lab. Terms of | ||
15 | * the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
16 | * online at http://secondlife.com/developers/opensource/gplv2 | ||
17 | * | ||
18 | * There are special exceptions to the terms and conditions of the GPL as | ||
19 | * it is applied to this Source Code. View the full text of the exception | ||
20 | * in the file doc/FLOSS-exception.txt in this software distribution, or | ||
21 | * online at | ||
22 | * http://secondlife.com/developers/opensource/flossexception | ||
23 | * | ||
24 | * By copying, modifying or distributing this software, you acknowledge | ||
25 | * that you have read and understood your obligations described above, | ||
26 | * and agree to abide by those obligations. | ||
27 | * | ||
28 | * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
29 | * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
30 | * COMPLETENESS OR PERFORMANCE. | ||
31 | * $/LicenseInfo$ | ||
32 | * | ||
33 | * @endcond | ||
34 | */ | ||
35 | |||
36 | #include "linden_common.h" | ||
37 | #include "indra_constants.h" | ||
38 | |||
39 | #include "llplugincookiestore.h" | ||
40 | #include <iostream> | ||
41 | |||
42 | // for curl_getdate() (apparently parsing RFC 1123 dates is hard) | ||
43 | #include <curl/curl.h> | ||
44 | |||
45 | LLPluginCookieStore::LLPluginCookieStore(): | ||
46 | mHasChangedCookies(false) | ||
47 | { | ||
48 | } | ||
49 | |||
50 | |||
51 | LLPluginCookieStore::~LLPluginCookieStore() | ||
52 | { | ||
53 | clearCookies(); | ||
54 | } | ||
55 | |||
56 | |||
57 | LLPluginCookieStore::Cookie::Cookie(const std::string &s, std::string::size_type cookie_start, std::string::size_type cookie_end): | ||
58 | mCookie(s, cookie_start, cookie_end - cookie_start), | ||
59 | mNameStart(0), mNameEnd(0), | ||
60 | mValueStart(0), mValueEnd(0), | ||
61 | mDomainStart(0), mDomainEnd(0), | ||
62 | mPathStart(0), mPathEnd(0), | ||
63 | mDead(false), mChanged(true) | ||
64 | { | ||
65 | } | ||
66 | |||
67 | LLPluginCookieStore::Cookie *LLPluginCookieStore::Cookie::createFromString(const std::string &s, std::string::size_type cookie_start, std::string::size_type cookie_end, const std::string &host) | ||
68 | { | ||
69 | Cookie *result = new Cookie(s, cookie_start, cookie_end); | ||
70 | |||
71 | if(!result->parse(host)) | ||
72 | { | ||
73 | delete result; | ||
74 | result = NULL; | ||
75 | } | ||
76 | |||
77 | return result; | ||
78 | } | ||
79 | |||
80 | std::string LLPluginCookieStore::Cookie::getKey() const | ||
81 | { | ||
82 | std::string result; | ||
83 | if(mDomainEnd > mDomainStart) | ||
84 | { | ||
85 | result += mCookie.substr(mDomainStart, mDomainEnd - mDomainStart); | ||
86 | } | ||
87 | result += ';'; | ||
88 | if(mPathEnd > mPathStart) | ||
89 | { | ||
90 | result += mCookie.substr(mPathStart, mPathEnd - mPathStart); | ||
91 | } | ||
92 | result += ';'; | ||
93 | result += mCookie.substr(mNameStart, mNameEnd - mNameStart); | ||
94 | return result; | ||
95 | } | ||
96 | |||
97 | bool LLPluginCookieStore::Cookie::parse(const std::string &host) | ||
98 | { | ||
99 | bool first_field = true; | ||
100 | |||
101 | std::string::size_type cookie_end = mCookie.size(); | ||
102 | std::string::size_type field_start = 0; | ||
103 | |||
104 | LL_DEBUGS("CookieStoreParse") << "parsing cookie: " << mCookie << LL_ENDL; | ||
105 | while(field_start < cookie_end) | ||
106 | { | ||
107 | // Finding the start of the next field requires honoring special quoting rules | ||
108 | // see the definition of 'quoted-string' in rfc2616 for details | ||
109 | std::string::size_type next_field_start = findFieldEnd(field_start); | ||
110 | |||
111 | // The end of this field should not include the terminating ';' or any trailing whitespace | ||
112 | std::string::size_type field_end = mCookie.find_last_not_of("; ", next_field_start); | ||
113 | if(field_end == std::string::npos || field_end < field_start) | ||
114 | { | ||
115 | // This field was empty or all whitespace. Set end = start so it shows as empty. | ||
116 | field_end = field_start; | ||
117 | } | ||
118 | else if (field_end < next_field_start) | ||
119 | { | ||
120 | // we actually want the index of the char _after_ what 'last not of' found | ||
121 | ++field_end; | ||
122 | } | ||
123 | |||
124 | // find the start of the actual name (skip separator and possible whitespace) | ||
125 | std::string::size_type name_start = mCookie.find_first_not_of("; ", field_start); | ||
126 | if(name_start == std::string::npos || name_start > next_field_start) | ||
127 | { | ||
128 | // Again, nothing but whitespace. | ||
129 | name_start = field_start; | ||
130 | } | ||
131 | |||
132 | // the name and value are separated by the first equals sign | ||
133 | std::string::size_type name_value_sep = mCookie.find_first_of("=", name_start); | ||
134 | if(name_value_sep == std::string::npos || name_value_sep > field_end) | ||
135 | { | ||
136 | // No separator found, so this is a field without an = | ||
137 | name_value_sep = field_end; | ||
138 | } | ||
139 | |||
140 | // the name end is before the name-value separator | ||
141 | std::string::size_type name_end = mCookie.find_last_not_of("= ", name_value_sep); | ||
142 | if(name_end == std::string::npos || name_end < name_start) | ||
143 | { | ||
144 | // I'm not sure how we'd hit this case... it seems like it would have to be an empty name. | ||
145 | name_end = name_start; | ||
146 | } | ||
147 | else if (name_end < name_value_sep) | ||
148 | { | ||
149 | // we actually want the index of the char _after_ what 'last not of' found | ||
150 | ++name_end; | ||
151 | } | ||
152 | |||
153 | // Value is between the name-value sep and the end of the field. | ||
154 | std::string::size_type value_start = mCookie.find_first_not_of("= ", name_value_sep); | ||
155 | if(value_start == std::string::npos || value_start > field_end) | ||
156 | { | ||
157 | // All whitespace or empty value | ||
158 | value_start = field_end; | ||
159 | } | ||
160 | std::string::size_type value_end = mCookie.find_last_not_of("; ", field_end); | ||
161 | if(value_end == std::string::npos || value_end < value_start) | ||
162 | { | ||
163 | // All whitespace or empty value | ||
164 | value_end = value_start; | ||
165 | } | ||
166 | else if (value_end < field_end) | ||
167 | { | ||
168 | // we actually want the index of the char _after_ what 'last not of' found | ||
169 | ++value_end; | ||
170 | } | ||
171 | |||
172 | LL_DEBUGS("CookieStoreParse") | ||
173 | << " field name: \"" << mCookie.substr(name_start, name_end - name_start) | ||
174 | << "\", value: \"" << mCookie.substr(value_start, value_end - value_start) << "\"" | ||
175 | << LL_ENDL; | ||
176 | |||
177 | // See whether this field is one we know | ||
178 | if(first_field) | ||
179 | { | ||
180 | // The first field is the name=value pair | ||
181 | mNameStart = name_start; | ||
182 | mNameEnd = name_end; | ||
183 | mValueStart = value_start; | ||
184 | mValueEnd = value_end; | ||
185 | first_field = false; | ||
186 | } | ||
187 | else | ||
188 | { | ||
189 | // Subsequent fields must come from the set in rfc2109 | ||
190 | if(matchName(name_start, name_end, "expires")) | ||
191 | { | ||
192 | std::string date_string(mCookie, value_start, value_end - value_start); | ||
193 | // If the cookie contains an "expires" field, it MUST contain a parsable date. | ||
194 | |||
195 | // HACK: LLDate apparently can't PARSE an rfc1123-format date, even though it can GENERATE one. | ||
196 | // The curl function curl_getdate can do this, but I'm hesitant to unilaterally introduce a curl dependency in LLDate. | ||
197 | #if 1 | ||
198 | time_t date = curl_getdate(date_string.c_str(), NULL ); | ||
199 | mDate.secondsSinceEpoch((F64)date); | ||
200 | LL_DEBUGS("CookieStoreParse") << " expire date parsed to: " << mDate.asRFC1123() << LL_ENDL; | ||
201 | #else | ||
202 | // This doesn't work (rfc1123-format dates cause it to fail) | ||
203 | if(!mDate.fromString(date_string)) | ||
204 | { | ||
205 | // Date failed to parse. | ||
206 | LL_WARNS("CookieStoreParse") << "failed to parse cookie's expire date: " << date << LL_ENDL; | ||
207 | return false; | ||
208 | } | ||
209 | #endif | ||
210 | } | ||
211 | else if(matchName(name_start, name_end, "domain")) | ||
212 | { | ||
213 | mDomainStart = value_start; | ||
214 | mDomainEnd = value_end; | ||
215 | } | ||
216 | else if(matchName(name_start, name_end, "path")) | ||
217 | { | ||
218 | mPathStart = value_start; | ||
219 | mPathEnd = value_end; | ||
220 | } | ||
221 | else if(matchName(name_start, name_end, "max-age")) | ||
222 | { | ||
223 | // TODO: how should we handle this? | ||
224 | } | ||
225 | else if(matchName(name_start, name_end, "secure")) | ||
226 | { | ||
227 | // We don't care about the value of this field (yet) | ||
228 | } | ||
229 | else if(matchName(name_start, name_end, "version")) | ||
230 | { | ||
231 | // We don't care about the value of this field (yet) | ||
232 | } | ||
233 | else if(matchName(name_start, name_end, "comment")) | ||
234 | { | ||
235 | // We don't care about the value of this field (yet) | ||
236 | } | ||
237 | else if(matchName(name_start, name_end, "httponly")) | ||
238 | { | ||
239 | // We don't care about the value of this field (yet) | ||
240 | } | ||
241 | else | ||
242 | { | ||
243 | // An unknown field is a parse failure | ||
244 | LL_WARNS("CookieStoreParse") << "unexpected field name: " << mCookie.substr(name_start, name_end - name_start) << LL_ENDL; | ||
245 | return false; | ||
246 | } | ||
247 | |||
248 | } | ||
249 | |||
250 | |||
251 | // move on to the next field, skipping this field's separator and any leading whitespace | ||
252 | field_start = mCookie.find_first_not_of("; ", next_field_start); | ||
253 | } | ||
254 | |||
255 | // The cookie MUST have a name | ||
256 | if(mNameEnd <= mNameStart) | ||
257 | return false; | ||
258 | |||
259 | // If the cookie doesn't have a domain, add the current host as the domain. | ||
260 | if(mDomainEnd <= mDomainStart) | ||
261 | { | ||
262 | if(host.empty()) | ||
263 | { | ||
264 | // no domain and no current host -- this is a parse failure. | ||
265 | return false; | ||
266 | } | ||
267 | |||
268 | // Figure out whether this cookie ended with a ";" or not... | ||
269 | std::string::size_type last_char = mCookie.find_last_not_of(" "); | ||
270 | if((last_char != std::string::npos) && (mCookie[last_char] != ';')) | ||
271 | { | ||
272 | mCookie += ";"; | ||
273 | } | ||
274 | |||
275 | mCookie += " domain="; | ||
276 | mDomainStart = mCookie.size(); | ||
277 | mCookie += host; | ||
278 | mDomainEnd = mCookie.size(); | ||
279 | |||
280 | LL_DEBUGS("CookieStoreParse") << "added domain (" << mDomainStart << " to " << mDomainEnd << "), new cookie is: " << mCookie << LL_ENDL; | ||
281 | } | ||
282 | |||
283 | // If the cookie doesn't have a path, add "/". | ||
284 | if(mPathEnd <= mPathStart) | ||
285 | { | ||
286 | // Figure out whether this cookie ended with a ";" or not... | ||
287 | std::string::size_type last_char = mCookie.find_last_not_of(" "); | ||
288 | if((last_char != std::string::npos) && (mCookie[last_char] != ';')) | ||
289 | { | ||
290 | mCookie += ";"; | ||
291 | } | ||
292 | |||
293 | mCookie += " path="; | ||
294 | mPathStart = mCookie.size(); | ||
295 | mCookie += "/"; | ||
296 | mPathEnd = mCookie.size(); | ||
297 | |||
298 | LL_DEBUGS("CookieStoreParse") << "added path (" << mPathStart << " to " << mPathEnd << "), new cookie is: " << mCookie << LL_ENDL; | ||
299 | } | ||
300 | |||
301 | |||
302 | return true; | ||
303 | } | ||
304 | |||
305 | std::string::size_type LLPluginCookieStore::Cookie::findFieldEnd(std::string::size_type start, std::string::size_type end) | ||
306 | { | ||
307 | std::string::size_type result = start; | ||
308 | |||
309 | if(end == std::string::npos) | ||
310 | end = mCookie.size(); | ||
311 | |||
312 | bool in_quotes = false; | ||
313 | for(; (result < end); result++) | ||
314 | { | ||
315 | switch(mCookie[result]) | ||
316 | { | ||
317 | case '\\': | ||
318 | if(in_quotes) | ||
319 | result++; // The next character is backslash-quoted. Skip over it. | ||
320 | break; | ||
321 | case '"': | ||
322 | in_quotes = !in_quotes; | ||
323 | break; | ||
324 | case ';': | ||
325 | if(!in_quotes) | ||
326 | return result; | ||
327 | break; | ||
328 | } | ||
329 | } | ||
330 | |||
331 | // If we got here, no ';' was found. | ||
332 | return end; | ||
333 | } | ||
334 | |||
335 | bool LLPluginCookieStore::Cookie::matchName(std::string::size_type start, std::string::size_type end, const char *name) | ||
336 | { | ||
337 | // NOTE: this assumes 'name' is already in lowercase. The code which uses it should be able to arrange this... | ||
338 | |||
339 | while((start < end) && (*name != '\0')) | ||
340 | { | ||
341 | if(tolower(mCookie[start]) != *name) | ||
342 | return false; | ||
343 | |||
344 | start++; | ||
345 | name++; | ||
346 | } | ||
347 | |||
348 | // iff both strings hit the end at the same time, they're equal. | ||
349 | return ((start == end) && (*name == '\0')); | ||
350 | } | ||
351 | |||
352 | std::string LLPluginCookieStore::getAllCookies() | ||
353 | { | ||
354 | std::stringstream result; | ||
355 | writeAllCookies(result); | ||
356 | return result.str(); | ||
357 | } | ||
358 | |||
359 | void LLPluginCookieStore::writeAllCookies(std::ostream& s) | ||
360 | { | ||
361 | cookie_map_t::iterator iter; | ||
362 | for(iter = mCookies.begin(); iter != mCookies.end(); iter++) | ||
363 | { | ||
364 | // Don't return expired cookies | ||
365 | if(!iter->second->isDead()) | ||
366 | { | ||
367 | s << (iter->second->getCookie()) << "\n"; | ||
368 | } | ||
369 | } | ||
370 | |||
371 | } | ||
372 | |||
373 | std::string LLPluginCookieStore::getPersistentCookies() | ||
374 | { | ||
375 | std::stringstream result; | ||
376 | writePersistentCookies(result); | ||
377 | return result.str(); | ||
378 | } | ||
379 | |||
380 | void LLPluginCookieStore::writePersistentCookies(std::ostream& s) | ||
381 | { | ||
382 | cookie_map_t::iterator iter; | ||
383 | for(iter = mCookies.begin(); iter != mCookies.end(); iter++) | ||
384 | { | ||
385 | // Don't return expired cookies or session cookies | ||
386 | if(!iter->second->isDead() && !iter->second->isSessionCookie()) | ||
387 | { | ||
388 | s << iter->second->getCookie() << "\n"; | ||
389 | } | ||
390 | } | ||
391 | } | ||
392 | |||
393 | std::string LLPluginCookieStore::getChangedCookies(bool clear_changed) | ||
394 | { | ||
395 | std::stringstream result; | ||
396 | writeChangedCookies(result, clear_changed); | ||
397 | |||
398 | return result.str(); | ||
399 | } | ||
400 | |||
401 | void LLPluginCookieStore::writeChangedCookies(std::ostream& s, bool clear_changed) | ||
402 | { | ||
403 | if(mHasChangedCookies) | ||
404 | { | ||
405 | lldebugs << "returning changed cookies: " << llendl; | ||
406 | cookie_map_t::iterator iter; | ||
407 | for(iter = mCookies.begin(); iter != mCookies.end(); ) | ||
408 | { | ||
409 | cookie_map_t::iterator next = iter; | ||
410 | next++; | ||
411 | |||
412 | // Only return cookies marked as "changed" | ||
413 | if(iter->second->isChanged()) | ||
414 | { | ||
415 | s << iter->second->getCookie() << "\n"; | ||
416 | |||
417 | lldebugs << " " << iter->second->getCookie() << llendl; | ||
418 | |||
419 | // If requested, clear the changed mark | ||
420 | if(clear_changed) | ||
421 | { | ||
422 | if(iter->second->isDead()) | ||
423 | { | ||
424 | // If this cookie was previously marked dead, it needs to be removed entirely. | ||
425 | delete iter->second; | ||
426 | mCookies.erase(iter); | ||
427 | } | ||
428 | else | ||
429 | { | ||
430 | // Not dead, just mark as not changed. | ||
431 | iter->second->setChanged(false); | ||
432 | } | ||
433 | } | ||
434 | } | ||
435 | |||
436 | iter = next; | ||
437 | } | ||
438 | } | ||
439 | |||
440 | if(clear_changed) | ||
441 | mHasChangedCookies = false; | ||
442 | } | ||
443 | |||
444 | void LLPluginCookieStore::setAllCookies(const std::string &cookies, bool mark_changed) | ||
445 | { | ||
446 | clearCookies(); | ||
447 | setCookies(cookies, mark_changed); | ||
448 | } | ||
449 | |||
450 | void LLPluginCookieStore::readAllCookies(std::istream& s, bool mark_changed) | ||
451 | { | ||
452 | clearCookies(); | ||
453 | readCookies(s, mark_changed); | ||
454 | } | ||
455 | |||
456 | void LLPluginCookieStore::setCookies(const std::string &cookies, bool mark_changed) | ||
457 | { | ||
458 | std::string::size_type start = 0; | ||
459 | |||
460 | while(start != std::string::npos) | ||
461 | { | ||
462 | std::string::size_type end = cookies.find_first_of("\r\n", start); | ||
463 | if(end > start) | ||
464 | { | ||
465 | // The line is non-empty. Try to create a cookie from it. | ||
466 | setOneCookie(cookies, start, end, mark_changed); | ||
467 | } | ||
468 | start = cookies.find_first_not_of("\r\n ", end); | ||
469 | } | ||
470 | } | ||
471 | |||
472 | void LLPluginCookieStore::setCookiesFromHost(const std::string &cookies, const std::string &host, bool mark_changed) | ||
473 | { | ||
474 | std::string::size_type start = 0; | ||
475 | |||
476 | while(start != std::string::npos) | ||
477 | { | ||
478 | std::string::size_type end = cookies.find_first_of("\r\n", start); | ||
479 | if(end > start) | ||
480 | { | ||
481 | // The line is non-empty. Try to create a cookie from it. | ||
482 | setOneCookie(cookies, start, end, mark_changed, host); | ||
483 | } | ||
484 | start = cookies.find_first_not_of("\r\n ", end); | ||
485 | } | ||
486 | } | ||
487 | |||
488 | void LLPluginCookieStore::readCookies(std::istream& s, bool mark_changed) | ||
489 | { | ||
490 | std::string line; | ||
491 | while(s.good() && !s.eof()) | ||
492 | { | ||
493 | std::getline(s, line); | ||
494 | if(!line.empty()) | ||
495 | { | ||
496 | // Try to create a cookie from this line. | ||
497 | setOneCookie(line, 0, std::string::npos, mark_changed); | ||
498 | } | ||
499 | } | ||
500 | } | ||
501 | |||
502 | std::string LLPluginCookieStore::quoteString(const std::string &s) | ||
503 | { | ||
504 | std::stringstream result; | ||
505 | |||
506 | result << '"'; | ||
507 | |||
508 | for(std::string::size_type i = 0; i < s.size(); ++i) | ||
509 | { | ||
510 | char c = s[i]; | ||
511 | switch(c) | ||
512 | { | ||
513 | // All these separators need to be quoted in HTTP headers, according to section 2.2 of rfc 2616: | ||
514 | case '(': case ')': case '<': case '>': case '@': | ||
515 | case ',': case ';': case ':': case '\\': case '"': | ||
516 | case '/': case '[': case ']': case '?': case '=': | ||
517 | case '{': case '}': case ' ': case '\t': | ||
518 | result << '\\'; | ||
519 | break; | ||
520 | } | ||
521 | |||
522 | result << c; | ||
523 | } | ||
524 | |||
525 | result << '"'; | ||
526 | |||
527 | return result.str(); | ||
528 | } | ||
529 | |||
530 | std::string LLPluginCookieStore::unquoteString(const std::string &s) | ||
531 | { | ||
532 | std::stringstream result; | ||
533 | |||
534 | bool in_quotes = false; | ||
535 | |||
536 | for(std::string::size_type i = 0; i < s.size(); ++i) | ||
537 | { | ||
538 | char c = s[i]; | ||
539 | switch(c) | ||
540 | { | ||
541 | case '\\': | ||
542 | if(in_quotes) | ||
543 | { | ||
544 | // The next character is backslash-quoted. Pass it through untouched. | ||
545 | ++i; | ||
546 | if(i < s.size()) | ||
547 | { | ||
548 | result << s[i]; | ||
549 | } | ||
550 | continue; | ||
551 | } | ||
552 | break; | ||
553 | case '"': | ||
554 | in_quotes = !in_quotes; | ||
555 | continue; | ||
556 | break; | ||
557 | } | ||
558 | |||
559 | result << c; | ||
560 | } | ||
561 | |||
562 | return result.str(); | ||
563 | } | ||
564 | |||
565 | // The flow for deleting a cookie is non-obvious enough that I should call it out here... | ||
566 | // Deleting a cookie is done by setting a cookie with the same name, path, and domain, but with an expire timestamp in the past. | ||
567 | // (This is exactly how a web server tells a browser to delete a cookie.) | ||
568 | // When deleting with mark_changed set to true, this replaces the existing cookie in the list with an entry that's marked both dead and changed. | ||
569 | // Some time later when writeChangedCookies() is called with clear_changed set to true, the dead cookie is deleted from the list after being returned, so that the | ||
570 | // delete operation (in the form of the expired cookie) is passed along. | ||
571 | void LLPluginCookieStore::setOneCookie(const std::string &s, std::string::size_type cookie_start, std::string::size_type cookie_end, bool mark_changed, const std::string &host) | ||
572 | { | ||
573 | Cookie *cookie = Cookie::createFromString(s, cookie_start, cookie_end, host); | ||
574 | if(cookie) | ||
575 | { | ||
576 | LL_DEBUGS("CookieStoreUpdate") << "setting cookie: " << cookie->getCookie() << LL_ENDL; | ||
577 | |||
578 | // Create a key for this cookie | ||
579 | std::string key = cookie->getKey(); | ||
580 | |||
581 | // Check to see whether this cookie should have expired | ||
582 | if(!cookie->isSessionCookie() && (cookie->getDate() < LLDate::now())) | ||
583 | { | ||
584 | // This cookie has expired. | ||
585 | if(mark_changed) | ||
586 | { | ||
587 | // If we're marking cookies as changed, we should keep it anyway since we'll need to send it out with deltas. | ||
588 | cookie->setDead(true); | ||
589 | LL_DEBUGS("CookieStoreUpdate") << " marking dead" << LL_ENDL; | ||
590 | } | ||
591 | else | ||
592 | { | ||
593 | // If we're not marking cookies as changed, we don't need to keep this cookie at all. | ||
594 | // If the cookie was already in the list, delete it. | ||
595 | removeCookie(key); | ||
596 | |||
597 | delete cookie; | ||
598 | cookie = NULL; | ||
599 | |||
600 | LL_DEBUGS("CookieStoreUpdate") << " removing" << LL_ENDL; | ||
601 | } | ||
602 | } | ||
603 | |||
604 | if(cookie) | ||
605 | { | ||
606 | // If it already exists in the map, replace it. | ||
607 | cookie_map_t::iterator iter = mCookies.find(key); | ||
608 | if(iter != mCookies.end()) | ||
609 | { | ||
610 | if(iter->second->getCookie() == cookie->getCookie()) | ||
611 | { | ||
612 | // The new cookie is identical to the old -- don't mark as changed. | ||
613 | // Just leave the old one in the map. | ||
614 | delete cookie; | ||
615 | cookie = NULL; | ||
616 | |||
617 | LL_DEBUGS("CookieStoreUpdate") << " unchanged" << LL_ENDL; | ||
618 | } | ||
619 | else | ||
620 | { | ||
621 | // A matching cookie was already in the map. Replace it. | ||
622 | delete iter->second; | ||
623 | iter->second = cookie; | ||
624 | |||
625 | cookie->setChanged(mark_changed); | ||
626 | if(mark_changed) | ||
627 | mHasChangedCookies = true; | ||
628 | |||
629 | LL_DEBUGS("CookieStoreUpdate") << " replacing" << LL_ENDL; | ||
630 | } | ||
631 | } | ||
632 | else | ||
633 | { | ||
634 | // The cookie wasn't in the map. Insert it. | ||
635 | mCookies.insert(std::make_pair(key, cookie)); | ||
636 | |||
637 | cookie->setChanged(mark_changed); | ||
638 | if(mark_changed) | ||
639 | mHasChangedCookies = true; | ||
640 | |||
641 | LL_DEBUGS("CookieStoreUpdate") << " adding" << LL_ENDL; | ||
642 | } | ||
643 | } | ||
644 | } | ||
645 | else | ||
646 | { | ||
647 | LL_WARNS("CookieStoreUpdate") << "failed to parse cookie: " << s.substr(cookie_start, cookie_end - cookie_start) << LL_ENDL; | ||
648 | } | ||
649 | |||
650 | } | ||
651 | |||
652 | void LLPluginCookieStore::clearCookies() | ||
653 | { | ||
654 | while(!mCookies.empty()) | ||
655 | { | ||
656 | cookie_map_t::iterator iter = mCookies.begin(); | ||
657 | delete iter->second; | ||
658 | mCookies.erase(iter); | ||
659 | } | ||
660 | } | ||
661 | |||
662 | void LLPluginCookieStore::removeCookie(const std::string &key) | ||
663 | { | ||
664 | cookie_map_t::iterator iter = mCookies.find(key); | ||
665 | if(iter != mCookies.end()) | ||
666 | { | ||
667 | delete iter->second; | ||
668 | mCookies.erase(iter); | ||
669 | } | ||
670 | } | ||
671 | |||