diff options
| author | Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com> | 2008-08-27 16:04:36 +0000 |
|---|---|---|
| committer | Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com> | 2008-08-27 16:04:36 +0000 |
| commit | 9cdcbf32ff69afc4c2c8f220df90aabed62dafde (patch) | |
| tree | 0388730a58d56f83b829c193f3a44fcc5b98c52e /src | |
| parent | 58ad225655b27370e0a3ebe5f603ec9890360b90 (diff) | |
2008-08-28 Tatsuhiro Tsujikawa <tujikawa at rednoah dot com>
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
Diffstat (limited to 'src')
| -rw-r--r-- | src/Cookie.cc | 93 | ||||
| -rw-r--r-- | src/Cookie.h | 10 | ||||
| -rw-r--r-- | src/CookieStorage.cc | 175 | ||||
| -rw-r--r-- | src/CookieStorage.h | 95 |
4 files changed, 367 insertions, 6 deletions
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 <algorithm> 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<Cookie> 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 @@ +/* <!-- copyright */ +/* + * aria2 - The high speed download utility + * + * Copyright (C) 2006 Tatsuhiro Tsujikawa + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + */ +/* copyright --> */ +#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 <algorithm> +#include <fstream> + +namespace aria2 { + +CookieStorage::CookieStorage():_logger(LogFactory::getInstance()) {} + +CookieStorage::~CookieStorage() {} + +bool CookieStorage::store(const Cookie& cookie) +{ + if(!cookie.good()) { + return false; + } + std::deque<Cookie>::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<Cookie>& cookies) +{ + for(std::deque<Cookie>::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<Cookie, bool> { +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<Cookie, Cookie, bool> { +public: + bool operator()(const Cookie& lhs, const Cookie& rhs) const + { + return lhs.path > rhs.path; + } +}; + +std::deque<Cookie> CookieStorage::criteriaFind(const std::string& requestHost, + const std::string& requestPath, + time_t date, bool secure) const +{ + std::deque<Cookie> 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 @@ +/* <!-- copyright */ +/* + * aria2 - The high speed download utility + * + * Copyright (C) 2006 Tatsuhiro Tsujikawa + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + */ +/* copyright --> */ +#ifndef _D_COOKIE_STORAGE_H_ +#define _D_COOKIE_STORAGE_H_ + +#include "common.h" +#include "a2time.h" +#include "Cookie.h" +#include "CookieParser.h" +#include <string> +#include <deque> + +namespace aria2 { + +class Logger; + +class CookieStorage { +private: + std::deque<Cookie> _cookies; + + CookieParser _parser; + + Logger* _logger; + + void storeCookies(const std::deque<Cookie>& 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<Cookie> 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<Cookie>::const_iterator begin() const + { + return _cookies.begin(); + } + + std::deque<Cookie>::const_iterator end() const + { + return _cookies.end(); + } + +}; + +} // namespace aria2 + +#endif // _D_COOKIE_STORAGE_H_ |
