summaryrefslogtreecommitdiff
path: root/indra/llplugin/llplugincookiestore.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llplugin/llplugincookiestore.cpp')
-rw-r--r--indra/llplugin/llplugincookiestore.cpp689
1 files changed, 0 insertions, 689 deletions
diff --git a/indra/llplugin/llplugincookiestore.cpp b/indra/llplugin/llplugincookiestore.cpp
deleted file mode 100644
index a5d717389d..0000000000
--- a/indra/llplugin/llplugincookiestore.cpp
+++ /dev/null
@@ -1,689 +0,0 @@
-/**
- * @file llplugincookiestore.cpp
- * @brief LLPluginCookieStore provides central storage for http cookies used by plugins
- *
- * @cond
- * $LicenseInfo:firstyear=2010&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- * @endcond
- */
-
-#include "linden_common.h"
-#include "llstl.h"
-#include "indra_constants.h"
-
-#include "llplugincookiestore.h"
-#include <iostream>
-
-// for curl_getdate() (apparently parsing RFC 1123 dates is hard)
-#include <curl/curl.h>
-
-LLPluginCookieStore::LLPluginCookieStore():
- mHasChangedCookies(false)
-{
-}
-
-
-LLPluginCookieStore::~LLPluginCookieStore()
-{
- clearCookies();
-}
-
-
-LLPluginCookieStore::Cookie::Cookie(const std::string &s, std::string::size_type cookie_start, std::string::size_type cookie_end):
- mCookie(s, cookie_start, cookie_end - cookie_start),
- mNameStart(0), mNameEnd(0),
- mValueStart(0), mValueEnd(0),
- mDomainStart(0), mDomainEnd(0),
- mPathStart(0), mPathEnd(0),
- mDead(false), mChanged(true)
-{
-}
-
-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)
-{
- Cookie *result = new Cookie(s, cookie_start, cookie_end);
-
- if(!result->parse(host))
- {
- delete result;
- result = NULL;
- }
-
- return result;
-}
-
-std::string LLPluginCookieStore::Cookie::getKey() const
-{
- std::string result;
- if(mDomainEnd > mDomainStart)
- {
- result += mCookie.substr(mDomainStart, mDomainEnd - mDomainStart);
- }
- result += ';';
- if(mPathEnd > mPathStart)
- {
- result += mCookie.substr(mPathStart, mPathEnd - mPathStart);
- }
- result += ';';
- result += mCookie.substr(mNameStart, mNameEnd - mNameStart);
- return result;
-}
-
-std::string LLPluginCookieStore::Cookie::getDomain() const
-{
- std::string result;
- if(mDomainEnd > mDomainStart)
- {
- result += mCookie.substr(mDomainStart, mDomainEnd - mDomainStart);
- }
- return result;
-}
-
-bool LLPluginCookieStore::Cookie::parse(const std::string &host)
-{
- bool first_field = true;
-
- std::string::size_type cookie_end = mCookie.size();
- std::string::size_type field_start = 0;
-
- LL_DEBUGS("CookieStoreParse") << "parsing cookie: " << mCookie << LL_ENDL;
- while(field_start < cookie_end)
- {
- // Finding the start of the next field requires honoring special quoting rules
- // see the definition of 'quoted-string' in rfc2616 for details
- std::string::size_type next_field_start = findFieldEnd(field_start);
-
- // The end of this field should not include the terminating ';' or any trailing whitespace
- std::string::size_type field_end = mCookie.find_last_not_of("; ", next_field_start);
- if(field_end == std::string::npos || field_end < field_start)
- {
- // This field was empty or all whitespace. Set end = start so it shows as empty.
- field_end = field_start;
- }
- else if (field_end < next_field_start)
- {
- // we actually want the index of the char _after_ what 'last not of' found
- ++field_end;
- }
-
- // find the start of the actual name (skip separator and possible whitespace)
- std::string::size_type name_start = mCookie.find_first_not_of("; ", field_start);
- if(name_start == std::string::npos || name_start > next_field_start)
- {
- // Again, nothing but whitespace.
- name_start = field_start;
- }
-
- // the name and value are separated by the first equals sign
- std::string::size_type name_value_sep = mCookie.find_first_of("=", name_start);
- if(name_value_sep == std::string::npos || name_value_sep > field_end)
- {
- // No separator found, so this is a field without an =
- name_value_sep = field_end;
- }
-
- // the name end is before the name-value separator
- std::string::size_type name_end = mCookie.find_last_not_of("= ", name_value_sep);
- if(name_end == std::string::npos || name_end < name_start)
- {
- // I'm not sure how we'd hit this case... it seems like it would have to be an empty name.
- name_end = name_start;
- }
- else if (name_end < name_value_sep)
- {
- // we actually want the index of the char _after_ what 'last not of' found
- ++name_end;
- }
-
- // Value is between the name-value sep and the end of the field.
- std::string::size_type value_start = mCookie.find_first_not_of("= ", name_value_sep);
- if(value_start == std::string::npos || value_start > field_end)
- {
- // All whitespace or empty value
- value_start = field_end;
- }
- std::string::size_type value_end = mCookie.find_last_not_of("; ", field_end);
- if(value_end == std::string::npos || value_end < value_start)
- {
- // All whitespace or empty value
- value_end = value_start;
- }
- else if (value_end < field_end)
- {
- // we actually want the index of the char _after_ what 'last not of' found
- ++value_end;
- }
-
- LL_DEBUGS("CookieStoreParse")
- << " field name: \"" << mCookie.substr(name_start, name_end - name_start)
- << "\", value: \"" << mCookie.substr(value_start, value_end - value_start) << "\""
- << LL_ENDL;
-
- // See whether this field is one we know
- if(first_field)
- {
- // The first field is the name=value pair
- mNameStart = name_start;
- mNameEnd = name_end;
- mValueStart = value_start;
- mValueEnd = value_end;
- first_field = false;
- }
- else
- {
- // Subsequent fields must come from the set in rfc2109
- if(matchName(name_start, name_end, "expires"))
- {
- std::string date_string(mCookie, value_start, value_end - value_start);
- // If the cookie contains an "expires" field, it MUST contain a parsable date.
-
- // HACK: LLDate apparently can't PARSE an rfc1123-format date, even though it can GENERATE one.
- // The curl function curl_getdate can do this, but I'm hesitant to unilaterally introduce a curl dependency in LLDate.
-#if 1
- time_t date = curl_getdate(date_string.c_str(), NULL );
- mDate.secondsSinceEpoch((F64)date);
- LL_DEBUGS("CookieStoreParse") << " expire date parsed to: " << mDate.asRFC1123() << LL_ENDL;
-#else
- // This doesn't work (rfc1123-format dates cause it to fail)
- if(!mDate.fromString(date_string))
- {
- // Date failed to parse.
- LL_WARNS("CookieStoreParse") << "failed to parse cookie's expire date: " << date << LL_ENDL;
- return false;
- }
-#endif
- }
- else if(matchName(name_start, name_end, "domain"))
- {
- mDomainStart = value_start;
- mDomainEnd = value_end;
- }
- else if(matchName(name_start, name_end, "path"))
- {
- mPathStart = value_start;
- mPathEnd = value_end;
- }
- else if(matchName(name_start, name_end, "max-age"))
- {
- // TODO: how should we handle this?
- }
- else if(matchName(name_start, name_end, "secure"))
- {
- // We don't care about the value of this field (yet)
- }
- else if(matchName(name_start, name_end, "version"))
- {
- // We don't care about the value of this field (yet)
- }
- else if(matchName(name_start, name_end, "comment"))
- {
- // We don't care about the value of this field (yet)
- }
- else if(matchName(name_start, name_end, "httponly"))
- {
- // We don't care about the value of this field (yet)
- }
- else
- {
- // An unknown field is a parse failure
- LL_WARNS("CookieStoreParse") << "unexpected field name: " << mCookie.substr(name_start, name_end - name_start) << LL_ENDL;
- return false;
- }
-
- }
-
-
- // move on to the next field, skipping this field's separator and any leading whitespace
- field_start = mCookie.find_first_not_of("; ", next_field_start);
- }
-
- // The cookie MUST have a name
- if(mNameEnd <= mNameStart)
- return false;
-
- // If the cookie doesn't have a domain, add the current host as the domain.
- if(mDomainEnd <= mDomainStart)
- {
- if(host.empty())
- {
- // no domain and no current host -- this is a parse failure.
- return false;
- }
-
- // Figure out whether this cookie ended with a ";" or not...
- std::string::size_type last_char = mCookie.find_last_not_of(" ");
- if((last_char != std::string::npos) && (mCookie[last_char] != ';'))
- {
- mCookie += ";";
- }
-
- mCookie += " domain=";
- mDomainStart = mCookie.size();
- mCookie += host;
- mDomainEnd = mCookie.size();
-
- LL_DEBUGS("CookieStoreParse") << "added domain (" << mDomainStart << " to " << mDomainEnd << "), new cookie is: " << mCookie << LL_ENDL;
- }
-
- // If the cookie doesn't have a path, add "/".
- if(mPathEnd <= mPathStart)
- {
- // Figure out whether this cookie ended with a ";" or not...
- std::string::size_type last_char = mCookie.find_last_not_of(" ");
- if((last_char != std::string::npos) && (mCookie[last_char] != ';'))
- {
- mCookie += ";";
- }
-
- mCookie += " path=";
- mPathStart = mCookie.size();
- mCookie += "/";
- mPathEnd = mCookie.size();
-
- LL_DEBUGS("CookieStoreParse") << "added path (" << mPathStart << " to " << mPathEnd << "), new cookie is: " << mCookie << LL_ENDL;
- }
-
-
- return true;
-}
-
-std::string::size_type LLPluginCookieStore::Cookie::findFieldEnd(std::string::size_type start, std::string::size_type end)
-{
- std::string::size_type result = start;
-
- if(end == std::string::npos)
- end = mCookie.size();
-
- bool in_quotes = false;
- for(; (result < end); result++)
- {
- switch(mCookie[result])
- {
- case '\\':
- if(in_quotes)
- result++; // The next character is backslash-quoted. Skip over it.
- break;
- case '"':
- in_quotes = !in_quotes;
- break;
- case ';':
- if(!in_quotes)
- return result;
- break;
- }
- }
-
- // If we got here, no ';' was found.
- return end;
-}
-
-bool LLPluginCookieStore::Cookie::matchName(std::string::size_type start, std::string::size_type end, const char *name)
-{
- // NOTE: this assumes 'name' is already in lowercase. The code which uses it should be able to arrange this...
-
- while((start < end) && (*name != '\0'))
- {
- if(tolower(mCookie[start]) != *name)
- return false;
-
- start++;
- name++;
- }
-
- // iff both strings hit the end at the same time, they're equal.
- return ((start == end) && (*name == '\0'));
-}
-
-std::string LLPluginCookieStore::getAllCookies()
-{
- std::stringstream result;
- writeAllCookies(result);
- return result.str();
-}
-
-void LLPluginCookieStore::writeAllCookies(std::ostream& s)
-{
- cookie_map_t::iterator iter;
- for(iter = mCookies.begin(); iter != mCookies.end(); iter++)
- {
- // Don't return expired cookies
- if(!iter->second->isDead())
- {
- s << (iter->second->getCookie()) << "\n";
- }
- }
-
-}
-
-std::string LLPluginCookieStore::getPersistentCookies()
-{
- std::stringstream result;
- writePersistentCookies(result);
- return result.str();
-}
-
-void LLPluginCookieStore::writePersistentCookies(std::ostream& s)
-{
- cookie_map_t::iterator iter;
- for(iter = mCookies.begin(); iter != mCookies.end(); iter++)
- {
- // Don't return expired cookies or session cookies
- if(!iter->second->isDead() && !iter->second->isSessionCookie())
- {
- s << iter->second->getCookie() << "\n";
- }
- }
-}
-
-std::string LLPluginCookieStore::getChangedCookies(bool clear_changed)
-{
- std::stringstream result;
- writeChangedCookies(result, clear_changed);
-
- return result.str();
-}
-
-void LLPluginCookieStore::writeChangedCookies(std::ostream& s, bool clear_changed)
-{
- if(mHasChangedCookies)
- {
- LL_DEBUGS() << "returning changed cookies: " << LL_ENDL;
- cookie_map_t::iterator iter;
- for(iter = mCookies.begin(); iter != mCookies.end(); )
- {
- cookie_map_t::iterator next = iter;
- next++;
-
- // Only return cookies marked as "changed"
- if(iter->second->isChanged())
- {
- s << iter->second->getCookie() << "\n";
-
- LL_DEBUGS() << " " << iter->second->getCookie() << LL_ENDL;
-
- // If requested, clear the changed mark
- if(clear_changed)
- {
- if(iter->second->isDead())
- {
- // If this cookie was previously marked dead, it needs to be removed entirely.
- delete iter->second;
- mCookies.erase(iter);
- }
- else
- {
- // Not dead, just mark as not changed.
- iter->second->setChanged(false);
- }
- }
- }
-
- iter = next;
- }
- }
-
- if(clear_changed)
- mHasChangedCookies = false;
-}
-
-void LLPluginCookieStore::setAllCookies(const std::string &cookies, bool mark_changed)
-{
- clearCookies();
- setCookies(cookies, mark_changed);
-}
-
-void LLPluginCookieStore::readAllCookies(std::istream& s, bool mark_changed)
-{
- clearCookies();
- readCookies(s, mark_changed);
-}
-
-void LLPluginCookieStore::setCookies(const std::string &cookies, bool mark_changed)
-{
- std::string::size_type start = 0;
-
- while(start != std::string::npos)
- {
- std::string::size_type end = cookies.find_first_of("\r\n", start);
- if(end > start)
- {
- // The line is non-empty. Try to create a cookie from it.
- setOneCookie(cookies, start, end, mark_changed);
- }
- start = cookies.find_first_not_of("\r\n ", end);
- }
-}
-
-void LLPluginCookieStore::setCookiesFromHost(const std::string &cookies, const std::string &host, bool mark_changed)
-{
- std::string::size_type start = 0;
-
- while(start != std::string::npos)
- {
- std::string::size_type end = cookies.find_first_of("\r\n", start);
- if(end > start)
- {
- // The line is non-empty. Try to create a cookie from it.
- setOneCookie(cookies, start, end, mark_changed, host);
- }
- start = cookies.find_first_not_of("\r\n ", end);
- }
-}
-
-void LLPluginCookieStore::readCookies(std::istream& s, bool mark_changed)
-{
- std::string line;
- while(s.good() && !s.eof())
- {
- std::getline(s, line);
- if(!line.empty())
- {
- // Try to create a cookie from this line.
- setOneCookie(line, 0, std::string::npos, mark_changed);
- }
- }
-}
-
-std::string LLPluginCookieStore::quoteString(const std::string &s)
-{
- std::stringstream result;
-
- result << '"';
-
- for(std::string::size_type i = 0; i < s.size(); ++i)
- {
- char c = s[i];
- switch(c)
- {
- // All these separators need to be quoted in HTTP headers, according to section 2.2 of rfc 2616:
- case '(': case ')': case '<': case '>': case '@':
- case ',': case ';': case ':': case '\\': case '"':
- case '/': case '[': case ']': case '?': case '=':
- case '{': case '}': case ' ': case '\t':
- result << '\\';
- break;
- }
-
- result << c;
- }
-
- result << '"';
-
- return result.str();
-}
-
-std::string LLPluginCookieStore::unquoteString(const std::string &s)
-{
- std::stringstream result;
-
- bool in_quotes = false;
-
- for(std::string::size_type i = 0; i < s.size(); ++i)
- {
- char c = s[i];
- switch(c)
- {
- case '\\':
- if(in_quotes)
- {
- // The next character is backslash-quoted. Pass it through untouched.
- ++i;
- if(i < s.size())
- {
- result << s[i];
- }
- continue;
- }
- break;
- case '"':
- in_quotes = !in_quotes;
- continue;
- break;
- }
-
- result << c;
- }
-
- return result.str();
-}
-
-// The flow for deleting a cookie is non-obvious enough that I should call it out here...
-// Deleting a cookie is done by setting a cookie with the same name, path, and domain, but with an expire timestamp in the past.
-// (This is exactly how a web server tells a browser to delete a cookie.)
-// 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.
-// 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
-// delete operation (in the form of the expired cookie) is passed along.
-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)
-{
- Cookie *cookie = Cookie::createFromString(s, cookie_start, cookie_end, host);
- if(cookie)
- {
- LL_DEBUGS("CookieStoreUpdate") << "setting cookie: " << cookie->getCookie() << LL_ENDL;
-
- // Create a key for this cookie
- std::string key = cookie->getKey();
-
- // Check to see whether this cookie should have expired
- if(!cookie->isSessionCookie() && (cookie->getDate() < LLDate::now()))
- {
- // This cookie has expired.
- if(mark_changed)
- {
- // If we're marking cookies as changed, we should keep it anyway since we'll need to send it out with deltas.
- cookie->setDead(true);
- LL_DEBUGS("CookieStoreUpdate") << " marking dead" << LL_ENDL;
- }
- else
- {
- // If we're not marking cookies as changed, we don't need to keep this cookie at all.
- // If the cookie was already in the list, delete it.
- removeCookie(key);
-
- delete cookie;
- cookie = NULL;
-
- LL_DEBUGS("CookieStoreUpdate") << " removing" << LL_ENDL;
- }
- }
-
- if(cookie)
- {
- // If it already exists in the map, replace it.
- cookie_map_t::iterator iter = mCookies.find(key);
- if(iter != mCookies.end())
- {
- if(iter->second->getCookie() == cookie->getCookie())
- {
- // The new cookie is identical to the old -- don't mark as changed.
- // Just leave the old one in the map.
- delete cookie;
- cookie = NULL;
-
- LL_DEBUGS("CookieStoreUpdate") << " unchanged" << LL_ENDL;
- }
- else
- {
- // A matching cookie was already in the map. Replace it.
- delete iter->second;
- iter->second = cookie;
-
- cookie->setChanged(mark_changed);
- if(mark_changed)
- mHasChangedCookies = true;
-
- LL_DEBUGS("CookieStoreUpdate") << " replacing" << LL_ENDL;
- }
- }
- else
- {
- // The cookie wasn't in the map. Insert it.
- mCookies.insert(std::make_pair(key, cookie));
-
- cookie->setChanged(mark_changed);
- if(mark_changed)
- mHasChangedCookies = true;
-
- LL_DEBUGS("CookieStoreUpdate") << " adding" << LL_ENDL;
- }
- }
- }
- else
- {
- LL_WARNS("CookieStoreUpdate") << "failed to parse cookie: " << s.substr(cookie_start, cookie_end - cookie_start) << LL_ENDL;
- }
-
-}
-
-void LLPluginCookieStore::clearCookies()
-{
- std::for_each(mCookies.begin(), mCookies.end(), DeletePairedPointer());
- mCookies.clear();
-}
-
-void LLPluginCookieStore::removeCookie(const std::string &key)
-{
- cookie_map_t::iterator iter = mCookies.find(key);
- if(iter != mCookies.end())
- {
- delete iter->second;
- mCookies.erase(iter);
- }
-}
-
-void LLPluginCookieStore::removeCookiesByDomain(const std::string &domain)
-{
- cookie_map_t::iterator iter = mCookies.begin();
- while(iter != mCookies.end())
- {
- if(iter->second->getDomain() == domain)
- {
- cookie_map_t::iterator doErase = iter;
- iter++;
- delete doErase->second;
- mCookies.erase(doErase);
- }
- else
- {
- iter++;
- }
- }
-}