From 9cdcbf32ff69afc4c2c8f220df90aabed62dafde Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Wed, 27 Aug 2008 16:04:36 +0000 Subject: 2008-08-28 Tatsuhiro Tsujikawa Added CookieStorage class which is based on RFC2109 and manages cookies more strictly than CookieBox and CookieBoxFactory class. * src/CookieStorage.cc * src/CookieStorage.h * src/Cookie.cc * src/Cookie.h * test/CookieStorageTest.cc * test/CookieTest.cc --- src/Cookie.cc | 93 +++++++++++++++++++++++++-- src/Cookie.h | 10 ++- src/CookieStorage.cc | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/CookieStorage.h | 95 ++++++++++++++++++++++++++++ 4 files changed, 367 insertions(+), 6 deletions(-) create mode 100644 src/CookieStorage.cc create mode 100644 src/CookieStorage.h (limited to 'src') diff --git a/src/Cookie.cc b/src/Cookie.cc index dcd95a9f..46693934 100644 --- a/src/Cookie.cc +++ b/src/Cookie.cc @@ -35,6 +35,8 @@ #include "Cookie.h" #include "Util.h" #include "A2STR.h" +#include "TimeA2.h" +#include namespace aria2 { @@ -48,7 +50,7 @@ Cookie::Cookie(const std::string& name, value(value), expires(expires), path(path), - domain(domain), + domain(Util::toLower(domain)), secure(secure), onetime(false) {} @@ -60,7 +62,7 @@ Cookie::Cookie(const std::string& name, name(name), value(value), path(path), - domain(domain), + domain(Util::toLower(domain)), secure(secure), onetime(true) {} @@ -85,11 +87,40 @@ bool Cookie::good() const return !name.empty(); } -bool Cookie::match(const std::string& host, const std::string& dir, time_t date, bool secure) const +static bool pathInclude(const std::string& requestPath, const std::string& path) { + if(requestPath == path) { + return true; + } + if(Util::startsWith(requestPath, path)) { + if(*path.rbegin() != '/' && requestPath[path.size()] != '/') { + return false; + } + } else if(*path.rbegin() != '/' || *requestPath.rbegin() == '/' || + !Util::startsWith(requestPath+"/", path)) { + return false; + } + return true; +} + +static bool domainMatch(const std::string& requestHost, + const std::string& domain) +{ + if(*domain.begin() == '.') { + return Util::endsWith("."+requestHost, domain); + } else { + return requestHost == domain; + } +} + +bool Cookie::match(const std::string& requestHost, + const std::string& requestPath, + time_t date, bool secure) const +{ + std::string lowerRequestHost = Util::toLower(requestHost); if((secure || (!this->secure && !secure)) && - Util::endsWith("."+host, this->domain) && - Util::startsWith(dir, this->path) && + domainMatch(lowerRequestHost, this->domain) && + pathInclude(requestPath, path) && (this->onetime || (date < this->expires))) { return true; } else { @@ -97,4 +128,56 @@ bool Cookie::match(const std::string& host, const std::string& dir, time_t date, } } +bool Cookie::validate(const std::string& requestHost, + const std::string& requestPath) const +{ + std::string lowerRequestHost = Util::toLower(requestHost); + if(lowerRequestHost != domain) { + // domain must start with '.' + if(*domain.begin() != '.') { + return false; + } + // domain must not end with '.' + if(*domain.rbegin() == '.') { + return false; + } + // domain must include at least one embeded '.' + if(domain.size() < 4 || domain.find(".", 1) == std::string::npos) { + return false; + } + if(!Util::endsWith(lowerRequestHost, domain)) { + return false; + } + // From RFC2109 + // * The request-host is a FQDN (not IP address) and has the form HD, + // where D is the value of the Domain attribute, and H is a string + // that contains one or more dots. + if(std::count(lowerRequestHost.begin(), + lowerRequestHost.begin()+ + (lowerRequestHost.size()-domain.size()), '.') + > 0) { + return false; + } + } + if(requestPath != path) { + // From RFC2109 + // * The value for the Path attribute is not a prefix of the request- + // URI. + if(!pathInclude(requestPath, path)) { + return false; + } + } + return !name.empty(); +} + +bool Cookie::operator==(const Cookie& cookie) const +{ + return domain == cookie.domain && path == cookie.path && name == cookie.name; +} + +bool Cookie::isExpired() const +{ + return !onetime && Time().getTime() >= expires; +} + } // namespace aria2 diff --git a/src/Cookie.h b/src/Cookie.h index a4529749..eeafb1f8 100644 --- a/src/Cookie.h +++ b/src/Cookie.h @@ -75,7 +75,15 @@ public: bool good() const; - bool match(const std::string& host, const std::string& dir, time_t date, bool secure) const; + bool match(const std::string& requestHost, const std::string& requestPath, + time_t date, bool secure) const; + + bool validate(const std::string& requestHost, + const std::string& requestPath) const; + + bool operator==(const Cookie& cookie) const; + + bool isExpired() const; }; typedef std::deque Cookies; diff --git a/src/CookieStorage.cc b/src/CookieStorage.cc new file mode 100644 index 00000000..6f2ccaf5 --- /dev/null +++ b/src/CookieStorage.cc @@ -0,0 +1,175 @@ +/* */ +#include "CookieStorage.h" +#include "Util.h" +#include "LogFactory.h" +#include "Logger.h" +#include "RecoverableException.h" +#include "StringFormat.h" +#include "NsCookieParser.h" +#ifdef HAVE_SQLITE3 +# include "Sqlite3MozCookieParser.h" +#endif // HAVE_SQLITE3 +#include +#include + +namespace aria2 { + +CookieStorage::CookieStorage():_logger(LogFactory::getInstance()) {} + +CookieStorage::~CookieStorage() {} + +bool CookieStorage::store(const Cookie& cookie) +{ + if(!cookie.good()) { + return false; + } + std::deque::iterator i = std::find(_cookies.begin(), _cookies.end(), + cookie); + if(i == _cookies.end()) { + if(cookie.isExpired()) { + return false; + } else { + _cookies.push_back(cookie); + return true; + } + } else if(cookie.isExpired()) { + _cookies.erase(i); + return false; + } else { + *i = cookie; + return true; + } +} + +void CookieStorage::storeCookies(const std::deque& cookies) +{ + for(std::deque::const_iterator i = cookies.begin(); + i != cookies.end(); ++i) { + store(*i); + } +} + +bool CookieStorage::parseAndStore(const std::string& setCookieString, + const std::string& requestHost, + const std::string& requestPath) +{ + if(Util::isNumbersAndDotsNotation(requestHost)) { + return false; + } + Cookie cookie = _parser.parse(setCookieString, requestHost, requestPath); + if(cookie.validate(requestHost, requestPath)) { + return store(cookie); + } else { + return false; + } +} + +class CriteriaMatch:public std::unary_function { +private: + std::string _requestHost; + std::string _requestPath; + time_t _date; + bool _secure; +public: + CriteriaMatch(const std::string& requestHost, const std::string& requestPath, + time_t date, bool secure): + _requestHost(requestHost), + _requestPath(requestPath), + _date(date), + _secure(secure) {} + + bool operator()(const Cookie& cookie) const + { + return cookie.match(_requestHost, _requestPath, _date, _secure); + } +}; + +class OrderByPathDesc:public std::binary_function { +public: + bool operator()(const Cookie& lhs, const Cookie& rhs) const + { + return lhs.path > rhs.path; + } +}; + +std::deque CookieStorage::criteriaFind(const std::string& requestHost, + const std::string& requestPath, + time_t date, bool secure) const +{ + std::deque res; + std::remove_copy_if(_cookies.begin(), _cookies.end(), std::back_inserter(res), + std::not1(CriteriaMatch(requestHost, requestPath, date, secure))); + std::sort(res.begin(), res.end(), OrderByPathDesc()); + return res; +} + +size_t CookieStorage::size() const +{ + return _cookies.size(); +} + +void CookieStorage::load(const std::string& filename) +{ + char header[16]; // "SQLite format 3" plus \0 + { + std::ifstream s(filename.c_str()); + s.get(header, sizeof(header)); + if(s.bad()) { + throw RecoverableException + (StringFormat("Failed to read header of cookie file %s", + filename.c_str()).str()); + } + } + try { + if(std::string(header) == "SQLite format 3") { +#ifdef HAVE_SQLITE3 + storeCookies(Sqlite3MozCookieParser().parse(filename)); +#else // !HAVE_SQLITE3 + throw RecoverableException + ("Cannot read SQLite3 database because SQLite3 support is disabled by" + " configuration."); +#endif // !HAVE_SQLITE3 + } else { + storeCookies(NsCookieParser().parse(filename)); + } + } catch(RecoverableException& e) { + throw RecoverableException + (StringFormat("Failed to load cookies from %s", filename.c_str()).str(), + e); + } +} + +} // namespace aria2 diff --git a/src/CookieStorage.h b/src/CookieStorage.h new file mode 100644 index 00000000..d8ea6149 --- /dev/null +++ b/src/CookieStorage.h @@ -0,0 +1,95 @@ +/* */ +#ifndef _D_COOKIE_STORAGE_H_ +#define _D_COOKIE_STORAGE_H_ + +#include "common.h" +#include "a2time.h" +#include "Cookie.h" +#include "CookieParser.h" +#include +#include + +namespace aria2 { + +class Logger; + +class CookieStorage { +private: + std::deque _cookies; + + CookieParser _parser; + + Logger* _logger; + + void storeCookies(const std::deque& cookies); +public: + CookieStorage(); + + ~CookieStorage(); + + // Returns true if cookie is stored or updated existing cookie. + // Returns false if cookie is expired. + bool store(const Cookie& cookie); + + // Returns true if cookie is stored or updated existing cookie. + // Otherwise, returns false. + bool parseAndStore(const std::string& setCookieString, + const std::string& requestHost, + const std::string& requestPath); + + std::deque criteriaFind(const std::string& requestHost, + const std::string& requestPath, + time_t date, bool secure) const; + + void load(const std::string& filename); + + size_t size() const; + + std::deque::const_iterator begin() const + { + return _cookies.begin(); + } + + std::deque::const_iterator end() const + { + return _cookies.end(); + } + +}; + +} // namespace aria2 + +#endif // _D_COOKIE_STORAGE_H_ -- cgit