diff options
author | Rider Linden <rider@lindenlab.com> | 2018-04-13 14:50:07 -0700 |
---|---|---|
committer | Rider Linden <rider@lindenlab.com> | 2018-04-13 14:50:07 -0700 |
commit | 033b6269593deb370378663354ee2a78eba54387 (patch) | |
tree | 200c8575c45a2384a8fa2803cd9a724c85057eae /indra/llplugin | |
parent | 7b56db31f91545102744885b4f70c93278e837bd (diff) | |
parent | 41f4e854732a7c75a813b2e99285cc41bde13e5e (diff) |
Merge
Diffstat (limited to 'indra/llplugin')
-rw-r--r-- | indra/llplugin/CMakeLists.txt | 19 | ||||
-rw-r--r-- | indra/llplugin/llpluginclassmedia.cpp | 48 | ||||
-rw-r--r-- | indra/llplugin/llpluginclassmedia.h | 10 | ||||
-rw-r--r-- | indra/llplugin/llpluginclassmediaowner.h | 28 | ||||
-rw-r--r-- | indra/llplugin/llplugincookiestore.cpp | 689 | ||||
-rw-r--r-- | indra/llplugin/llplugincookiestore.h | 123 | ||||
-rw-r--r-- | indra/llplugin/tests/llplugincookiestore_test.cpp | 207 |
7 files changed, 48 insertions, 1076 deletions
diff --git a/indra/llplugin/CMakeLists.txt b/indra/llplugin/CMakeLists.txt index 129efeb529..5cc129a267 100644 --- a/indra/llplugin/CMakeLists.txt +++ b/indra/llplugin/CMakeLists.txt @@ -29,7 +29,6 @@ include_directories(SYSTEM set(llplugin_SOURCE_FILES llpluginclassmedia.cpp - llplugincookiestore.cpp llplugininstance.cpp llpluginmessage.cpp llpluginmessagepipe.cpp @@ -43,7 +42,6 @@ set(llplugin_HEADER_FILES llpluginclassmedia.h llpluginclassmediaowner.h - llplugincookiestore.h llplugininstance.h llpluginmessage.h llpluginmessageclasses.h @@ -70,20 +68,3 @@ add_library (llplugin ${llplugin_SOURCE_FILES}) add_subdirectory(slplugin) -# Add tests -if (LL_TESTS) - include(LLAddBuildTest) - # UNIT TESTS - SET(llplugin_TEST_SOURCE_FILES - llplugincookiestore.cpp - ) - - # llplugincookiestore has a dependency on curl, so we need to link the curl library into the test. - set_source_files_properties( - llplugincookiestore.cpp - PROPERTIES - LL_TEST_ADDITIONAL_LIBRARIES "${CURL_LIBRARIES};${NGHTTP2_LIBRARIES}" - ) - - LL_ADD_PROJECT_UNIT_TESTS(llplugin "${llplugin_TEST_SOURCE_FILES}") -endif (LL_TESTS) diff --git a/indra/llplugin/llpluginclassmedia.cpp b/indra/llplugin/llpluginclassmedia.cpp index 680017204c..78eb6e75bb 100644 --- a/indra/llplugin/llpluginclassmedia.cpp +++ b/indra/llplugin/llpluginclassmedia.cpp @@ -31,6 +31,9 @@ #include "llpluginclassmedia.h" #include "llpluginmessageclasses.h" +#include "llcontrol.h" + +extern LLControlGroup gSavedSettings; static int LOW_PRIORITY_TEXTURE_SIZE_DEFAULT = 256; @@ -792,15 +795,22 @@ F64 LLPluginClassMedia::getCPUUsage() return result; } -void LLPluginClassMedia::sendPickFileResponse(const std::string &file) +void LLPluginClassMedia::sendPickFileResponse(const std::vector<std::string> files) { LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "pick_file_response"); - message.setValue("file", file); if(mPlugin && mPlugin->isBlocked()) { // If the plugin sent a blocking pick-file request, the response should unblock it. message.setValueBoolean("blocking_response", true); } + + LLSD file_list = LLSD::emptyArray(); + for (std::vector<std::string>::const_iterator in_iter = files.begin(); in_iter != files.end(); ++in_iter) + { + file_list.append(LLSD::String(*in_iter)); + } + message.setValueLLSD("file_list", file_list); + sendMessage(message); } @@ -836,11 +846,17 @@ void LLPluginClassMedia::paste() sendMessage(message); } -void LLPluginClassMedia::setUserDataPath(const std::string &user_data_path_cache, const std::string &user_data_path_cookies) +void LLPluginClassMedia::setUserDataPath(const std::string &user_data_path_cache, + const std::string &user_data_path_cookies, + const std::string &user_data_path_cef_log) { LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "set_user_data_path"); message.setValue("cache_path", user_data_path_cache); message.setValue("cookies_path", user_data_path_cookies); + message.setValue("cef_log_file", user_data_path_cef_log); + + bool cef_verbose_log = gSavedSettings.getBOOL("CefVerboseLog"); + message.setValueBoolean("cef_verbose_log", cef_verbose_log); sendMessage(message); } @@ -1090,6 +1106,7 @@ void LLPluginClassMedia::receivePluginMessage(const LLPluginMessage &message) } else if(message_name == "pick_file") { + mIsMultipleFilePick = message.getValueBoolean("multiple_files"); mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_PICK_FILE_REQUEST); } else if(message_name == "auth_request") @@ -1151,7 +1168,12 @@ void LLPluginClassMedia::receivePluginMessage(const LLPluginMessage &message) { mClickURL = message.getValue("uri"); mClickTarget = message.getValue("target"); - mClickUUID = message.getValue("uuid"); + + // need a link to have a UUID that identifies it to a system further + // upstream - plugin could make it but we have access to LLUUID here + // so why don't we use it + mClickUUID = LLUUID::generateNewID().asString(); + mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_CLICK_LINK_HREF); } else if(message_name == "click_nofollow") @@ -1166,13 +1188,6 @@ void LLPluginClassMedia::receivePluginMessage(const LLPluginMessage &message) mStatusCode = message.getValueS32("status_code"); mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_NAVIGATE_ERROR_PAGE); } - else if(message_name == "cookie_set") - { - if(mOwner) - { - mOwner->handleCookieSet(this, message.getValue("cookie")); - } - } else if(message_name == "close_request") { mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_CLOSE_REQUEST); @@ -1287,16 +1302,9 @@ void LLPluginClassMedia::clear_cookies() sendMessage(message); } -void LLPluginClassMedia::set_cookies(const std::string &cookies) -{ - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "set_cookies"); - message.setValue("cookies", cookies); - sendMessage(message); -} - -void LLPluginClassMedia::enable_cookies(bool enable) +void LLPluginClassMedia::cookies_enabled(bool enable) { - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "enable_cookies"); + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "cookies_enabled"); message.setValueBoolean("enable", enable); sendMessage(message); } diff --git a/indra/llplugin/llpluginclassmedia.h b/indra/llplugin/llpluginclassmedia.h index 3b0739d044..3b3075c6bd 100644 --- a/indra/llplugin/llpluginclassmedia.h +++ b/indra/llplugin/llpluginclassmedia.h @@ -176,7 +176,7 @@ public: F64 getCPUUsage(); - void sendPickFileResponse(const std::string &file); + void sendPickFileResponse(const std::vector<std::string> files); void sendAuthResponse(bool ok, const std::string &username, const std::string &password); @@ -195,7 +195,7 @@ public: bool canPaste() const { return mCanPaste; }; // These can be called before init(), and they will be queued and sent before the media init message. - void setUserDataPath(const std::string &user_data_path_cache, const std::string &user_data_path_cookies); + void setUserDataPath(const std::string &user_data_path_cache, const std::string &user_data_path_cookies, const std::string &user_data_path_cef_log); void setLanguageCode(const std::string &language_code); void setPluginsEnabled(const bool enabled); void setJavascriptEnabled(const bool enabled); @@ -210,7 +210,7 @@ public: void clear_cache(); void clear_cookies(); void set_cookies(const std::string &cookies); - void enable_cookies(bool enable); + void cookies_enabled(bool enable); void proxy_setup(bool enable, const std::string &host = LLStringUtil::null, int port = 0); void browse_stop(); void browse_reload(bool ignore_cache = false); @@ -277,6 +277,9 @@ public: std::string getAuthURL() const { return mAuthURL; }; std::string getAuthRealm() const { return mAuthRealm; }; + // These are valid during MEDIA_EVENT_PICK_FILE_REQUEST + bool getIsMultipleFilePick() const { return mIsMultipleFilePick; } + // These are valid during MEDIA_EVENT_LINK_HOVERED std::string getHoverText() const { return mHoverText; }; std::string getHoverLink() const { return mHoverLink; }; @@ -435,6 +438,7 @@ protected: std::string mHoverText; std::string mHoverLink; std::string mFileDownloadFilename; + bool mIsMultipleFilePick; ///////////////////////////////////////// // media_time class diff --git a/indra/llplugin/llpluginclassmediaowner.h b/indra/llplugin/llpluginclassmediaowner.h index 391c23d883..89f55eaf71 100644 --- a/indra/llplugin/llpluginclassmediaowner.h +++ b/indra/llplugin/llpluginclassmediaowner.h @@ -1,4 +1,4 @@ -/** +/** * @file llpluginclassmediaowner.h * @brief LLPluginClassMedia handles interaction with a plugin which knows about the "media" message class. * @@ -6,21 +6,21 @@ * $LicenseInfo:firstyear=2008&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 @@ -34,18 +34,17 @@ #include <queue> class LLPluginClassMedia; -class LLPluginCookieStore; class LLPluginClassMediaOwner { public: typedef enum { - MEDIA_EVENT_CONTENT_UPDATED, // contents/dirty rect have updated + MEDIA_EVENT_CONTENT_UPDATED, // contents/dirty rect have updated MEDIA_EVENT_TIME_DURATION_UPDATED, // current time and/or duration have updated MEDIA_EVENT_SIZE_CHANGED, // media size has changed MEDIA_EVENT_CURSOR_CHANGED, // plugin has requested a cursor change - + MEDIA_EVENT_NAVIGATE_BEGIN, // browser has begun navigation MEDIA_EVENT_NAVIGATE_COMPLETE, // browser has finished navigation MEDIA_EVENT_PROGRESS_UPDATED, // browser has updated loading progress @@ -58,8 +57,8 @@ public: MEDIA_EVENT_CLOSE_REQUEST, // The plugin requested its window be closed (currently hooked up to javascript window.close in webkit) MEDIA_EVENT_PICK_FILE_REQUEST, // The plugin wants the user to pick a file MEDIA_EVENT_GEOMETRY_CHANGE, // The plugin requested its window geometry be changed (per the javascript window interface) - - MEDIA_EVENT_PLUGIN_FAILED_LAUNCH, // The plugin failed to launch + + MEDIA_EVENT_PLUGIN_FAILED_LAUNCH, // The plugin failed to launch MEDIA_EVENT_PLUGIN_FAILED, // The plugin died unexpectedly MEDIA_EVENT_AUTH_REQUEST, // The plugin wants to display an auth dialog @@ -69,9 +68,9 @@ public: MEDIA_EVENT_DEBUG_MESSAGE, // plugin sending back debug information for host to process MEDIA_EVENT_LINK_HOVERED // Got a "link hovered" event from the plugin - + } EMediaEvent; - + typedef enum { MEDIA_NONE, // Uninitialized -- no useful state @@ -81,12 +80,11 @@ public: MEDIA_PLAYING, // playing (only for time-based media) MEDIA_PAUSED, // paused (only for time-based media) MEDIA_DONE // finished playing (only for time-based media) - + } EMediaStatus; - + virtual ~LLPluginClassMediaOwner() {}; virtual void handleMediaEvent(LLPluginClassMedia* /*self*/, EMediaEvent /*event*/) {}; - virtual void handleCookieSet(LLPluginClassMedia* /*self*/, const std::string &/*cookie*/) {}; }; #endif // LL_LLPLUGINCLASSMEDIAOWNER_H 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++; - } - } -} diff --git a/indra/llplugin/llplugincookiestore.h b/indra/llplugin/llplugincookiestore.h deleted file mode 100644 index a2fdeab647..0000000000 --- a/indra/llplugin/llplugincookiestore.h +++ /dev/null @@ -1,123 +0,0 @@ -/** - * @file llplugincookiestore.h - * @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 - */ - -#ifndef LL_LLPLUGINCOOKIESTORE_H -#define LL_LLPLUGINCOOKIESTORE_H - -#include "lldate.h" -#include <map> -#include <string> -#include <iostream> - -class LLPluginCookieStore -{ - LOG_CLASS(LLPluginCookieStore); -public: - LLPluginCookieStore(); - ~LLPluginCookieStore(); - - // gets all cookies currently in storage -- use when initializing a plugin - std::string getAllCookies(); - void writeAllCookies(std::ostream& s); - - // gets only persistent cookies (i.e. not session cookies) -- use when writing cookies to a file - std::string getPersistentCookies(); - void writePersistentCookies(std::ostream& s); - - // gets cookies which are marked as "changed" -- use when sending periodic updates to plugins - std::string getChangedCookies(bool clear_changed = true); - void writeChangedCookies(std::ostream& s, bool clear_changed = true); - - // (re)initializes internal data structures and bulk-sets cookies -- use when reading cookies from a file - void setAllCookies(const std::string &cookies, bool mark_changed = false); - void readAllCookies(std::istream& s, bool mark_changed = false); - - // sets one or more cookies (without reinitializing anything) -- use when receiving cookies from a plugin - void setCookies(const std::string &cookies, bool mark_changed = true); - void readCookies(std::istream& s, bool mark_changed = true); - - // sets one or more cookies (without reinitializing anything), supplying a hostname the cookies came from -- use when setting a cookie manually - void setCookiesFromHost(const std::string &cookies, const std::string &host, bool mark_changed = true); - - // quote or unquote a string as per the definition of 'quoted-string' in rfc2616 - static std::string quoteString(const std::string &s); - static std::string unquoteString(const std::string &s); - - void removeCookiesByDomain(const std::string &domain); - -private: - - void setOneCookie(const std::string &s, std::string::size_type cookie_start, std::string::size_type cookie_end, bool mark_changed, const std::string &host = LLStringUtil::null); - - class Cookie - { - public: - static Cookie *createFromString(const std::string &s, std::string::size_type cookie_start = 0, std::string::size_type cookie_end = std::string::npos, const std::string &host = LLStringUtil::null); - - // Construct a string from the cookie that uniquely represents it, to be used as a key in a std::map. - std::string getKey() const; - std::string getDomain() const; - - const std::string &getCookie() const { return mCookie; }; - bool isSessionCookie() const { return mDate.isNull(); }; - - bool isDead() const { return mDead; }; - void setDead(bool dead) { mDead = dead; }; - - bool isChanged() const { return mChanged; }; - void setChanged(bool changed) { mChanged = changed; }; - - const LLDate &getDate() const { return mDate; }; - - private: - Cookie(const std::string &s, std::string::size_type cookie_start = 0, std::string::size_type cookie_end = std::string::npos); - bool parse(const std::string &host); - std::string::size_type findFieldEnd(std::string::size_type start = 0, std::string::size_type end = std::string::npos); - bool matchName(std::string::size_type start, std::string::size_type end, const char *name); - - std::string mCookie; // The full cookie, in RFC 2109 string format - LLDate mDate; // The expiration date of the cookie. For session cookies, this will be a null date (mDate.isNull() is true). - // Start/end indices of various parts of the cookie string. Stored as indices into the string to save space and time. - std::string::size_type mNameStart, mNameEnd; - std::string::size_type mValueStart, mValueEnd; - std::string::size_type mDomainStart, mDomainEnd; - std::string::size_type mPathStart, mPathEnd; - bool mDead; - bool mChanged; - }; - - typedef std::map<std::string, Cookie*> cookie_map_t; - - cookie_map_t mCookies; - bool mHasChangedCookies; - - void clearCookies(); - void removeCookie(const std::string &key); -}; - -#endif // LL_LLPLUGINCOOKIESTORE_H diff --git a/indra/llplugin/tests/llplugincookiestore_test.cpp b/indra/llplugin/tests/llplugincookiestore_test.cpp deleted file mode 100644 index c2cb236cba..0000000000 --- a/indra/llplugin/tests/llplugincookiestore_test.cpp +++ /dev/null @@ -1,207 +0,0 @@ -/** - * @file llplugincookiestore_test.cpp - * @brief Unit tests for LLPluginCookieStore. - * - * @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 <list> -#include "../test/lltut.h" - -#include "../llplugincookiestore.h" - - -namespace tut -{ - // Main Setup - struct LLPluginCookieStoreFixture - { - LLPluginCookieStoreFixture() - { - // We need dates definitively in the past and the future to properly test cookie expiration. - LLDate now = LLDate::now(); - LLDate past(now.secondsSinceEpoch() - (60.0 * 60.0 * 24.0)); // 1 day in the past - LLDate future(now.secondsSinceEpoch() + (60.0 * 60.0 * 24.0)); // 1 day in the future - - mPastString = past.asRFC1123(); - mFutureString = future.asRFC1123(); - } - - std::string mPastString; - std::string mFutureString; - LLPluginCookieStore mCookieStore; - - // List of cookies used for validation - std::list<std::string> mCookies; - - // This sets up mCookies from a string returned by one of the functions in LLPluginCookieStore - void setCookies(const std::string &cookies) - { - mCookies.clear(); - 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) - { - std::string line(cookies, start, end - start); - if(line.find_first_not_of("\r\n\t ") != std::string::npos) - { - // The line has some non-whitespace characters. Save it to the list. - mCookies.push_back(std::string(cookies, start, end - start)); - } - } - start = cookies.find_first_not_of("\r\n ", end); - } - } - - // This ensures that a cookie matching the one passed is in the list. - void ensureCookie(const std::string &cookie) - { - std::list<std::string>::iterator iter; - for(iter = mCookies.begin(); iter != mCookies.end(); iter++) - { - if(*iter == cookie) - { - // Found the cookie - // TODO: this should do a smarter equality comparison on the two cookies, instead of just a string compare. - return; - } - } - - // Didn't find this cookie - std::string message = "cookie not found: "; - message += cookie; - ensure(message, false); - } - - // This ensures that the number of cookies in the list matches what's expected. - void ensureSize(const std::string &message, size_t size) - { - if(mCookies.size() != size) - { - std::stringstream full_message; - - full_message << message << " (expected " << size << ", actual " << mCookies.size() << ")"; - ensure(full_message.str(), false); - } - } - }; - - typedef test_group<LLPluginCookieStoreFixture> factory; - typedef factory::object object; - factory tf("LLPluginCookieStore"); - - // Tests - template<> template<> - void object::test<1>() - { - // Test 1: cookie uniqueness and update lists. - // Valid, distinct cookies: - - std::string cookie01 = "cookieA=value; domain=example.com; path=/"; - std::string cookie02 = "cookieB=value; Domain=example.com; Path=/; Max-Age=10; Secure; Version=1; Comment=foo!; HTTPOnly"; // cookie with every supported field, in different cases. - std::string cookie03 = "cookieA=value; domain=foo.example.com; path=/"; // different domain - std::string cookie04 = "cookieA=value; domain=example.com; path=/bar/"; // different path - std::string cookie05 = "cookieC; domain=example.com; path=/"; // empty value - std::string cookie06 = "cookieD=value; domain=example.com; path=/; expires="; // different name, persistent cookie - cookie06 += mFutureString; - - mCookieStore.setCookies(cookie01); - mCookieStore.setCookies(cookie02); - mCookieStore.setCookies(cookie03); - mCookieStore.setCookies(cookie04); - mCookieStore.setCookies(cookie05); - mCookieStore.setCookies(cookie06); - - // Invalid cookies (these will get parse errors and not be added to the store) - - std::string badcookie01 = "cookieD=value; domain=example.com; path=/; foo=bar"; // invalid field name - std::string badcookie02 = "cookieE=value; path=/"; // no domain - - mCookieStore.setCookies(badcookie01); - mCookieStore.setCookies(badcookie02); - - // All cookies added so far should have been marked as "changed" - setCookies(mCookieStore.getChangedCookies()); - ensureSize("count of changed cookies", 6); - ensureCookie(cookie01); - ensureCookie(cookie02); - ensureCookie(cookie03); - ensureCookie(cookie04); - ensureCookie(cookie05); - ensureCookie(cookie06); - - // Save off the current state of the cookie store (we'll restore it later) - std::string savedCookies = mCookieStore.getAllCookies(); - - // Test replacing cookies - std::string cookie01a = "cookieA=newvalue; domain=example.com; path=/"; // updated value - std::string cookie02a = "cookieB=newvalue; domain=example.com; path=/; expires="; // remove cookie (by setting an expire date in the past) - cookie02a += mPastString; - - mCookieStore.setCookies(cookie01a); - mCookieStore.setCookies(cookie02a); - - // test for getting changed cookies - setCookies(mCookieStore.getChangedCookies()); - ensureSize("count of updated cookies", 2); - ensureCookie(cookie01a); - ensureCookie(cookie02a); - - // and for the state of the store after getting changed cookies - setCookies(mCookieStore.getAllCookies()); - ensureSize("count of valid cookies", 5); - ensureCookie(cookie01a); - ensureCookie(cookie03); - ensureCookie(cookie04); - ensureCookie(cookie05); - ensureCookie(cookie06); - - // Check that only the persistent cookie is returned here - setCookies(mCookieStore.getPersistentCookies()); - ensureSize("count of persistent cookies", 1); - ensureCookie(cookie06); - - // Restore the cookie store to a previous state and verify - mCookieStore.setAllCookies(savedCookies); - - // Since setAllCookies defaults to not marking cookies as changed, this list should be empty. - setCookies(mCookieStore.getChangedCookies()); - ensureSize("count of changed cookies after restore", 0); - - // Verify that the restore worked as it should have. - setCookies(mCookieStore.getAllCookies()); - ensureSize("count of restored cookies", 6); - ensureCookie(cookie01); - ensureCookie(cookie02); - ensureCookie(cookie03); - ensureCookie(cookie04); - ensureCookie(cookie05); - ensureCookie(cookie06); - } - -} |