diff options
| -rw-r--r-- | indra/llplugin/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | indra/llplugin/llpluginclassmedia.cpp | 14 | ||||
| -rw-r--r-- | indra/llplugin/llpluginclassmedia.h | 1 | ||||
| -rw-r--r-- | indra/llplugin/llpluginclassmediaowner.h | 2 | ||||
| -rw-r--r-- | indra/llplugin/llplugincookiestore.cpp | 600 | ||||
| -rw-r--r-- | indra/llplugin/llplugincookiestore.h | 122 | ||||
| -rw-r--r-- | indra/media_plugins/webkit/media_plugin_webkit.cpp | 17 | ||||
| -rw-r--r-- | indra/newview/llappviewer.cpp | 2 | ||||
| -rw-r--r-- | indra/newview/llstartup.cpp | 3 | ||||
| -rw-r--r-- | indra/newview/llviewermedia.cpp | 150 | ||||
| -rw-r--r-- | indra/newview/llviewermedia.h | 11 | ||||
| -rw-r--r-- | install.xml | 8 | 
12 files changed, 927 insertions, 5 deletions
| diff --git a/indra/llplugin/CMakeLists.txt b/indra/llplugin/CMakeLists.txt index 6706775d4f..def9fcbeae 100644 --- a/indra/llplugin/CMakeLists.txt +++ b/indra/llplugin/CMakeLists.txt @@ -23,6 +23,7 @@ include_directories(  set(llplugin_SOURCE_FILES      llpluginclassmedia.cpp +    llplugincookiestore.cpp      llplugininstance.cpp      llpluginmessage.cpp      llpluginmessagepipe.cpp @@ -36,6 +37,7 @@ set(llplugin_HEADER_FILES      llpluginclassmedia.h      llpluginclassmediaowner.h +    llplugincookiestore.h      llplugininstance.h      llpluginmessage.h      llpluginmessageclasses.h diff --git a/indra/llplugin/llpluginclassmedia.cpp b/indra/llplugin/llpluginclassmedia.cpp index bf0e19473e..e09b511a6e 100644 --- a/indra/llplugin/llpluginclassmedia.cpp +++ b/indra/llplugin/llpluginclassmedia.cpp @@ -993,6 +993,13 @@ void LLPluginClassMedia::receivePluginMessage(const LLPluginMessage &message)  			mClickTargetType = TARGET_NONE;  			mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_CLICK_LINK_NOFOLLOW);  		} +		else if(message_name == "cookie_set") +		{ +			if(mOwner) +			{ +				mOwner->handleCookieSet(this, message.getValue("cookie")); +			} +		}  		else  		{  			LL_WARNS("Plugin") << "Unknown " << message_name << " class message: " << message_name << LL_ENDL; @@ -1076,6 +1083,13 @@ 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)  {  	LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "enable_cookies"); diff --git a/indra/llplugin/llpluginclassmedia.h b/indra/llplugin/llpluginclassmedia.h index 79356beb68..8c7b00f45b 100644 --- a/indra/llplugin/llpluginclassmedia.h +++ b/indra/llplugin/llpluginclassmedia.h @@ -189,6 +189,7 @@ public:  	void focus(bool focused);  	void clear_cache();  	void clear_cookies(); +	void set_cookies(const std::string &cookies);  	void enable_cookies(bool enable);  	void proxy_setup(bool enable, const std::string &host = LLStringUtil::null, int port = 0);  	void browse_stop(); diff --git a/indra/llplugin/llpluginclassmediaowner.h b/indra/llplugin/llpluginclassmediaowner.h index 6d369cd51a..5669b81fd1 100644 --- a/indra/llplugin/llpluginclassmediaowner.h +++ b/indra/llplugin/llpluginclassmediaowner.h @@ -39,6 +39,7 @@  #include <queue>  class LLPluginClassMedia; +class LLPluginCookieStore;  class LLPluginClassMediaOwner  { @@ -78,6 +79,7 @@ public:  	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 new file mode 100644 index 0000000000..1964b8d789 --- /dev/null +++ b/indra/llplugin/llplugincookiestore.cpp @@ -0,0 +1,600 @@ +/**  + * @file llplugincookiestore.cpp + * @brief LLPluginCookieStore provides central storage for http cookies used by plugins + * + * @cond + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2010, Linden Research, Inc. + *  + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab.  Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + *  + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + *  + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + *  + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + * @endcond + */ + +#include "linden_common.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), +	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) +{ +	Cookie *result = new Cookie(s, cookie_start, cookie_end); + +	if(!result->parse()) +	{ +		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; +} + +bool LLPluginCookieStore::Cookie::parse() +{ +	bool first_field = true; + +	std::string::size_type cookie_end = mCookie.size(); +	std::string::size_type field_start = 0; + +	lldebugs << "parsing cookie: " << mCookie << llendl; +	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; +		} + +		lldebugs  +			<< "    field name: \"" << mCookie.substr(name_start, name_end - name_start)  +			<< "\", value: \"" << mCookie.substr(value_start, value_end - value_start) << "\"" +			<< llendl; +				 +		// 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); +				lldebugs << "        expire date parsed to: " << mDate.asRFC1123() << llendl; +#else +				// This doesn't work (rfc1123-format dates cause it to fail) +				if(!mDate.fromString(date_string)) +				{ +					// Date failed to parse. +					llwarns << "failed to parse cookie's expire date: " << date << llendl; +					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 +			{ +				// An unknown field is a parse failure +				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; +		 +	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) +	{ +		lldebugs << "returning changed cookies: " << llendl; +		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"; + +				lldebugs << "    " << iter->second->getCookie() << llendl; + +				// 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('\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("\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) +{ +	Cookie *cookie = Cookie::createFromString(s, cookie_start, cookie_end); +	if(cookie) +	{ +		lldebugs << "setting cookie: " << cookie->getCookie() << llendl; +		 +		// 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); +				lldebugs << "    marking dead" << llendl; +			} +			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; + +				lldebugs << "    removing" << llendl; +			} +		} +		 +		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; + +					lldebugs << "    unchanged" << llendl; +				} +				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; + +					lldebugs << "    replacing" << llendl; +				} +			} +			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; + +				lldebugs << "    adding" << llendl; +			} +		} +	} +} + +void LLPluginCookieStore::clearCookies() +{ +	while(!mCookies.empty()) +	{ +		cookie_map_t::iterator iter = mCookies.begin(); +		delete iter->second; +		mCookies.erase(iter); +	} +} + +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); +	} +} + diff --git a/indra/llplugin/llplugincookiestore.h b/indra/llplugin/llplugincookiestore.h new file mode 100644 index 0000000000..5250f008b6 --- /dev/null +++ b/indra/llplugin/llplugincookiestore.h @@ -0,0 +1,122 @@ +/**  + * @file llplugincookiestore.h + * @brief LLPluginCookieStore provides central storage for http cookies used by plugins + * + * @cond + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2010, Linden Research, Inc. + *  + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab.  Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + *  + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + *  + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + *  + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/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); + +	// 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); +	 +private: + +	void setOneCookie(const std::string &s, std::string::size_type cookie_start, std::string::size_type cookie_end, bool mark_changed); + +	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); +		 +		// Construct a string from the cookie that uniquely represents it, to be used as a key in a std::map. +		std::string getKey() 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(); +		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/media_plugins/webkit/media_plugin_webkit.cpp b/indra/media_plugins/webkit/media_plugin_webkit.cpp index 0462fce236..85d6b2f5ff 100644 --- a/indra/media_plugins/webkit/media_plugin_webkit.cpp +++ b/indra/media_plugins/webkit/media_plugin_webkit.cpp @@ -507,6 +507,19 @@ private:  		sendMessage(message);  	} + +	//////////////////////////////////////////////////////////////////////////////// +	// virtual +	void onCookieChanged(const EventType& event) +	{ +		LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "cookie_set"); +		message.setValue("cookie", event.getStringValue()); +		// These could be passed through as well, but aren't really needed. +//		message.setValue("uri", event.getEventUri()); +//		message.setValueBoolean("dead", (event.getIntValue() != 0)) +		sendMessage(message); +	} +	  	LLQtWebKit::EKeyboardModifier decodeModifiers(std::string &modifiers)  	{  		int result = 0; @@ -1051,6 +1064,10 @@ void MediaPluginWebKit::receiveMessage(const char *message_string)  				mJavascriptEnabled = message_in.getValueBoolean("enable");  				//LLQtWebKit::getInstance()->enableJavascript( mJavascriptEnabled );  			} +			else if(message_name == "set_cookies") +			{ +				LLQtWebKit::getInstance()->setCookies(message_in.getValue("cookies")); +			}  			else if(message_name == "proxy_setup")  			{  				bool val = message_in.getValueBoolean("enable"); diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index d30d7fd26d..a3d0b8d8d9 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1526,6 +1526,8 @@ bool LLAppViewer::cleanup()  	LLLocationHistory::getInstance()->save();  	LLAvatarIconIDCache::getInstance()->save(); +	 +	LLViewerMedia::saveCookieFile();  	llinfos << "Shutting down Threads" << llendflush; diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index edd03dc836..59d118abe2 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -938,6 +938,9 @@ bool idle_startup()  		// Load Avatars icons cache  		LLAvatarIconIDCache::getInstance()->load(); +		 +		// Load media plugin cookies +		LLViewerMedia::loadCookieFile();  		//-------------------------------------------------  		// Handle startup progress screen diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 2fcd3f1114..8863bf187d 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -45,6 +45,7 @@  #include "llviewertexturelist.h"  #include "llvovolume.h"  #include "llpluginclassmedia.h" +#include "llplugincookiestore.h"  #include "llviewerwindow.h"  #include "llfocusmgr.h"  #include "llcallbacklist.h" @@ -256,6 +257,7 @@ public:  		LLViewerMediaImpl *mMediaImpl;  		bool mInitialized;  }; +LLPluginCookieStore *LLViewerMedia::sCookieStore = NULL;  static LLViewerMedia::impl_list sViewerMediaImplList;  static LLViewerMedia::impl_id_map sViewerMediaTextureIDMap;  static LLTimer sMediaCreateTimer; @@ -264,6 +266,8 @@ static F32 sGlobalVolume = 1.0f;  static F64 sLowestLoadableImplInterest = 0.0f;  static bool sAnyMediaShowing = false;  static boost::signals2::connection sTeleportFinishConnection; +static std::string sUpdatedCookies; +static const char *PLUGIN_COOKIE_FILE_NAME = "plugin_cookies.txt";  //////////////////////////////////////////////////////////////////////////////////////////  static void add_media_impl(LLViewerMediaImpl* media) @@ -399,7 +403,6 @@ viewer_media_t LLViewerMedia::updateMediaImpl(LLMediaEntry* media_entry, const s  		media_impl->setHomeURL(media_entry->getHomeURL());  		media_impl->mMediaAutoPlay = media_entry->getAutoPlay();  		media_impl->mMediaEntryURL = media_entry->getCurrentURL(); -		  		if(media_impl->isAutoPlayable())  		{  			needs_navigate = true; @@ -698,6 +701,13 @@ static bool proximity_comparitor(const LLViewerMediaImpl* i1, const LLViewerMedi  void LLViewerMedia::updateMedia(void *dummy_arg)  {  	sAnyMediaShowing = false; +	sUpdatedCookies = getCookieStore()->getChangedCookies(); +	if(!sUpdatedCookies.empty()) +	{ +		lldebugs << "updated cookies will be sent to all loaded plugins: " << llendl; +		lldebugs << sUpdatedCookies << llendl; +	} +	  	impl_list::iterator iter = sViewerMediaImplList.begin();  	impl_list::iterator end = sViewerMediaImplList.end(); @@ -1095,6 +1105,116 @@ void LLViewerMedia::setProxyConfig(bool enable, const std::string &host, int por  /////////////////////////////////////////////////////////////////////////////////////////  // static  +///////////////////////////////////////////////////////////////////////////////////////// +// static +LLPluginCookieStore *LLViewerMedia::getCookieStore() +{ +	if(sCookieStore == NULL) +	{ +		sCookieStore = new LLPluginCookieStore; +	} +	 +	return sCookieStore; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::loadCookieFile() +{ +	// build filename for each user +	std::string resolved_filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, PLUGIN_COOKIE_FILE_NAME); + +	if (resolved_filename.empty()) +	{ +		llinfos << "can't get path to plugin cookie file - probably not logged in yet." << llendl; +		return; +	} +	 +	// open the file for reading +	llifstream file(resolved_filename); +	if (!file.is_open()) +	{ +		llwarns << "can't load plugin cookies from file \"" << PLUGIN_COOKIE_FILE_NAME << "\"" << llendl; +		return; +	} +	 +	getCookieStore()->readAllCookies(file, true); + +	file.close(); +	 +	// TODO: send the clear_cookies message to all loaded plugins +} + + +///////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::saveCookieFile() +{ +	// build filename for each user +	std::string resolved_filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, PLUGIN_COOKIE_FILE_NAME); + +	if (resolved_filename.empty()) +	{ +		llinfos << "can't get path to plugin cookie file - probably not logged in yet." << llendl; +		return; +	} + +	// open a file for writing +	llofstream file (resolved_filename); +	if (!file.is_open()) +	{ +		llwarns << "can't open plugin cookie file \"" << PLUGIN_COOKIE_FILE_NAME << "\" for writing" << llendl; +		return; +	} + +	getCookieStore()->writePersistentCookies(file); + +	file.close(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::addCookie(const std::string &name, const std::string &value, const std::string &domain, const LLDate &expires, const std::string &path, bool secure) +{ +	std::stringstream cookie; +	 +	cookie << name << "=" << LLPluginCookieStore::quoteString(value); +	 +	if(expires.notNull()) +	{ +		cookie << "; expires=" << expires.asRFC1123(); +	} +	 +	cookie << "; domain=" << domain; + +	cookie << "; path=" << path; +	 +	if(secure) +	{ +		cookie << "; secure"; +	} +	 +	getCookieStore()->setCookies(cookie.str()); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::addSessionCookie(const std::string &name, const std::string &value, const std::string &domain, const std::string &path, bool secure) +{ +	// A session cookie just has a NULL date. +	addCookie(name, value, domain, LLDate(), path, secure); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::removeCookie(const std::string &name, const std::string &domain, const std::string &path ) +{ +	// To remove a cookie, add one with the same name, domain, and path that expires in the past. +	 +	addCookie(name, "", domain, LLDate(LLDate::now().secondsSinceEpoch() - 1.0), path); +} + +  bool LLViewerMedia::hasInWorldMedia()  {  	if (sInWorldMediaDisabled) return false; @@ -1455,6 +1575,17 @@ bool LLViewerMediaImpl::initializePlugin(const std::string& media_type)  			media_source->clear_cache();  		} +		// TODO: Only send cookies to plugins that need them +		//  Ideally, the plugin should tell us whether it handles cookies or not -- either via the init response or through a separate message. +		//  Due to the ordering of messages, it's possible we wouldn't get that information back in time to send cookies before sending a navigate message, +		//  which could cause odd race conditions. +		std::string all_cookies = LLViewerMedia::getCookieStore()->getAllCookies(); +		lldebugs << "setting cookies: " << all_cookies << llendl; +		if(!all_cookies.empty()) +		{ +			media_source->set_cookies(all_cookies); +		} +				  		mMediaSource = media_source;  		updateVolume(); @@ -2152,6 +2283,16 @@ void LLViewerMediaImpl::update()  			}  		}  	} +	else +	{ +		// If we didn't just create the impl, it may need to get cookie updates. +		if(!sUpdatedCookies.empty()) +		{ +			// TODO: Only send cookies to plugins that need them +			mMediaSource->set_cookies(sUpdatedCookies); +		} +	} +  	if(mMediaSource == NULL)  	{ @@ -2614,6 +2755,13 @@ void LLViewerMediaImpl::handleMediaEvent(LLPluginClassMedia* plugin, LLPluginCla  ////////////////////////////////////////////////////////////////////////////////  // virtual +void LLViewerMediaImpl::handleCookieSet(LLPluginClassMedia* self, const std::string &cookie) +{ +	LLViewerMedia::getCookieStore()->setCookies(cookie); +} + +//////////////////////////////////////////////////////////////////////////////// +// virtual  void  LLViewerMediaImpl::cut()  { diff --git a/indra/newview/llviewermedia.h b/indra/newview/llviewermedia.h index f9870fb3b9..10dacf9532 100644 --- a/indra/newview/llviewermedia.h +++ b/indra/newview/llviewermedia.h @@ -50,6 +50,7 @@ class LLViewerMediaTexture;  class LLMediaEntry;  class LLVOVolume;  class LLMimeDiscoveryResponder; +class LLPluginCookieStore;  typedef LLPointer<LLViewerMediaImpl> viewer_media_t;  /////////////////////////////////////////////////////////////////////////////// @@ -145,8 +146,17 @@ public:  	// Set the proxy config for all loaded plugins  	static void setProxyConfig(bool enable, const std::string &host, int port); +	static LLPluginCookieStore *getCookieStore(); +	static void loadCookieFile(); +	static void saveCookieFile(); +	static void addCookie(const std::string &name, const std::string &value, const std::string &domain, const LLDate &expires, const std::string &path = std::string("/"), bool secure = false ); +	static void addSessionCookie(const std::string &name, const std::string &value, const std::string &domain, const std::string &path = std::string("/"), bool secure = false ); +	static void removeCookie(const std::string &name, const std::string &domain, const std::string &path = std::string("/") ); +	  private:  	static void onTeleportFinished(); +	 +	static LLPluginCookieStore *sCookieStore;  };  // Implementation functions not exported into header file @@ -294,6 +304,7 @@ public:  	// Inherited from LLPluginClassMediaOwner  	/*virtual*/ void handleMediaEvent(LLPluginClassMedia* plugin, LLPluginClassMediaOwner::EMediaEvent); +	/*virtual*/ void handleCookieSet(LLPluginClassMedia* self, const std::string &cookie);  	// LLEditMenuHandler overrides  	/*virtual*/ void	cut(); diff --git a/install.xml b/install.xml index 0f4ab373f5..25c9fdd045 100644 --- a/install.xml +++ b/install.xml @@ -948,9 +948,9 @@ anguage Infrstructure (CLI) international standard</string>            <key>darwin</key>            <map>              <key>md5sum</key> -            <string>ada82d41467cdb7f8e25a442f58b6963</string> +            <string>171bd85ebb81d319e1f15fab8092f8cd</string>              <key>url</key> -            <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/llqtwebkit-4.6-darwin-20100318.tar.bz2</uri> +            <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/llqtwebkit-4.6-darwin-20100326.tar.bz2</uri>            </map>            <key>linux</key>            <map> @@ -962,9 +962,9 @@ anguage Infrstructure (CLI) international standard</string>            <key>windows</key>            <map>              <key>md5sum</key> -            <string>93b49cfea365fe082eda3e466a9beec3</string> +            <string>04d86bb2eeed4f928d155cb5598ca6b5</string>              <key>url</key> -            <uri>http://viewer-source-downloads.s3.amazonaws.com/install_pkgs/llqtwebkit-windows-qt4.6-20100318.tar.bz2</uri> +            <uri>http://viewer-source-downloads.s3.amazonaws.com/install_pkgs/llqtwebkit-windows-qt4.6-20100326.tar.bz2</uri>            </map>          </map>        </map> | 
