diff options
Diffstat (limited to 'indra/llplugin')
| -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 | 
6 files changed, 741 insertions, 0 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 | 
