Browse Source

2010-10-09 Tatsuhiro Tsujikawa <[email protected]>

	Rewritten Cookie class and Cookie parser based on
	http://tools.ietf.org/html/draft-ietf-httpstate-cookie-14 with
	some modifications. When parsing cookie date, match time first so
	that it parses asctime() format. The request-path must be ends
	with '/' so that request-path '/foo/' path-matches cookie-path
	'/foo' and '/foo/' in the proposed algorithm.
	* src/Cookie.cc
	* src/Cookie.h
	* src/CookieParser.cc: Removed
	* src/CookieParser.h: Removed
	* src/CookieStorage.cc
	* src/CookieStorage.h
	* src/HttpResponse.cc
	* src/Makefile.am
	* src/Makefile.in
	* src/MultiUrlRequestInfo.cc
	* src/NsCookieParser.cc
	* src/NsCookieParser.h
	* src/Sqlite3CookieParser.cc
	* src/Sqlite3CookieParser.h
	* src/a2functional.h
	* src/cookie_helper.cc
	* src/cookie_helper.h
	* src/util.cc
	* src/util.h
	* test/CookieBoxFactoryTest.cc: Removed
	* test/CookieHelperTest.cc
	* test/CookieParserTest.cc: Removed
	* test/CookieStorageTest.cc
	* test/CookieTest.cc
	* test/HttpRequestTest.cc
	* test/Makefile.am
	* test/Makefile.in
	* test/NsCookieParserTest.cc
	* test/Sqlite3CookieParserTest.cc
	* test/TestUtil.cc
	* test/TestUtil.h
	* test/a2functionalTest.cc
	* test/chromium_cookies.sqlite
	* test/cookies.sqlite
	* test/nscookietest.txt
Tatsuhiro Tsujikawa 15 years ago
parent
commit
8b17d4b276

+ 44 - 0
ChangeLog

@@ -1,3 +1,47 @@
+2010-10-09  Tatsuhiro Tsujikawa  <[email protected]>
+
+	Rewritten Cookie class and Cookie parser based on
+	http://tools.ietf.org/html/draft-ietf-httpstate-cookie-14 with
+	some modifications. When parsing cookie date, match time first so
+	that it parses asctime() format. The request-path must be ends
+	with '/' so that request-path '/foo/' path-matches cookie-path
+	'/foo' and '/foo/' in the proposed algorithm.
+	* src/Cookie.cc
+	* src/Cookie.h
+	* src/CookieParser.cc: Removed
+	* src/CookieParser.h: Removed
+	* src/CookieStorage.cc
+	* src/CookieStorage.h
+	* src/HttpResponse.cc
+	* src/Makefile.am
+	* src/Makefile.in
+	* src/MultiUrlRequestInfo.cc
+	* src/NsCookieParser.cc
+	* src/NsCookieParser.h
+	* src/Sqlite3CookieParser.cc
+	* src/Sqlite3CookieParser.h
+	* src/a2functional.h
+	* src/cookie_helper.cc
+	* src/cookie_helper.h
+	* src/util.cc
+	* src/util.h
+	* test/CookieBoxFactoryTest.cc: Removed
+	* test/CookieHelperTest.cc
+	* test/CookieParserTest.cc: Removed
+	* test/CookieStorageTest.cc
+	* test/CookieTest.cc
+	* test/HttpRequestTest.cc
+	* test/Makefile.am
+	* test/Makefile.in
+	* test/NsCookieParserTest.cc
+	* test/Sqlite3CookieParserTest.cc
+	* test/TestUtil.cc
+	* test/TestUtil.h
+	* test/a2functionalTest.cc
+	* test/chromium_cookies.sqlite
+	* test/cookies.sqlite
+	* test/nscookietest.txt
+
 2010-10-05  Tatsuhiro Tsujikawa  <[email protected]>
 
 	Supported ANSI C's asctime() format in Time::parseHTTPDate().

+ 53 - 163
src/Cookie.cc

@@ -2,7 +2,7 @@
 /*
  * aria2 - The high speed download utility
  *
- * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ * Copyright (C) 2010 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
@@ -34,173 +34,67 @@
 /* copyright --> */
 #include "Cookie.h"
 
-#include <algorithm>
 #include <sstream>
 
-#include "util.h"
 #include "A2STR.h"
-#include "TimeA2.h"
 #include "a2functional.h"
+#include "cookie_helper.h"
 
 namespace aria2 {
 
-static std::string prependDotIfNotExists(const std::string& domain)
-{
-  // From RFC2965:
-  // * Domain=value
-  //   OPTIONAL.  The value of the Domain attribute specifies the domain
-  //   for which the cookie is valid.  If an explicitly specified value
-  //   does not start with a dot, the user agent supplies a leading dot.
-  return (!domain.empty() && domain[0] != '.') ? A2STR::DOT_C+domain : domain;
-}
-
-std::string Cookie::normalizeDomain(const std::string& domain)
-{
-  if(domain.empty() || util::isNumericHost(domain)) {
-    return domain;
-  }
-  std::string md = prependDotIfNotExists(domain);
-  // TODO use util::split to strict verification
-  std::string::size_type p = md.find_last_of(A2STR::DOT_C);
-  if(p == 0 || p == std::string::npos) {
-    md += ".local";
-  }
-  return util::toLower(prependDotIfNotExists(md));
-}
-
-Cookie::Cookie(const std::string& name,
-               const std::string& value,
-               time_t  expiry,
-               const std::string& path,
-               const std::string& domain,
-               bool secure):
+Cookie::Cookie
+(const std::string& name,
+ const std::string& value,
+ time_t  expiryTime,
+ bool persistent,
+ const std::string& domain,
+ bool hostOnly,
+ const std::string& path,
+ bool secure,
+ bool httpOnly,
+ time_t creationTime):
   name_(name),
   value_(value),
-  expiry_(expiry),
+  expiryTime_(expiryTime),
+  persistent_(persistent),
+  domain_(domain),
+  hostOnly_(hostOnly),
   path_(path),
-  domain_(normalizeDomain(domain)),
   secure_(secure),
-  creationTime_(time(0)),
-  lastAccess_(creationTime_) {}
-
-Cookie::Cookie(const std::string& name,
-               const std::string& value,
-               const std::string& path,
-               const std::string& domain,
-               bool secure):
-  name_(name),
-  value_(value),
-  expiry_(0),
-  path_(path),
-  domain_(normalizeDomain(domain)),
-  secure_(secure),
-  creationTime_(time(0)),
-  lastAccess_(creationTime_) {}
-
-Cookie::Cookie():expiry_(0), secure_(false), lastAccess_(time(0)) {}
+  httpOnly_(httpOnly),
+  creationTime_(creationTime),
+  lastAccessTime_(creationTime) {}
+
+Cookie::Cookie():
+  expiryTime_(0),
+  persistent_(false),
+  hostOnly_(false),
+  secure_(false),
+  httpOnly_(false),
+  creationTime_(0),
+  lastAccessTime_(0) {}
 
 Cookie::~Cookie() {}
 
 std::string Cookie::toString() const
 {
-  return strconcat(name_, "=", value_);
-}
-
-bool Cookie::good() const
-{
-  return !name_.empty();
+  return strconcat(name_, '=', value_);
 }
 
-static bool pathInclude(const std::string& requestPath, const std::string& path)
+bool Cookie::match
+(const std::string& requestHost,
+ const std::string& requestPath,
+ time_t date, bool secure) const
 {
-  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)) {
+  if((secure_ && !secure) || isExpired(date) ||
+     !cookie::pathMatch(requestPath, path_)) {
     return false;
   }
-  return true;
-}
-
-static bool domainMatch(const std::string& normReqHost,
-                        const std::string& domain)
-{
-  // RFC2965 stated that:
-  //
-  // A Set-Cookie2 with Domain=ajax.com will be accepted, and the
-  // value for Domain will be taken to be .ajax.com, because a dot
-  // gets prepended to the value.
-  //
-  // Also original Netscape implementation behaves exactly the same.
-
-  // domain_ always starts ".". See Cookie::Cookie().
-  return util::endsWith(normReqHost, domain);
-}
-
-bool Cookie::match(const std::string& requestHost,
-                   const std::string& requestPath,
-                   time_t date, bool secure) const
-{
-  if((secure || (!secure_ && !secure)) &&
-     (requestHost == domain_ || // For default domain or IP address
-      domainMatch(normalizeDomain(requestHost), domain_)) &&
-     pathInclude(requestPath, path_) &&
-     (isSessionCookie() || (date < expiry_))) {
-    return true;
+  if(hostOnly_) {
+    return requestHost == domain_ ;
   } else {
-    return false;
-  }
-}
-
-bool Cookie::validate(const std::string& requestHost,
-                      const std::string& requestPath) const
-{
-  // If domain_ doesn't start with "." or it is IP address, then it
-  // must equal to requestHost. Otherwise, do domain tail match.
-  if(requestHost != domain_) {
-    std::string normReqHost = normalizeDomain(requestHost);
-    if(normReqHost != 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(A2STR::DOT_C, 1) == std::string::npos) {
-        return false;
-      }
-      if(!util::endsWith(normReqHost, domain_)) {
-        return false;
-      }
-      // From RFC2965 3.3.2 Rejecting Cookies
-      // * The request-host is a HDN (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.
-      size_t dotCount = std::count(normReqHost.begin(),
-                                   normReqHost.begin()+
-                                   (normReqHost.size()-domain_.size()), '.');
-      if(dotCount > 1 || (dotCount == 1 && normReqHost[0] != '.')) {
-        return false;
-      } 
-    }
-  }
-  if(requestPath != path_) {
-    // From RFC2965 3.3.2 Rejecting Cookies
-    // * The value for the Path attribute is not a prefix of the request-URI.
-    if(!pathInclude(requestPath, path_)) {
-      return false;
-    }
+    return cookie::domainMatch(requestHost, domain_);
   }
-  return good();
 }
 
 bool Cookie::operator==(const Cookie& cookie) const
@@ -209,19 +103,22 @@ bool Cookie::operator==(const Cookie& cookie) const
     name_ == cookie.name_;
 }
 
-bool Cookie::isExpired() const
+bool Cookie::isExpired(time_t base) const
 {
-  return !expiry_ == 0 && Time().getTime() >= expiry_;
+  return persistent_ && base > expiryTime_;
 }
 
 std::string Cookie::toNsCookieFormat() const
 {
   std::stringstream ss;
+  if(!hostOnly_) {
+    ss << A2STR::DOT_C;
+  }
   ss << domain_ << "\t";
-  if(util::startsWith(domain_, A2STR::DOT_C)) {
-    ss << "TRUE";
-  } else {
+  if(hostOnly_) {
     ss << "FALSE";
+  } else {
+    ss << "TRUE";
   }
   ss << "\t";
   ss << path_ << "\t";
@@ -231,22 +128,15 @@ std::string Cookie::toNsCookieFormat() const
     ss << "FALSE";
   }
   ss << "\t";
-  ss << expiry_ << "\t";
+  if(persistent_) {
+    ss << expiryTime_;
+  } else {
+    ss << 0;
+  }
+  ss << "\t";
   ss << name_ << "\t";
   ss << value_;
   return ss.str();
 }
 
-void Cookie::markOriginServerOnly()
-{
-  if(util::startsWith(domain_, A2STR::DOT_C)) {
-    domain_.erase(domain_.begin(), domain_.begin()+1);
-  }
-}
-
-void Cookie::updateLastAccess()
-{
-  lastAccess_ = time(0);
-}
-
 } // namespace aria2

+ 101 - 56
src/Cookie.h

@@ -2,7 +2,7 @@
 /*
  * aria2 - The high speed download utility
  *
- * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ * Copyright (C) 2010 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
@@ -32,8 +32,8 @@
  * files in the program, then also delete it here.
  */
 /* copyright --> */
-#ifndef _D_COOKIE_H_
-#define _D_COOKIE_H_
+#ifndef D_COOKIE_H
+#define D_COOKIE_H
 
 #include "common.h"
 
@@ -47,70 +47,83 @@ class Cookie {
 private:
   std::string name_;
   std::string value_;
-  time_t expiry_;
-  std::string path_;
+  time_t expiryTime_;
+  // If persistent_ is false, this is a session scope cookie and it is
+  // never expired during session. So isExpired() always returns
+  // false.
+  bool persistent_;
   std::string domain_;
+  bool hostOnly_;
+  std::string path_;
   bool secure_;
+  bool httpOnly_;
   time_t creationTime_;
-  time_t lastAccess_;
+  time_t lastAccessTime_;
 public:
-  /*
-   * If expires = 0 is given, then the cookie becomes session cookie.
-   * domain is normalized using normalizeDomain() function and
-   * assigned to domain_.  If domain is not specified in cookie, call
-   * markOriginServerOnly() after construction.
-   */
-  Cookie(const std::string& name,
-         const std::string& value,
-         time_t  expires,
-         const std::string& path,
-         const std::string& domain,
-         bool secure);
-
-  /*
-   * Creates session cookie. This is equivalent to Cookie(name, value,
-   * 0, path, domain, secure); domain is normalized using
-   * normalizeDomain() function and assigned to domain_.  If domain is
-   * not specified in cookie, call markOriginServerOnly() after
-   * construction.
-   */
-  Cookie(const std::string& name,
-         const std::string& value,
-         const std::string& path,
-         const std::string& domain,
-         bool secure);
-
   Cookie();
 
+  Cookie
+  (const std::string& name,
+   const std::string& value,
+   time_t  expiryTime,
+   bool persistent,
+   const std::string& domain,
+   bool hostOnly,
+   const std::string& path,
+   bool secure,
+   bool httpOnly,
+   time_t creationTime);
+
   ~Cookie();
 
   std::string toString() const;
 
-  bool good() 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 match
+  (const std::string& requestHost, const std::string& requestPath,
+   time_t date, bool secure) const;
 
   bool operator==(const Cookie& cookie) const;
 
-  bool isExpired() const;
+  bool isExpired(time_t base) const;
 
   const std::string& getName() const
   {
     return name_;
   }
 
+  void setName(const std::string& name)
+  {
+    name_ = name;
+  }
+
   const std::string& getValue() const
   {
     return value_;
   }
 
-  const std::string& getPath() const
+  void setValue(const std::string& value)
   {
-    return path_;
+    value_ = value;
+  }
+
+  time_t getExpiryTime() const
+  {
+    return expiryTime_;
+  }
+
+  void setExpiryTime(time_t expiryTime)
+  {
+    expiryTime_ = expiryTime;
+  }
+
+  bool getPersistent() const
+  {
+    return persistent_;
+  }
+
+  void setPersistent(bool persistent)
+  {
+    persistent_ = persistent;
   }
 
   const std::string& getDomain() const
@@ -118,42 +131,74 @@ public:
     return domain_;
   }
 
-  time_t getExpiry() const
+  void setDomain(const std::string& domain)
+  {
+    domain_ = domain;
+  }
+
+  bool getHostOnly() const
+  {
+    return hostOnly_;
+  }
+
+  void setHostOnly(bool hostOnly)
+  {
+    hostOnly_ = hostOnly;
+  }
+
+  const std::string& getPath() const
+  {
+    return path_;
+  }
+
+  void setPath(const std::string& path)
   {
-    return expiry_;
+    path_ = path;
   }
 
-  bool isSecureCookie() const
+  bool getSecure() const
   {
     return secure_;
   }
 
-  bool isSessionCookie() const
+  void setSecure(bool secure)
   {
-    return expiry_ == 0;
+    secure_ = secure;
   }
 
-  std::string toNsCookieFormat() const;
+  bool getHttpOnly() const
+  {
+    return httpOnly_;
+  }
 
-  // Makes this Cookie only sent to the origin server.  This function
-  // removes first "." from domain_ if domain_ starts with ".".
-  void markOriginServerOnly();
+  void setHttpOnly(bool httpOnly)
+  {
+    httpOnly_ = httpOnly;
+  }
 
   time_t getCreationTime() const
   {
     return creationTime_;
   }
 
-  void updateLastAccess();
+  void setCreationTime(time_t creationTime)
+  {
+    creationTime_ = creationTime;
+  }
+
+  time_t getLastAccessTime() const
+  {
+    return lastAccessTime_;
+  }
 
-  time_t getLastAccess() const
+  void setLastAccessTime(time_t lastAccessTime)
   {
-    return lastAccess_;
+    lastAccessTime_ = lastAccessTime;
   }
 
-  static std::string normalizeDomain(const std::string& domain);
+  std::string toNsCookieFormat() const;
 };
 
 } // namespace aria2
 
-#endif // _D_COOKIE_H_
+#endif // D_COOKIE_H

+ 0 - 103
src/CookieParser.cc

@@ -1,103 +0,0 @@
-/* <!-- 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 "CookieParser.h"
-
-#include <strings.h>
-
-#include <utility>
-#include <istream>
-#include <map>
-#include <vector>
-
-#include "util.h"
-#include "A2STR.h"
-#include "TimeA2.h"
-
-namespace aria2 {
-
-const std::string CookieParser::C_SECURE("secure");
-
-const std::string CookieParser::C_DOMAIN("domain");
-
-const std::string CookieParser::C_PATH("path");
-
-const std::string CookieParser::C_EXPIRES("expires");
-
-Cookie CookieParser::parse(const std::string& cookieStr, const std::string& defaultDomain, const std::string& defaultPath) const
-{
-  std::vector<std::string> terms;
-  util::split(cookieStr, std::back_inserter(terms), ";", true);
-  if(terms.empty()) {
-    return Cookie();
-  }
-  std::pair<std::string, std::string> nameValue;
-  util::split(nameValue, terms.front(), '=');
-
-  std::map<std::string, std::string> values;
-  for(std::vector<std::string>::iterator itr = terms.begin()+1,
-        eoi = terms.end(); itr != eoi; ++itr) {
-    std::pair<std::string, std::string> nv;
-    util::split(nv, *itr, '=');
-    values[nv.first] = nv.second;
-  }
-  bool useDefaultDomain = false;
-  std::map<std::string, std::string>::iterator mitr;
-  mitr = values.find(C_DOMAIN);
-  if(mitr == values.end() || (*mitr).second.empty()) {
-    useDefaultDomain = true;
-    values[C_DOMAIN] = defaultDomain;
-  }
-  mitr = values.find(C_PATH);
-  if(mitr == values.end() || (*mitr).second.empty()) {
-    values[C_PATH] = defaultPath;
-  }
-  time_t expiry = 0;
-  if(values.count(C_EXPIRES)) {
-    Time expiryTime = Time::parseHTTPDate(values[C_EXPIRES]);
-    if(expiryTime.good()) {
-      expiry = expiryTime.getTime();
-    }
-  }
-  Cookie cookie(nameValue.first, nameValue.second,
-                expiry,
-                values[C_PATH], values[C_DOMAIN],
-                values.find(C_SECURE) != values.end());
-  if(useDefaultDomain) {
-    cookie.markOriginServerOnly();
-  }
-  return cookie;
-}
-
-} // namespace aria2

+ 68 - 71
src/CookieStorage.cc

@@ -48,6 +48,7 @@
 #include "a2functional.h"
 #include "A2STR.h"
 #include "message.h"
+#include "cookie_helper.h"
 #ifdef HAVE_SQLITE3
 # include "Sqlite3CookieParserImpl.h"
 #endif // HAVE_SQLITE3
@@ -55,26 +56,21 @@
 namespace aria2 {
 
 CookieStorage::DomainEntry::DomainEntry
-(const std::string& domain):key_(domain)
-{
-  std::reverse(key_.begin(), key_.end());
-}
-
-void CookieStorage::DomainEntry::updateLastAccess()
-{
-  lastAccess_ = time(0);
-}
+(const std::string& domain):
+  key_(util::isNumericHost(domain)?domain:cookie::reverseDomainLevel(domain))
+{}
 
-bool CookieStorage::DomainEntry::addCookie(const Cookie& cookie)
+bool CookieStorage::DomainEntry::addCookie(const Cookie& cookie, time_t now)
 {
-  updateLastAccess();
-  std::deque<Cookie>::iterator i = std::find(cookies_.begin(), cookies_.end(),
-                                             cookie);
+  setLastAccessTime(now);
+  std::deque<Cookie>::iterator i =
+    std::find(cookies_.begin(), cookies_.end(), cookie);
   if(i == cookies_.end()) {
-    if(cookie.isExpired()) {
+    if(cookie.isExpired(now)) {
       return false;
     } else {
       if(cookies_.size() >= CookieStorage::MAX_COOKIE_PER_DOMAIN) {
+        // TODO First remove expired cookie
         std::deque<Cookie>::iterator m = std::min_element
           (cookies_.begin(), cookies_.end(), LeastRecentAccess<Cookie>());
         *m = cookie;
@@ -83,7 +79,7 @@ bool CookieStorage::DomainEntry::addCookie(const Cookie& cookie)
       }
       return true;
     }
-  } else if(cookie.isExpired()) {
+  } else if(cookie.isExpired(now)) {
     cookies_.erase(i);
     return false;
   } else {
@@ -114,12 +110,11 @@ static const size_t DOMAIN_EVICTION_TRIGGER = 2000;
 
 static const double DOMAIN_EVICTION_RATE = 0.1;
 
-bool CookieStorage::store(const Cookie& cookie)
+bool CookieStorage::store(const Cookie& cookie, time_t now)
 {
-  if(!cookie.good()) {
-    return false;
-  }
   if(domains_.size() >= DOMAIN_EVICTION_TRIGGER) {
+    // TODO Do this in a separete Command.
+    // TODO Erase expired cookie first
     std::sort(domains_.begin(), domains_.end(),
               LeastRecentAccess<DomainEntry>());
     size_t delnum = (size_t)(domains_.size()*DOMAIN_EVICTION_RATE);
@@ -131,9 +126,9 @@ bool CookieStorage::store(const Cookie& cookie)
     std::lower_bound(domains_.begin(), domains_.end(), v);
   bool added = false;
   if(i != domains_.end() && (*i).getKey() == v.getKey()) {
-    added = (*i).addCookie(cookie);
+    added = (*i).addCookie(cookie, now);
   } else {
-    added = v.addCookie(cookie);
+    added = v.addCookie(cookie, now);
     if(added) {
       domains_.insert(i, v);
     }
@@ -141,13 +136,15 @@ bool CookieStorage::store(const Cookie& cookie)
   return added;
 }
 
-bool CookieStorage::parseAndStore(const std::string& setCookieString,
-                                  const std::string& requestHost,
-                                  const std::string& requestPath)
+bool CookieStorage::parseAndStore
+(const std::string& setCookieString,
+ const std::string& requestHost,
+ const std::string& defaultPath,
+ time_t now)
 {
-  Cookie cookie = parser_.parse(setCookieString, requestHost, requestPath);
-  if(cookie.validate(requestHost, requestPath)) {
-    return store(cookie);
+  Cookie cookie;
+  if(cookie::parse(cookie, setCookieString, requestHost, defaultPath, now)) {
+    return store(cookie, now);
   } else {
     return false;
   }
@@ -198,32 +195,34 @@ public:
     // "name1=foo" with a path mapping of "/" should be sent after a
     // cookie "name1=foo2" with a path mapping of "/bar" if they are
     // both to be sent.
-    int comp = lhs.pathDepth_-rhs.pathDepth_;
-    if(comp == 0) {
-      return lhs.cookie_.getCreationTime() < rhs.cookie_.getCreationTime();
-    } else {
-      return comp > 0;
-    }
+    //
+    // See also http://tools.ietf.org/html/draft-ietf-httpstate-cookie-14
+    // section5.4
+    return lhs.pathDepth_ > rhs.pathDepth_ ||
+      (!(rhs.pathDepth_ > lhs.pathDepth_) &&
+       lhs.cookie_.getCreationTime() < rhs.cookie_.getCreationTime());
   }
 };
 }
 
+namespace {
 template<typename DomainInputIterator, typename CookieOutputIterator>
-static void searchCookieByDomainSuffix
+void searchCookieByDomainSuffix
 (const std::string& domain,
  DomainInputIterator first, DomainInputIterator last, CookieOutputIterator out,
  const std::string& requestHost,
  const std::string& requestPath,
- time_t date, bool secure)
+ time_t now, bool secure)
 {
   CookieStorage::DomainEntry v(domain);
   std::deque<CookieStorage::DomainEntry>::iterator i =
     std::lower_bound(first, last, v);
   if(i != last && (*i).getKey() == v.getKey()) {
-    (*i).updateLastAccess();
-    (*i).findCookie(out, requestHost, requestPath, date, secure);
+    (*i).setLastAccessTime(now);
+    (*i).findCookie(out, requestHost, requestPath, now, secure);
   }
 }
+}
 
 bool CookieStorage::contains(const Cookie& cookie) const
 {
@@ -237,36 +236,34 @@ bool CookieStorage::contains(const Cookie& cookie) const
   }
 }
 
-std::vector<Cookie> CookieStorage::criteriaFind(const std::string& requestHost,
-                                               const std::string& requestPath,
-                                               time_t date, bool secure)
+std::vector<Cookie> CookieStorage::criteriaFind
+(const std::string& requestHost,
+ const std::string& requestPath,
+ time_t now,
+ bool secure)
 {
   std::vector<Cookie> res;
-  bool numericHost = util::isNumericHost(requestHost);
-  searchCookieByDomainSuffix
-    ((!numericHost && requestHost.find(A2STR::DOT_C) == std::string::npos)?
-     requestHost+".local":requestHost,
-     domains_.begin(), domains_.end(),
-     std::back_inserter(res),
-     requestHost, requestPath, date, secure);
-  if(!numericHost) {
-    std::string normRequestHost = Cookie::normalizeDomain(requestHost);
-    std::vector<std::string> domainComponents;
-    util::split(normRequestHost, std::back_inserter(domainComponents),
-                A2STR::DOT_C);
-    if(domainComponents.size() <= 1) {
-      return res;
-    }
-    std::reverse(domainComponents.begin(), domainComponents.end());
-    std::string domain = A2STR::DOT_C;
-    domain += domainComponents[0];
-    for(std::vector<std::string>::const_iterator di =
-          domainComponents.begin()+1, eoi = domainComponents.end();
-        di != eoi; ++di) {
-      domain = strconcat(A2STR::DOT_C, *di, domain);
-      searchCookieByDomainSuffix(domain, domains_.begin(), domains_.end(),
-                                 std::back_inserter(res),
-                                 normRequestHost, requestPath, date, secure);
+  if(requestPath.empty()) {
+    return res;
+  }
+  std::string normRequestPath =
+    requestPath == A2STR::SLASH_C?requestPath:requestPath+A2STR::SLASH_C;
+  if(util::isNumericHost(requestHost)) {
+    searchCookieByDomainSuffix
+      (requestHost, domains_.begin(), domains_.end(), std::back_inserter(res),
+       requestHost, normRequestPath, now, secure);
+  } else {
+    std::vector<std::string> levels;
+    util::split(requestHost, std::back_inserter(levels),A2STR::DOT_C);
+    std::reverse(levels.begin(), levels.end());
+    std::string domain;
+    for(std::vector<std::string>::const_iterator i =
+          levels.begin(), eoi = levels.end();
+        i != eoi; ++i, domain.insert(domain.begin(), '.')) {
+      domain.insert(domain.begin(), (*i).begin(), (*i).end());
+      searchCookieByDomainSuffix
+        (domain, domains_.begin(), domains_.end(),
+         std::back_inserter(res), requestHost, normRequestPath, now, secure);
     }
   }
   std::vector<CookiePathDivider> divs;
@@ -288,7 +285,7 @@ size_t CookieStorage::size() const
   return numCookie;
 }
 
-bool CookieStorage::load(const std::string& filename)
+bool CookieStorage::load(const std::string& filename, time_t now)
 {
   char header[16]; // "SQLite format 3" plus \0
   std::ifstream s(filename.c_str(), std::ios::binary);
@@ -307,7 +304,7 @@ bool CookieStorage::load(const std::string& filename)
 #ifdef HAVE_SQLITE3
       std::vector<Cookie> cookies;
       try {
-        Sqlite3MozCookieParser(filename).parse(cookies);
+        Sqlite3MozCookieParser(filename).parse(cookies, now);
       } catch(RecoverableException& e) {
         if(logger_->info()) {
           logger_->info(EX_EXCEPTION_CAUGHT, e);
@@ -315,17 +312,17 @@ bool CookieStorage::load(const std::string& filename)
                         " Retrying, assuming it is Chromium cookie file.");
         }
         // Try chrome cookie format
-        Sqlite3ChromiumCookieParser(filename).parse(cookies);
+        Sqlite3ChromiumCookieParser(filename).parse(cookies, now);
       }
-      storeCookies(cookies.begin(), cookies.end());
+      storeCookies(cookies.begin(), cookies.end(), now);
 #else // !HAVE_SQLITE3
       throw DL_ABORT_EX
         ("Cannot read SQLite3 database because SQLite3 support is disabled by"
          " configuration.");
 #endif // !HAVE_SQLITE3
     } else {
-      std::vector<Cookie> cookies = NsCookieParser().parse(filename);
-      storeCookies(cookies.begin(), cookies.end());
+      std::vector<Cookie> cookies = NsCookieParser().parse(filename, now);
+      storeCookies(cookies.begin(), cookies.end(), now);
     }
     return true;
   } catch(RecoverableException& e) {

+ 32 - 24
src/CookieStorage.h

@@ -44,7 +44,6 @@
 
 #include "a2time.h"
 #include "Cookie.h"
-#include "CookieParser.h"
 
 namespace aria2 {
 
@@ -57,9 +56,12 @@ public:
 
   class DomainEntry {
   private:
+    // This is reversed domain level string.
+    // e.g. net.sourceforge.aria2
+    // e.g. 192.168.0.1
     std::string key_;
 
-    time_t lastAccess_;
+    time_t lastAccessTime_;
 
     std::deque<Cookie> cookies_;
   public:
@@ -75,12 +77,12 @@ public:
     (OutputIterator out,
      const std::string& requestHost,
      const std::string& requestPath,
-     time_t date, bool secure)
+     time_t now, bool secure)
     {
-      for(std::deque<Cookie>::iterator i = cookies_.begin();
-          i != cookies_.end(); ++i) {
-        if((*i).match(requestHost, requestPath, date, secure)) {
-          (*i).updateLastAccess();
+      for(std::deque<Cookie>::iterator i = cookies_.begin(),
+            eoi = cookies_.end(); i != eoi; ++i) {
+        if((*i).match(requestHost, requestPath, now, secure)) {
+          (*i).setLastAccessTime(now);
           out++ = *i;
         }
       }
@@ -92,13 +94,16 @@ public:
       return cookies_.size();
     }
 
-    bool addCookie(const Cookie& cookie);
+    bool addCookie(const Cookie& cookie, time_t now);
 
-    void updateLastAccess();
+    void setLastAccessTime(time_t lastAccessTime)
+    {
+      lastAccessTime_ = lastAccessTime;
+    }
 
-    time_t getLastAccess() const
+    time_t getLastAccessTime() const
     {
-      return lastAccess_;
+      return lastAccessTime_;
     }
 
     void writeCookie(std::ostream& o) const;
@@ -119,15 +124,13 @@ public:
 private:
   std::deque<DomainEntry> domains_;
 
-  CookieParser parser_;
-
   Logger* logger_;
 
   template<typename InputIterator>
-  void storeCookies(InputIterator first, InputIterator last)
+  void storeCookies(InputIterator first, InputIterator last, time_t now)
   {
     for(; first != last; ++first) {
-      store(*first);
+      store(*first, now);
     }
   }
 public:
@@ -136,27 +139,32 @@ public:
   ~CookieStorage();
 
   // Returns true if cookie is stored or updated existing cookie.
-  // Returns false if cookie is expired.
-  bool store(const Cookie& cookie);
+  // Returns false if cookie is expired. now is used as last access
+  // time.
+  bool store(const Cookie& cookie, time_t now);
 
   // 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);
+  // Otherwise, returns false. now is used as creation time and last
+  // access time.
+  bool parseAndStore
+  (const std::string& setCookieString,
+   const std::string& requestHost,
+   const std::string& requestPath,
+   time_t now);
 
   // Finds cookies matched with given criteria and returns them.
   // Matched cookies' lastAccess_ property is updated.
   std::vector<Cookie> criteriaFind(const std::string& requestHost,
                                    const std::string& requestPath,
-                                   time_t date, bool secure);
+                                   time_t now, bool secure);
 
   // Loads Cookies from file denoted by filename.  If compiled with
   // libsqlite3, this method automatically detects the specified file
   // is sqlite3 or just plain text file and calls appropriate parser
   // implementation class.  If Cookies are successfully loaded, this
-  // method returns true.  Otherwise, this method returns false.
-  bool load(const std::string& filename);
+  // method returns true.  Otherwise, this method returns false.  now
+  // is used as creation time and last access time.
+  bool load(const std::string& filename, time_t now);
   
   // Saves Cookies in Netspace format which is used in
   // Firefox1.2/Netscape/Mozilla.  If Cookies are successfully saved,

+ 3 - 3
src/HttpResponse.cc

@@ -129,12 +129,12 @@ std::string HttpResponse::determinFilename() const
 
 void HttpResponse::retrieveCookie()
 {
+  Time now;
   std::vector<std::string> v = httpHeader_->get(HttpHeader::SET_COOKIE);
   for(std::vector<std::string>::const_iterator itr = v.begin(), eoi = v.end();
       itr != eoi; ++itr) {
-    httpRequest_->getCookieStorage()->parseAndStore(*itr,
-                                                    httpRequest_->getHost(),
-                                                    httpRequest_->getDir());
+    httpRequest_->getCookieStorage()->parseAndStore
+      (*itr, httpRequest_->getHost(), httpRequest_->getDir(), now.getTime());
   }
 }
 

+ 2 - 2
src/Makefile.am

@@ -94,7 +94,6 @@ SRCS =  Socket.h\
 	ProgressAwareEntry.h\
 	RequestGroupEntry.cc RequestGroupEntry.h\
 	Cookie.cc Cookie.h\
-	CookieParser.cc CookieParser.h\
 	HttpHeaderProcessor.cc HttpHeaderProcessor.h\
 	FileEntry.cc FileEntry.h\
 	Platform.cc Platform.h\
@@ -211,7 +210,8 @@ SRCS =  Socket.h\
 	ChunkedDecodingStreamFilter.cc ChunkedDecodingStreamFilter.h\
 	NullSinkStreamFilter.cc NullSinkStreamFilter.h\
 	uri.cc uri.h\
-	Triplet.h
+	Triplet.h\
+	cookie_helper.cc cookie_helper.h
 
 if ENABLE_XML_RPC
 SRCS += XmlRpcRequestParserController.cc XmlRpcRequestParserController.h\

+ 47 - 48
src/Makefile.in

@@ -379,16 +379,15 @@ am__libaria2c_a_SOURCES_DIST = Socket.h SocketCore.cc SocketCore.h \
 	MultiUrlRequestInfo.h UriListParser.cc UriListParser.h \
 	RealtimeCommand.cc RealtimeCommand.h ProgressAwareEntry.h \
 	RequestGroupEntry.cc RequestGroupEntry.h Cookie.cc Cookie.h \
-	CookieParser.cc CookieParser.h HttpHeaderProcessor.cc \
-	HttpHeaderProcessor.h FileEntry.cc FileEntry.h Platform.cc \
-	Platform.h PStringDatum.h PStringSegment.cc PStringSegment.h \
-	PStringNumLoop.h PStringSelect.h PStringVisitor.h \
-	PStringBuildVisitor.cc PStringBuildVisitor.h \
-	ParameterizedStringParser.cc ParameterizedStringParser.h \
-	FixedWidthNumberDecorator.h NumberDecorator.h \
-	AlphaNumberDecorator.h TimeBasedCommand.cc TimeBasedCommand.h \
-	AutoSaveCommand.cc AutoSaveCommand.h PieceStorage.h \
-	DefaultPieceStorage.cc DefaultPieceStorage.h \
+	HttpHeaderProcessor.cc HttpHeaderProcessor.h FileEntry.cc \
+	FileEntry.h Platform.cc Platform.h PStringDatum.h \
+	PStringSegment.cc PStringSegment.h PStringNumLoop.h \
+	PStringSelect.h PStringVisitor.h PStringBuildVisitor.cc \
+	PStringBuildVisitor.h ParameterizedStringParser.cc \
+	ParameterizedStringParser.h FixedWidthNumberDecorator.h \
+	NumberDecorator.h AlphaNumberDecorator.h TimeBasedCommand.cc \
+	TimeBasedCommand.h AutoSaveCommand.cc AutoSaveCommand.h \
+	PieceStorage.h DefaultPieceStorage.cc DefaultPieceStorage.h \
 	UnknownLengthPieceStorage.cc UnknownLengthPieceStorage.h \
 	PieceStatMan.cc PieceStatMan.h StatCalc.h ConsoleStatCalc.cc \
 	ConsoleStatCalc.h TransferStat.cc TransferStat.h Dependency.h \
@@ -448,7 +447,8 @@ am__libaria2c_a_SOURCES_DIST = Socket.h SocketCore.cc SocketCore.h \
 	StreamFilter.h SinkStreamFilter.cc SinkStreamFilter.h \
 	ChunkedDecodingStreamFilter.cc ChunkedDecodingStreamFilter.h \
 	NullSinkStreamFilter.cc NullSinkStreamFilter.h uri.cc uri.h \
-	Triplet.h XmlRpcRequestParserController.cc \
+	Triplet.h cookie_helper.cc cookie_helper.h \
+	XmlRpcRequestParserController.cc \
 	XmlRpcRequestParserController.h \
 	XmlRpcRequestParserStateMachine.cc \
 	XmlRpcRequestParserStateMachine.h XmlRpcRequestParserState.h \
@@ -854,10 +854,9 @@ am__objects_32 = SocketCore.$(OBJEXT) Command.$(OBJEXT) \
 	StreamFileAllocationEntry.$(OBJEXT) \
 	MultiUrlRequestInfo.$(OBJEXT) UriListParser.$(OBJEXT) \
 	RealtimeCommand.$(OBJEXT) RequestGroupEntry.$(OBJEXT) \
-	Cookie.$(OBJEXT) CookieParser.$(OBJEXT) \
-	HttpHeaderProcessor.$(OBJEXT) FileEntry.$(OBJEXT) \
-	Platform.$(OBJEXT) PStringSegment.$(OBJEXT) \
-	PStringBuildVisitor.$(OBJEXT) \
+	Cookie.$(OBJEXT) HttpHeaderProcessor.$(OBJEXT) \
+	FileEntry.$(OBJEXT) Platform.$(OBJEXT) \
+	PStringSegment.$(OBJEXT) PStringBuildVisitor.$(OBJEXT) \
 	ParameterizedStringParser.$(OBJEXT) TimeBasedCommand.$(OBJEXT) \
 	AutoSaveCommand.$(OBJEXT) DefaultPieceStorage.$(OBJEXT) \
 	UnknownLengthPieceStorage.$(OBJEXT) PieceStatMan.$(OBJEXT) \
@@ -896,17 +895,18 @@ am__objects_32 = SocketCore.$(OBJEXT) Command.$(OBJEXT) \
 	ValueBase.$(OBJEXT) AdaptiveFileAllocationIterator.$(OBJEXT) \
 	StreamFilter.$(OBJEXT) SinkStreamFilter.$(OBJEXT) \
 	ChunkedDecodingStreamFilter.$(OBJEXT) \
-	NullSinkStreamFilter.$(OBJEXT) uri.$(OBJEXT) $(am__objects_1) \
-	$(am__objects_2) $(am__objects_3) $(am__objects_4) \
-	$(am__objects_5) $(am__objects_6) $(am__objects_7) \
-	$(am__objects_8) $(am__objects_9) $(am__objects_10) \
-	$(am__objects_11) $(am__objects_12) $(am__objects_13) \
-	$(am__objects_14) $(am__objects_15) $(am__objects_16) \
-	$(am__objects_17) $(am__objects_18) $(am__objects_19) \
-	$(am__objects_20) $(am__objects_21) $(am__objects_22) \
-	$(am__objects_23) $(am__objects_24) $(am__objects_25) \
-	$(am__objects_26) $(am__objects_27) $(am__objects_28) \
-	$(am__objects_29) $(am__objects_30) $(am__objects_31)
+	NullSinkStreamFilter.$(OBJEXT) uri.$(OBJEXT) \
+	cookie_helper.$(OBJEXT) $(am__objects_1) $(am__objects_2) \
+	$(am__objects_3) $(am__objects_4) $(am__objects_5) \
+	$(am__objects_6) $(am__objects_7) $(am__objects_8) \
+	$(am__objects_9) $(am__objects_10) $(am__objects_11) \
+	$(am__objects_12) $(am__objects_13) $(am__objects_14) \
+	$(am__objects_15) $(am__objects_16) $(am__objects_17) \
+	$(am__objects_18) $(am__objects_19) $(am__objects_20) \
+	$(am__objects_21) $(am__objects_22) $(am__objects_23) \
+	$(am__objects_24) $(am__objects_25) $(am__objects_26) \
+	$(am__objects_27) $(am__objects_28) $(am__objects_29) \
+	$(am__objects_30) $(am__objects_31)
 am_libaria2c_a_OBJECTS = $(am__objects_32)
 libaria2c_a_OBJECTS = $(am_libaria2c_a_OBJECTS)
 am__installdirs = "$(DESTDIR)$(bindir)"
@@ -1175,16 +1175,15 @@ SRCS = Socket.h SocketCore.cc SocketCore.h BinaryStream.h Command.cc \
 	MultiUrlRequestInfo.h UriListParser.cc UriListParser.h \
 	RealtimeCommand.cc RealtimeCommand.h ProgressAwareEntry.h \
 	RequestGroupEntry.cc RequestGroupEntry.h Cookie.cc Cookie.h \
-	CookieParser.cc CookieParser.h HttpHeaderProcessor.cc \
-	HttpHeaderProcessor.h FileEntry.cc FileEntry.h Platform.cc \
-	Platform.h PStringDatum.h PStringSegment.cc PStringSegment.h \
-	PStringNumLoop.h PStringSelect.h PStringVisitor.h \
-	PStringBuildVisitor.cc PStringBuildVisitor.h \
-	ParameterizedStringParser.cc ParameterizedStringParser.h \
-	FixedWidthNumberDecorator.h NumberDecorator.h \
-	AlphaNumberDecorator.h TimeBasedCommand.cc TimeBasedCommand.h \
-	AutoSaveCommand.cc AutoSaveCommand.h PieceStorage.h \
-	DefaultPieceStorage.cc DefaultPieceStorage.h \
+	HttpHeaderProcessor.cc HttpHeaderProcessor.h FileEntry.cc \
+	FileEntry.h Platform.cc Platform.h PStringDatum.h \
+	PStringSegment.cc PStringSegment.h PStringNumLoop.h \
+	PStringSelect.h PStringVisitor.h PStringBuildVisitor.cc \
+	PStringBuildVisitor.h ParameterizedStringParser.cc \
+	ParameterizedStringParser.h FixedWidthNumberDecorator.h \
+	NumberDecorator.h AlphaNumberDecorator.h TimeBasedCommand.cc \
+	TimeBasedCommand.h AutoSaveCommand.cc AutoSaveCommand.h \
+	PieceStorage.h DefaultPieceStorage.cc DefaultPieceStorage.h \
 	UnknownLengthPieceStorage.cc UnknownLengthPieceStorage.h \
 	PieceStatMan.cc PieceStatMan.h StatCalc.h ConsoleStatCalc.cc \
 	ConsoleStatCalc.h TransferStat.cc TransferStat.h Dependency.h \
@@ -1244,17 +1243,17 @@ SRCS = Socket.h SocketCore.cc SocketCore.h BinaryStream.h Command.cc \
 	StreamFilter.h SinkStreamFilter.cc SinkStreamFilter.h \
 	ChunkedDecodingStreamFilter.cc ChunkedDecodingStreamFilter.h \
 	NullSinkStreamFilter.cc NullSinkStreamFilter.h uri.cc uri.h \
-	Triplet.h $(am__append_1) $(am__append_2) $(am__append_3) \
-	$(am__append_4) $(am__append_5) $(am__append_6) \
-	$(am__append_7) $(am__append_8) $(am__append_9) \
-	$(am__append_10) $(am__append_11) $(am__append_12) \
-	$(am__append_13) $(am__append_14) $(am__append_15) \
-	$(am__append_16) $(am__append_17) $(am__append_18) \
-	$(am__append_19) $(am__append_20) $(am__append_21) \
-	$(am__append_22) $(am__append_23) $(am__append_24) \
-	$(am__append_25) $(am__append_26) $(am__append_27) \
-	$(am__append_28) $(am__append_29) $(am__append_30) \
-	$(am__append_31)
+	Triplet.h cookie_helper.cc cookie_helper.h $(am__append_1) \
+	$(am__append_2) $(am__append_3) $(am__append_4) \
+	$(am__append_5) $(am__append_6) $(am__append_7) \
+	$(am__append_8) $(am__append_9) $(am__append_10) \
+	$(am__append_11) $(am__append_12) $(am__append_13) \
+	$(am__append_14) $(am__append_15) $(am__append_16) \
+	$(am__append_17) $(am__append_18) $(am__append_19) \
+	$(am__append_20) $(am__append_21) $(am__append_22) \
+	$(am__append_23) $(am__append_24) $(am__append_25) \
+	$(am__append_26) $(am__append_27) $(am__append_28) \
+	$(am__append_29) $(am__append_30) $(am__append_31)
 noinst_LIBRARIES = libaria2c.a
 libaria2c_a_SOURCES = $(SRCS)
 aria2c_LDADD = libaria2c.a @LIBINTL@ @ALLOCA@ @LIBGNUTLS_LIBS@\
@@ -1417,7 +1416,6 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ConsoleStatCalc.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ContentTypeRequestGroupCriteria.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Cookie.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CookieParser.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CookieStorage.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CreateRequestCommand.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DHTAbstractMessage.Po@am__quote@
@@ -1674,6 +1672,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bittorrent_helper.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clock_gettime_mingw.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clock_gettime_osx.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cookie_helper.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/daemon.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/download_helper.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gai_strerror.Po@am__quote@

+ 2 - 1
src/MultiUrlRequestInfo.cc

@@ -59,6 +59,7 @@
 #include "ServerStatMan.h"
 #include "FileAllocationEntry.h"
 #include "CheckIntegrityEntry.h"
+#include "TimeA2.h"
 #ifdef ENABLE_SSL
 # include "SocketCore.h"
 # include "TLSContext.h"
@@ -128,7 +129,7 @@ downloadresultcode::RESULT MultiUrlRequestInfo::execute()
     if(!option_->blank(PREF_LOAD_COOKIES)) {
       File cookieFile(option_->get(PREF_LOAD_COOKIES));
       if(cookieFile.isFile()) {
-        e->getCookieStorage()->load(cookieFile.getPath());
+        e->getCookieStorage()->load(cookieFile.getPath(), Time().getTime());
         logger_->info("Loaded cookies from '%s'.",
                       cookieFile.getPath().c_str());
       } else {

+ 36 - 28
src/NsCookieParser.cc

@@ -40,6 +40,8 @@
 #include "A2STR.h"
 #include "DlAbortEx.h"
 #include "StringFormat.h"
+#include "Cookie.h"
+#include "cookie_helper.h"
 
 namespace aria2 {
 
@@ -47,35 +49,46 @@ NsCookieParser::NsCookieParser() {}
 
 NsCookieParser::~NsCookieParser() {}
 
-static const std::string C_TRUE("TRUE");
+namespace {
+const std::string C_TRUE("TRUE");
+}
 
-static Cookie parseNsCookie(const std::string& nsCookieStr)
+namespace {
+bool parseNsCookie
+(Cookie& cookie, const std::string& nsCookieStr, time_t creationTime)
 {
   std::vector<std::string> vs;
   util::split(nsCookieStr, std::back_inserter(vs), "\t", true);
-  if(vs.size() < 6 ) {
-    return Cookie();
+  if(vs.size() < 6) {
+    return false;
   }
-
-  int64_t expireDate = util::parseLLInt(vs[4]);
-  // TODO assuming time_t is int32_t...
-  if(expireDate > INT32_MAX) {
-    expireDate = INT32_MAX;
+  std::string cookieDomain = cookie::removePrecedingDots(vs[0]);
+  if(vs[5].empty() || cookieDomain.empty() || !cookie::goodPath(vs[2])) {
+    return false;
   }
-
-  Cookie c(vs[5], // name
-           vs.size() >= 7? vs[6]:A2STR::NIL, // value
-           expireDate, // expires
-           vs[2], // path
-           vs[0], // domain
-           vs[3] == C_TRUE ? true : false);
-  if(!util::startsWith(vs[0], A2STR::DOT_C)) {
-    c.markOriginServerOnly();
+  int64_t expiryTime;
+  if(!util::parseLLIntNoThrow(expiryTime, vs[4])) {
+    return false;
   }
-  return c;
+  if(sizeof(time_t) == 4 && expiryTime > INT32_MAX) {
+    expiryTime = INT32_MAX;
+  }
+  cookie.setName(vs[5]);
+  cookie.setValue(vs.size() >= 7? vs[6]:A2STR::NIL);
+  cookie.setExpiryTime(expiryTime == 0?INT32_MAX:expiryTime);
+  // aria2 treats expiryTime == 0 means session cookie.
+  cookie.setPersistent(expiryTime != 0);
+  cookie.setDomain(cookieDomain);
+  cookie.setHostOnly(util::isNumericHost(cookieDomain) || vs[1] != C_TRUE);
+  cookie.setPath(vs[2]);
+  cookie.setSecure(vs[3] == C_TRUE);
+  cookie.setCreationTime(creationTime);
+  return true;
+}
 }
 
-std::vector<Cookie> NsCookieParser::parse(const std::string& filename)
+std::vector<Cookie> NsCookieParser::parse
+(const std::string& filename, time_t creationTime)
 {
   std::ifstream s(filename.c_str(), std::ios::binary);
   if(!s) {
@@ -88,14 +101,9 @@ std::vector<Cookie> NsCookieParser::parse(const std::string& filename)
     if(util::startsWith(line, A2STR::SHARP_C)) {
       continue;
     }
-    try {
-      Cookie c = parseNsCookie(line);
-      if(c.good()) {
-        cookies.push_back(c);
-      }
-    } catch(RecoverableException& e) {
-      // ignore malformed cookie entry
-      // TODO better to log it
+    Cookie c;
+    if(parseNsCookie(c, line, creationTime)) {
+      cookies.push_back(c);
     }
   }
   return cookies;

+ 3 - 3
src/NsCookieParser.h

@@ -40,17 +40,17 @@
 #include <string>
 #include <vector>
 
-#include "Cookie.h"
-
 namespace aria2 {
 
+class Cookie;
+
 class NsCookieParser {
 public:
   NsCookieParser();
 
   ~NsCookieParser();
 
-  std::vector<Cookie> parse(const std::string& filename);
+  std::vector<Cookie> parse(const std::string& filename, time_t creationTime);
 };
 
 } // namespace aria2

+ 38 - 24
src/Sqlite3CookieParser.cc

@@ -40,6 +40,7 @@
 #include "util.h"
 #include "StringFormat.h"
 #include "A2STR.h"
+#include "cookie_helper.h"
 #ifndef HAVE_SQLITE3_OPEN_V2
 # include "File.h"
 #endif // !HAVE_SQLITE3_OPEN_V2
@@ -80,34 +81,40 @@ static std::string toString(const char* str)
 static int cookieRowMapper(void* data, int rowIndex,
                            char** values, char** names)
 {
-  try {
-    std::vector<Cookie>& cookies =
-      *reinterpret_cast<std::vector<Cookie>*>(data);
-    int64_t expireDate = util::parseLLInt(toString(values[3]));
-    // TODO assuming time_t is int32_t...
-    if(expireDate > INT32_MAX) {
-      expireDate = INT32_MAX;
-    }
-    Cookie c(toString(values[4]), // name
-             toString(values[5]), // value
-             expireDate, // expires
-             toString(values[1]), // path
-             toString(values[0]), // domain
-             strcmp(toString(values[2]).c_str(), "1") == 0 ? true:false //secure
-             );
-    if(!util::startsWith(values[0], A2STR::DOT_C)) {
-      c.markOriginServerOnly();
-    }
-    if(c.good()) {
-      cookies.push_back(c);
-    }
-  } catch(RecoverableException& e) {
-    //failed to parse expiry.
+  std::vector<Cookie>& cookies =
+    *reinterpret_cast<std::vector<Cookie>*>(data);
+  std::string cookieDomain = cookie::removePrecedingDots(toString(values[0]));
+  std::string cookieName = toString(values[4]);
+  std::string cookiePath = toString(values[1]);
+  if(cookieName.empty() || cookieDomain.empty() ||
+     !cookie::goodPath(cookiePath)) {
+    return 0;
   }
+  int64_t expiryTime;
+  if(!util::parseLLIntNoThrow(expiryTime, toString(values[3]))) {
+    return 0;
+  }
+  if(sizeof(time_t) == 4 && expiryTime > INT32_MAX) {
+    expiryTime = INT32_MAX;
+  }
+  // TODO get last access, creation date(chrome only)
+  Cookie c(cookieName,
+           toString(values[5]), // value
+           expiryTime,
+           true, // persistent
+           cookieDomain,
+           util::isNumericHost(cookieDomain) || values[0][0] != '.', // hostOnly
+           cookiePath,
+           strcmp(toString(values[2]).c_str(), "1") == 0, //secure
+           false,
+           0 // creation time. Set this later.
+           );
+  cookies.push_back(c);
   return 0;
 }
 
-void Sqlite3CookieParser::parse(std::vector<Cookie>& cookies)
+void Sqlite3CookieParser::parse
+(std::vector<Cookie>& cookies, time_t creationTime)
 {
   if(!db_) {
     throw DL_ABORT_EX(StringFormat("SQLite3 database is not opened.").str());
@@ -116,6 +123,13 @@ void Sqlite3CookieParser::parse(std::vector<Cookie>& cookies)
   char* sqlite3ErrMsg = 0;
   int ret = sqlite3_exec(db_, getQuery().c_str(), cookieRowMapper,
                          &tcookies, &sqlite3ErrMsg);
+  // TODO If last access, creation date are retrieved from database,
+  // following for loop must be removed.
+  for(std::vector<Cookie>::iterator i = tcookies.begin(), eoi = tcookies.end();
+      i != eoi; ++i) {
+    (*i).setCreationTime(creationTime);
+    (*i).setLastAccessTime(creationTime);
+  }
   std::string errMsg;
   if(sqlite3ErrMsg) {
     errMsg = sqlite3ErrMsg;

+ 1 - 1
src/Sqlite3CookieParser.h

@@ -55,7 +55,7 @@ public:
   // Loads cookies from sqlite3 database and stores them in cookies.
   // When loading is successful, cookies stored in cookies initially
   // are removed. Otherwise, the content of cookies is unchanged.
-  void parse(std::vector<Cookie>& cookies);
+  void parse(std::vector<Cookie>& cookies, time_t creationTime);
 protected:
   // Returns SQL select statement to get 1 record of cookie.  The sql
   // must return 6 columns in the following order: host, path,

+ 9 - 1
src/a2functional.h

@@ -421,10 +421,18 @@ class LeastRecentAccess:public std::binary_function<T, T, bool> {
 public:
   bool operator()(const T& lhs, const T& rhs) const
   {
-    return lhs.getLastAccess() < rhs.getLastAccess();
+    return lhs.getLastAccessTime() < rhs.getLastAccessTime();
   }
 };
 
+namespace {
+template<typename T, typename S>
+bool in(T x, S s, S t)
+{
+  return s <= x && x <= t;
+}
+}
+
 } // namespace aria2
 
 #endif // _D_A2_FUNCTIONAL_H_

+ 391 - 0
src/cookie_helper.cc

@@ -0,0 +1,391 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2010 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 "cookie_helper.h"
+
+#include <cstring>
+#include <vector>
+
+#include "util.h"
+#include "array_fun.h"
+#include "Cookie.h"
+#include "a2functional.h"
+
+namespace aria2 {
+
+namespace cookie {
+
+namespace {
+bool isDelimiter(unsigned char c)
+{
+  return c == 0x09U || in(c, 0x20U, 0x2fU) || in(c, 0x3bU, 0x40U) ||
+    in(c, 0x5bU, 0x60U) || in(c, 0x7bU, 0x7eU);
+}
+} // namespace
+
+namespace {
+std::string::const_iterator getNextDigit
+(std::string::const_iterator first, std::string::const_iterator last)
+{
+  for(; first != last && in(static_cast<unsigned char>(*first), 0x30U, 0x39U);
+      ++first);
+  return first;
+}
+}
+
+bool parseDate(time_t& time, const std::string& cookieDate)
+{
+  std::vector<std::string> dateTokens;
+  for(std::string::const_iterator i = cookieDate.begin(),
+        eoi = cookieDate.end(); i != eoi;) {
+    unsigned char c = *i;
+    if(isDelimiter(c)) {
+      ++i;
+      continue;
+    }
+    std::string::const_iterator s = i;
+    for(; s != eoi && !isDelimiter(static_cast<unsigned char>(*s)); ++s);
+    dateTokens.push_back(std::string(i, s));
+    i = s;
+  }
+  int dayOfMonth = 0;
+  bool foundDayOfMonth = false;
+  int month = 0;
+  bool foundMonth = false;
+  int year = 0;
+  bool foundYear = false;
+  int hour = 0;
+  int minute = 0;
+  int second = 0;
+  bool foundTime = false;
+  for(std::vector<std::string>::const_iterator i = dateTokens.begin(),
+        eoi = dateTokens.end(); i != eoi; ++i) {
+    if(!foundTime) {
+      std::string::const_iterator hEnd;
+      std::string::const_iterator mEnd;
+      std::string::const_iterator sEnd;
+      hEnd = getNextDigit((*i).begin(),(*i).end());
+      size_t len = std::distance((*i).begin(), hEnd);
+      if(len == 0 || 2 < len || hEnd == (*i).end() || *hEnd != ':') {
+        goto NOT_TIME;
+      }
+      mEnd = getNextDigit(hEnd+1, (*i).end());
+      len = std::distance(hEnd+1, mEnd);
+      if(len == 0 || 2 < len || mEnd == (*i).end() || *mEnd != ':') {
+        goto NOT_TIME;
+      }
+      sEnd = getNextDigit(mEnd+1, (*i).end());
+      len = std::distance(mEnd+1, sEnd);
+      if(len == 0 || 2 < len) {
+        goto NOT_TIME;
+      }
+      foundTime = true;
+      hour = util::parseInt(std::string((*i).begin(), hEnd));
+      minute = util::parseInt(std::string(hEnd+1, mEnd));
+      second = util::parseInt(std::string(mEnd+1, sEnd));
+      continue;
+    NOT_TIME:
+      ;
+    }
+    if(!foundDayOfMonth) {
+      std::string::const_iterator j = getNextDigit((*i).begin(), (*i).end());
+      size_t len = std::distance((*i).begin(), j);
+      if(1 <= len && len <= 2) {
+        foundDayOfMonth = true;
+        dayOfMonth = util::parseInt(std::string((*i).begin(), j));
+        continue;
+      }
+    }
+    if(!foundMonth) {
+      static std::string MONTH[] = {
+        "jan", "feb", "mar", "apr",
+        "may", "jun", "jul", "aug",
+        "sep", "oct", "nov", "dec" };
+      if((*i).size() >= 3) {
+        std::string head = util::toLower((*i).substr(0, 3));
+        std::string* mptr = std::find(vbegin(MONTH), vend(MONTH), head);
+        if(mptr != vend(MONTH)) {
+          foundMonth = true;
+          month = std::distance(vbegin(MONTH), mptr)+1;
+          continue;
+        }
+      }
+    }
+    if(!foundYear) {
+      std::string::const_iterator j = getNextDigit((*i).begin(), (*i).end());
+      size_t len = std::distance((*i).begin(), j);
+      if(1 <= len && len <= 4) {
+        foundYear = true;
+        year = util::parseInt(std::string((*i).begin(), j));
+        continue;
+      }
+    }
+  }
+  if(in(year, 70, 99)) {
+    year += 1900;
+  } else if(in(year, 0, 69)) {
+    year += 2000;
+  }
+  if(!foundDayOfMonth || !foundMonth || !foundYear || !foundTime ||
+     !in(dayOfMonth, 1, 31) || year < 1601 || hour > 23 ||
+     minute > 59 || second > 59) {
+    return false;
+  }
+  if((month == 4 || month == 6 || month == 9 || month == 11) &&
+     dayOfMonth > 30) {
+    return false;
+  }
+  if(month == 2) {
+    if((year%4 == 0 && year%100 != 0) || year%400 == 0) {
+      if(dayOfMonth > 29) {
+        return false;
+      }
+    } else if(dayOfMonth > 28) {
+      return false;
+    }
+  }
+        
+  tm timespec;
+  memset(&timespec, 0, sizeof(timespec));
+  timespec.tm_sec = second;
+  timespec.tm_min = minute;
+  timespec.tm_hour = hour;
+  timespec.tm_mday = dayOfMonth;
+  timespec.tm_mon = month-1;
+  timespec.tm_year = year-1900;
+  time = timegm(&timespec);
+
+  return time != -1;
+}
+
+bool parse
+(Cookie& cookie,
+ const std::string& cookieStr,
+ const std::string& requestHost,
+ const std::string& defaultPath,
+ time_t creationTime)
+{
+  std::string::const_iterator nvEnd = cookieStr.begin();
+  std::string::const_iterator end = cookieStr.end();
+  for(; nvEnd != end && *nvEnd != ';'; ++nvEnd);
+  std::string::const_iterator eq = cookieStr.begin();
+  for(; eq != nvEnd && *eq != '='; ++eq);
+  if(eq == nvEnd) {
+    return false;
+  }
+  std::string cookieName = util::trim(std::string(cookieStr.begin(), eq));
+  if(cookieName.empty()) {
+    return false;
+  }
+  std::string cookieValue = util::trim(std::string(eq+1, nvEnd));
+  time_t expiryTime = 0;
+  bool foundExpires = false;
+  bool persistent = false;
+  time_t maxAge = 0;
+  bool foundMaxAge = false;
+  std::string cookieDomain;
+  bool hostOnly = false;
+  std::string cookiePath;
+  bool secure = false;
+  bool httpOnly = false;
+
+  for(std::string::const_iterator i = nvEnd; i != end;) {
+    std::string::const_iterator j = i;
+    for(; j != end && *j != ';'; ++j);
+    std::string::const_iterator eq = i;
+    for(; eq != j && *eq != '='; ++eq);
+    std::string attrName = util::toLower(util::trim(std::string(i, eq)));
+    std::string attrValue;
+    if(eq != j) {
+      attrValue = util::trim(std::string(eq+1, j));
+    }
+    i = j;
+    if(j != end) {
+      ++i;
+    }
+    if(attrName == "expires") {
+      if(parseDate(expiryTime, attrValue)) {
+        foundExpires = true;
+      } else {
+        return false;
+      }
+    } else if(attrName == "max-age") {
+      if(attrValue.empty() ||
+         (!in(static_cast<unsigned char>(attrValue[0]), 0x30u, 0x39u) &&
+          attrValue[0] != '-')) {
+        return false;
+      }
+      for(std::string::const_iterator s = attrValue.begin()+1,
+            eos = attrValue.end(); s != eos; ++s) {
+        if(!in(static_cast<unsigned char>(*s), 0x30u, 0x39u)) {
+          return false;
+        }
+      }
+      int64_t delta;
+      if(util::parseLLIntNoThrow(delta, attrValue)) {
+        foundMaxAge = true;
+        if(delta <= 0) {
+          maxAge = 0;
+        } else {
+          int64_t n = creationTime;
+          n += delta;
+          if(n < 0 || (sizeof(time_t) < 8 && n > INT32_MAX)) {
+            maxAge = INT32_MAX;
+          } else {
+            maxAge = n;
+          }
+        }
+      } else {
+        return false;
+      }
+    } else if(attrName == "domain") {
+      if(attrValue.empty()) {
+        return false;
+      }
+      std::string::const_iterator noDot = attrValue.begin();
+      std::string::const_iterator end = attrValue.end();
+      for(; noDot != end && *noDot == '.'; ++noDot);
+      if(noDot == end) {
+        return false;
+      }
+      cookieDomain = std::string(noDot, end);
+    } else if(attrName == "path") {
+      if(goodPath(attrValue)) {
+        cookiePath = attrValue;
+      } else {
+        cookiePath = defaultPath;
+      }
+    } else if(attrName == "secure") {
+      secure = true;
+    } else if(attrName == "httponly") {
+      httpOnly = true;
+    }
+  }
+
+  if(foundMaxAge) {
+    expiryTime = maxAge;
+    persistent = true;
+  } else if(foundExpires) {
+    persistent = true;
+  } else {
+    expiryTime = INT32_MAX;
+    persistent = false;
+  }
+
+  std::string canonicalizedHost = canonicalizeHost(requestHost);
+  if(cookieDomain.empty()) {
+    hostOnly = true;
+    cookieDomain = canonicalizedHost;
+  } else if(domainMatch(canonicalizedHost, cookieDomain)) {
+    hostOnly = util::isNumericHost(canonicalizedHost);
+  } else {
+    return false;
+  }
+
+  if(cookiePath.empty()) {
+    cookiePath = defaultPath;
+  }
+  
+  cookie.setName(cookieName);
+  cookie.setValue(cookieValue);
+  cookie.setExpiryTime(expiryTime);
+  cookie.setPersistent(persistent);
+  cookie.setDomain(cookieDomain);
+  cookie.setHostOnly(hostOnly);
+  cookie.setPath(cookiePath);
+  cookie.setSecure(secure);
+  cookie.setHttpOnly(httpOnly);
+  cookie.setCreationTime(creationTime);
+  cookie.setLastAccessTime(creationTime);
+
+  return true;
+}
+
+std::string removePrecedingDots(const std::string& host)
+{
+  std::string::const_iterator noDot = host.begin();
+  std::string::const_iterator end = host.end();
+  for(; noDot != end && *noDot == '.'; ++noDot);
+  return std::string(noDot, end);
+}
+
+bool goodPath(const std::string& cookiePath)
+{
+  return !cookiePath.empty() && cookiePath[0] == '/';
+}
+
+std::string canonicalizeHost(const std::string& host)
+{
+  std::string ch = util::toLower(host);
+  return ch;
+}
+
+bool domainMatch(const std::string& requestHost, const std::string& domain)
+{
+  return requestHost == domain ||
+    (util::endsWith(requestHost, domain) &&
+     requestHost[requestHost.size()-domain.size()-1] == '.' &&
+     !util::isNumericHost(requestHost));
+}
+
+bool pathMatch(const std::string& requestPath, const std::string& path)
+{
+  return requestPath == path ||
+    (util::startsWith(requestPath, path) &&
+     (path[path.size()-1] == '/' || requestPath[path.size()] == '/'));
+}
+
+std::string reverseDomainLevel(const std::string& domain)
+{
+  std::string r;
+  for(std::string::const_iterator i = domain.begin(), eoi = domain.end();
+      i != eoi;) {
+    std::string::const_iterator j = i;
+    for(; j != eoi && *j != '.'; ++j);
+    r.insert(r.begin(), '.');
+    r.insert(r.begin(), i, j);
+    i = j;
+    if(j != eoi) {
+      ++i;
+    }
+  }
+  if(!r.empty()) {
+    r.erase(r.size()-1, 1);
+  }
+  return r;
+}
+
+} // namespace cookie
+
+} // namespace aria2

+ 31 - 21
src/CookieParser.h → src/cookie_helper.h

@@ -2,7 +2,7 @@
 /*
  * aria2 - The high speed download utility
  *
- * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ * Copyright (C) 2010 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
@@ -32,34 +32,44 @@
  * files in the program, then also delete it here.
  */
 /* copyright --> */
-#ifndef _D_COOKIE_PARSER_H_
-#define _D_COOKIE_PARSER_H_
+#ifndef D_COOKIE_HELPER_H
+#define D_COOKIE_HELPER_H
 
 #include "common.h"
 
 #include <string>
 
-#include "SharedHandle.h"
-#include "Cookie.h"
+#include "a2time.h"
 
 namespace aria2 {
 
-class CookieParser {
-public:
-  Cookie parse(const std::string& cookieStr, const std::string& defaultDomain,
-               const std::string& defaultPath) const;
-private:
-  static const std::string C_SECURE;
-  
-  static const std::string C_DOMAIN;
-  
-  static const std::string C_PATH;
-  
-  static const std::string C_EXPIRES;
-};
-
-typedef SharedHandle<CookieParser> CookieParserHandle;
+class Cookie;
+
+namespace cookie {
+
+bool parseDate(time_t& time, const std::string& cookieDate);
+
+bool parse
+(Cookie& cookie,
+ const std::string& cookieStr,
+ const std::string& requestHost,
+ const std::string& defaultPath,
+ time_t creationTime);
+
+std::string removePrecedingDots(const std::string& host);
+
+bool goodPath(const std::string& cookiePath);
+
+std::string canonicalizeHost(const std::string& host);
+
+bool domainMatch(const std::string& requestHost, const std::string& domain);
+
+bool pathMatch(const std::string& requestPath, const std::string& path);
+
+std::string reverseDomainLevel(const std::string& domain);
+
+} // namespace cookie
 
 } // namespace aria2
 
-#endif // _D_COOKIE_PARSER_H_
+#endif // D_COOKIE_HELPER_H

+ 43 - 33
src/util.cc

@@ -74,7 +74,6 @@
 #include "StringFormat.h"
 #include "A2STR.h"
 #include "array_fun.h"
-#include "a2functional.h"
 #include "bitfield.h"
 #include "DownloadHandlerConstants.h"
 #include "RequestGroup.h"
@@ -291,17 +290,10 @@ bool inRFC2616HttpToken(const char c)
     std::find(vbegin(chars), vend(chars), c) != vend(chars);
 }
 
-namespace {
-bool in(unsigned char ch, unsigned char s, unsigned char t)
-{
-  return s <= ch && ch <= t;
-}
-}
-
 namespace {
 bool isUtf8Tail(unsigned char ch)
 {
-  return in(ch, 0x80, 0xbf);
+  return in(ch, 0x80u, 0xbfu);
 }
 }
 
@@ -311,49 +303,49 @@ bool isUtf8(const std::string& str)
       ++s) {
     unsigned char firstChar = *s;
     // See ABNF in http://tools.ietf.org/search/rfc3629#section-4
-    if(in(firstChar, 0x20, 0x7e) ||
-       firstChar == 0x09 || firstChar == 0x0a ||firstChar == 0x0d) {
+    if(in(firstChar, 0x20u, 0x7eu) ||
+       firstChar == 0x09u || firstChar == 0x0au ||firstChar == 0x0du) {
       // UTF8-1 (without ctrl chars)
-    } else if(in(firstChar, 0xc2, 0xdf)) {
+    } else if(in(firstChar, 0xc2u, 0xdfu)) {
        // UTF8-2
       if(++s == eos || !isUtf8Tail(*s)) {
         return false;
       }
-    } else if(0xe0 == firstChar) {
+    } else if(0xe0u == firstChar) {
       // UTF8-3
-      if(++s == eos || !in(*s, 0xa0, 0xbf) ||
+      if(++s == eos || !in(static_cast<unsigned char>(*s), 0xa0u, 0xbfu) ||
          ++s == eos || !isUtf8Tail(*s)) {
         return false;
       }
-    } else if(in(firstChar, 0xe1, 0xec) || in(firstChar, 0xee, 0xef)) {
+    } else if(in(firstChar, 0xe1u, 0xecu) || in(firstChar, 0xeeu, 0xefu)) {
       // UTF8-3
       if(++s == eos || !isUtf8Tail(*s) ||
          ++s == eos || !isUtf8Tail(*s)) {
         return false;
       }
-    } else if(0xed == firstChar) {
+    } else if(0xedu == firstChar) {
       // UTF8-3
-      if(++s == eos || !in(*s, 0x80, 0x9f) ||
+      if(++s == eos || !in(static_cast<unsigned char>(*s), 0x80u, 0x9fu) ||
          ++s == eos || !isUtf8Tail(*s)) {
         return false;
       } 
-    } else if(0xf0 == firstChar) {
+    } else if(0xf0u == firstChar) {
       // UTF8-4
-      if(++s == eos || !in(*s, 0x90, 0xbf) ||
+      if(++s == eos || !in(static_cast<unsigned char>(*s), 0x90u, 0xbfu) ||
          ++s == eos || !isUtf8Tail(*s) ||
          ++s == eos || !isUtf8Tail(*s)) {
         return false;
       }
-    } else if(in(firstChar, 0xf1, 0xf3)) {
+    } else if(in(firstChar, 0xf1u, 0xf3u)) {
       // UTF8-4
       if(++s == eos || !isUtf8Tail(*s) ||
          ++s == eos || !isUtf8Tail(*s) ||
          ++s == eos || !isUtf8Tail(*s)) {
         return false;
       }
-    } else if(0xf4 == firstChar) {
+    } else if(0xf4u == firstChar) {
       // UTF8-4
-      if(++s == eos || !in(*s, 0x80, 0x8f) ||
+      if(++s == eos || !in(static_cast<unsigned char>(*s), 0x80u, 0x8fu) ||
          ++s == eos || !isUtf8Tail(*s) ||
          ++s == eos || !isUtf8Tail(*s)) {
         return false;
@@ -427,7 +419,7 @@ std::string toHex(const unsigned char* src, size_t len) {
   const unsigned char* last = src+len;
   for(const unsigned char* i = src; i != last; ++i) {
     *o = (*i >> 4);
-    *(o+1) = (*i)&0x0f;
+    *(o+1) = (*i)&0x0fu;
     for(int j = 0; j < 2; ++j) {
       if(*o < 10) {
         *o += '0';
@@ -598,6 +590,24 @@ int64_t parseLLInt(const std::string& s, int32_t base)
   return v;
 }
 
+bool parseLLIntNoThrow(int64_t& result, const std::string& s, int base)
+{
+  std::string trimed = trim(s);
+  if(trimed.empty()) {
+    return false;
+  }
+  char* stop;
+  errno = 0;
+  int64_t v = strtoll(trimed.c_str(), &stop, base);
+  if(*stop != '\0') {
+    return false;
+  } else if(((v == INT64_MIN) || (v == INT64_MAX)) && (errno == ERANGE)) {
+    return false;
+  }
+  result = v;
+  return true;
+}
+
 uint64_t parseULLInt(const std::string& s, int base)
 {
   std::string trimed = trim(s);
@@ -740,14 +750,14 @@ std::string iso8859ToUtf8(const std::string& src)
   for(std::string::const_iterator itr = src.begin(), eoi = src.end();
       itr != eoi; ++itr) {
     unsigned char c = *itr;
-    if(0xa0 <= c) {
-      if(c <= 0xbf) {
-        dest += 0xc2;
+    if(0xa0u <= c) {
+      if(c <= 0xbfu) {
+        dest += 0xc2u;
       } else {
-        dest += 0xc3;
+        dest += 0xc3u;
       }
-      dest += c&(~0x40);
-    } else if(0x80 <= c && c <= 0x9f) {
+      dest += c&(~0x40u);
+    } else if(0x80u <= c && c <= 0x9fu) {
       return A2STR::NIL;
     } else {
       dest += c;
@@ -1000,10 +1010,10 @@ std::string abbrevSize(int64_t size)
   char units[] = { 'K', 'M' };
   size_t numUnit = sizeof(units)/sizeof(char);
   size_t i = 0;
-  int r = size&0x3ff;
+  int r = size&0x3ffu;
   size >>= 10;
   for(; i < numUnit-1 && size >= 1024; ++i) {
-    r = size&0x3ff;
+    r = size&0x3ffu;
     size >>= 10;
   }
   std::string result = itos(size, true);
@@ -1332,7 +1342,7 @@ bool detectDirTraversal(const std::string& s)
 {
   for(std::string::const_iterator i = s.begin(), eoi = s.end(); i != eoi; ++i) {
     unsigned char c = *i;
-    if(in(c, 0x00, 0x1f) || c == 0x7f) {
+    if(in(c, 0x00u, 0x1fu) || c == 0x7fu) {
       return true;
     }
   }
@@ -1367,7 +1377,7 @@ std::string escapePath(const std::string& s)
   std::string d;
   for(std::string::const_iterator i = s.begin(), eoi = s.end(); i != eoi; ++i) {
     unsigned char c = *i;
-    if(in(c, 0x00, 0x1f) || c == 0x7f
+    if(in(c, 0x00u, 0x1fu) || c == 0x7fu
 #ifdef __MINGW32__
        || std::find(vbegin(WIN_INVALID_PATH_CHARS),
                     vend(WIN_INVALID_PATH_CHARS),

+ 2 - 0
src/util.h

@@ -181,6 +181,8 @@ bool parseUIntNoThrow(uint32_t& result, const std::string& s, int base = 10);
 
 int64_t parseLLInt(const std::string& s, int32_t base = 10);
 
+bool parseLLIntNoThrow(int64_t& result, const std::string& s, int base = 10);
+
 uint64_t parseULLInt(const std::string& s, int base = 10);
 
 IntSequence parseIntRange(const std::string& src);

+ 0 - 89
test/CookieBoxFactoryTest.cc

@@ -1,89 +0,0 @@
-#include "CookieBoxFactory.h"
-#include "CookieBox.h"
-#include <cppunit/extensions/HelperMacros.h>
-
-namespace aria2 {
-
-class CookieBoxFactoryTest:public CppUnit::TestFixture {
-
-  CPPUNIT_TEST_SUITE(CookieBoxFactoryTest);
-  CPPUNIT_TEST(testLoadDefaultCookie);
-  CPPUNIT_TEST(testLoadDefaultCookie_sqlite3);
-  CPPUNIT_TEST(testCreateNewInstance);
-  CPPUNIT_TEST_SUITE_END();
-private:
-
-public:
-  void setUp() {
-  }
-
-  void testLoadDefaultCookie();
-  void testLoadDefaultCookie_sqlite3();
-  void testCreateNewInstance();
-};
-
-
-CPPUNIT_TEST_SUITE_REGISTRATION( CookieBoxFactoryTest );
-
-void CookieBoxFactoryTest::testLoadDefaultCookie()
-{
-  CookieBoxFactory factory;
-
-  factory.loadDefaultCookie("nscookietest.txt");
-
-  Cookies cookies = factory.getDefaultCookies();
-
-  CPPUNIT_ASSERT_EQUAL((int32_t)4, (int32_t)cookies.size());
-
-  Cookie c = cookies[0];
-  CPPUNIT_ASSERT_EQUAL(std::string("JSESSIONID"), c.name);
-  CPPUNIT_ASSERT_EQUAL(std::string("123456789"), c.value);
-  CPPUNIT_ASSERT_EQUAL((time_t)1181473200, c.expires);
-  CPPUNIT_ASSERT_EQUAL(std::string("/"), c.path);
-  CPPUNIT_ASSERT_EQUAL(std::string("localhost"), c.domain);
-
-  c = cookies[1];
-  CPPUNIT_ASSERT_EQUAL(std::string("user"), c.name);
-  CPPUNIT_ASSERT_EQUAL(std::string("me"), c.value);
-  CPPUNIT_ASSERT_EQUAL((time_t)1181473200, c.expires);
-  CPPUNIT_ASSERT_EQUAL(std::string("/"), c.path);
-  CPPUNIT_ASSERT_EQUAL(std::string("localhost"), c.domain);
-
-  c = cookies[2];
-  CPPUNIT_ASSERT_EQUAL(std::string("passwd"), c.name);
-  CPPUNIT_ASSERT_EQUAL(std::string("secret"), c.value);
-  CPPUNIT_ASSERT_EQUAL((time_t)2147483647, c.expires);
-  CPPUNIT_ASSERT_EQUAL(std::string("/cgi-bin"), c.path);
-  CPPUNIT_ASSERT_EQUAL(std::string("localhost"), c.domain);
-
-  c = cookies[3];
-  CPPUNIT_ASSERT_EQUAL(std::string("novalue"), c.name);
-  CPPUNIT_ASSERT_EQUAL(std::string(""), c.value);
-  CPPUNIT_ASSERT_EQUAL((time_t)2147483647, c.expires);
-  CPPUNIT_ASSERT_EQUAL(std::string("/"), c.path);
-  CPPUNIT_ASSERT_EQUAL(std::string("localhost"), c.domain);
-}
-
-void CookieBoxFactoryTest::testLoadDefaultCookie_sqlite3()
-{
-  CookieBoxFactory factory;
-  factory.loadDefaultCookie("cookies.sqlite");
-  const std::deque<Cookie>& cookies = factory.getDefaultCookies();
-#ifdef HAVE_SQLITE3
-  CPPUNIT_ASSERT_EQUAL((size_t)3, cookies.size());
-#else // !HAVE_SQLITE3
-  CPPUNIT_ASSERT(cookies.empty());
-#endif // !HAVE_SQLITE3
-}
-
-void CookieBoxFactoryTest::testCreateNewInstance()
-{
-  CookieBoxFactory factory;
-  factory.loadDefaultCookie("nscookietest.txt");
-  SharedHandle<CookieBox> box = factory.createNewInstance();
-  std::deque<Cookie> cookies = box->criteriaFind("localhost", "/", 0, true);
-
-  CPPUNIT_ASSERT_EQUAL((int32_t)3, (int32_t)cookies.size());
-}
-
-} // namespace aria2

+ 229 - 0
test/CookieHelperTest.cc

@@ -0,0 +1,229 @@
+#include "cookie_helper.h"
+
+#include <cppunit/extensions/HelperMacros.h>
+
+#include "Exception.h"
+#include "util.h"
+#include "Cookie.h"
+
+namespace aria2 {
+
+class CookieHelperTest:public CppUnit::TestFixture {
+
+  CPPUNIT_TEST_SUITE(CookieHelperTest);
+  CPPUNIT_TEST(testParseDate);
+  CPPUNIT_TEST(testDomainMatch);
+  CPPUNIT_TEST(testPathMatch);
+  CPPUNIT_TEST(testParse);
+  CPPUNIT_TEST(testReverseDomainLevel);
+  CPPUNIT_TEST_SUITE_END();
+public:
+  void testParseDate();
+  void testDomainMatch();
+  void testPathMatch();
+  void testParse();
+  void testReverseDomainLevel();
+};
+
+
+CPPUNIT_TEST_SUITE_REGISTRATION(CookieHelperTest);
+
+void CookieHelperTest::testParseDate()
+{
+  // RFC1123
+  time_t time = 0;
+  CPPUNIT_ASSERT(cookie::parseDate(time, "Sat, 06 Sep 2008 15:26:33 GMT"));
+  CPPUNIT_ASSERT_EQUAL((time_t)1220714793, time);
+  // RFC850
+  CPPUNIT_ASSERT(cookie::parseDate(time, "Saturday, 06-Sep-08 15:26:33 GMT"));
+  CPPUNIT_ASSERT_EQUAL((time_t)1220714793, time);
+  // ANSI C's asctime()
+  CPPUNIT_ASSERT(cookie::parseDate(time, "Sun Sep  6 15:26:33 2008"));
+  CPPUNIT_ASSERT_EQUAL((time_t)1220714793, time);
+
+  CPPUNIT_ASSERT(cookie::parseDate(time, "Thu Jan 1 0:0:0 1970"));
+  CPPUNIT_ASSERT_EQUAL((time_t)0, time);
+
+  CPPUNIT_ASSERT(!cookie::parseDate(time, "Thu Jan 1 1970 0:")); 
+  CPPUNIT_ASSERT(!cookie::parseDate(time, "Thu Jan 1 1970 0:0")); 
+  CPPUNIT_ASSERT(!cookie::parseDate(time, "Thu Jan 1 1970 0:0:")); 
+
+  // Leap year
+  CPPUNIT_ASSERT(cookie::parseDate(time, "Tue, 29 Feb 2000 00:00:00 GMT"));
+  CPPUNIT_ASSERT(!cookie::parseDate(time, "Thu, 29 Feb 2001 00:00:00 GMT"));
+}
+
+void CookieHelperTest::testDomainMatch()
+{
+  CPPUNIT_ASSERT(cookie::domainMatch("localhost", "localhost"));
+  CPPUNIT_ASSERT(cookie::domainMatch("192.168.0.1", "192.168.0.1"));
+  CPPUNIT_ASSERT(cookie::domainMatch("www.example.org", "example.org"));
+  CPPUNIT_ASSERT(!cookie::domainMatch("192.168.0.1", "0.1"));
+  CPPUNIT_ASSERT(!cookie::domainMatch("example.org", "example.com"));
+  CPPUNIT_ASSERT(!cookie::domainMatch("example.org", "www.example.org"));  
+}
+
+void CookieHelperTest::testPathMatch()
+{
+  CPPUNIT_ASSERT(cookie::pathMatch("/", "/"));
+  CPPUNIT_ASSERT(cookie::pathMatch("/foo/", "/foo"));
+  CPPUNIT_ASSERT(!cookie::pathMatch("/bar/", "/foo"));
+  CPPUNIT_ASSERT(!cookie::pathMatch("/foo", "/bar/foo"));
+  CPPUNIT_ASSERT(cookie::pathMatch("/foo/bar", "/foo/"));
+}
+
+void CookieHelperTest::testParse()
+{
+  time_t creationDate = 141;
+  {
+    std::string str = "ID=123456789; expires=Sun, 10-Jun-2007 11:00:00 GMT;"
+      "path=/foo; domain=localhost; secure;httpOnly   ";
+    Cookie c;
+    CPPUNIT_ASSERT(cookie::parse(c, str, "localhost", "/", creationDate));
+    CPPUNIT_ASSERT_EQUAL(std::string("ID"), c.getName());
+    CPPUNIT_ASSERT_EQUAL(std::string("123456789"), c.getValue());
+    CPPUNIT_ASSERT_EQUAL((time_t)1181473200, c.getExpiryTime());
+    CPPUNIT_ASSERT(c.getPersistent());
+    CPPUNIT_ASSERT_EQUAL(std::string("localhost"), c.getDomain());
+    CPPUNIT_ASSERT(!c.getHostOnly());
+    CPPUNIT_ASSERT_EQUAL(std::string("/foo"), c.getPath());
+    CPPUNIT_ASSERT(c.getSecure());
+    CPPUNIT_ASSERT(c.getHttpOnly());
+    CPPUNIT_ASSERT_EQUAL((time_t)141, c.getCreationTime());
+    CPPUNIT_ASSERT_EQUAL((time_t)141, c.getLastAccessTime());
+  }
+  {
+    std::string str = "id=; Max-Age=0;";
+    Cookie c;
+    CPPUNIT_ASSERT(cookie::parse(c, str, "localhost", "/", creationDate));
+    CPPUNIT_ASSERT_EQUAL(std::string("id"), c.getName());
+    CPPUNIT_ASSERT_EQUAL((time_t)0, c.getExpiryTime());
+    CPPUNIT_ASSERT(c.getPersistent());
+    CPPUNIT_ASSERT_EQUAL(std::string("localhost"), c.getDomain());
+    CPPUNIT_ASSERT(c.getHostOnly());
+    CPPUNIT_ASSERT_EQUAL(std::string("/"), c.getPath());
+    CPPUNIT_ASSERT(!c.getSecure());
+    CPPUNIT_ASSERT(!c.getHttpOnly());
+  }
+  {
+    std::string str = "id=; Max-Age=-100;";
+    Cookie c;
+    CPPUNIT_ASSERT(cookie::parse(c, str, "localhost", "/", creationDate));
+    CPPUNIT_ASSERT_EQUAL((time_t)0, c.getExpiryTime());
+    CPPUNIT_ASSERT(c.getPersistent());
+  }
+  {
+    std::string str = "id=; Max-Age=100;";
+    Cookie c;
+    CPPUNIT_ASSERT(cookie::parse(c, str, "localhost", "/", creationDate));
+    CPPUNIT_ASSERT_EQUAL((time_t)creationDate+100, c.getExpiryTime());
+    CPPUNIT_ASSERT(c.getPersistent());
+  }
+  {
+    std::string str = "id=; Max-Age=9223372036854775807;";
+    Cookie c;
+    CPPUNIT_ASSERT(cookie::parse(c, str, "localhost", "/", creationDate));
+    CPPUNIT_ASSERT_EQUAL((time_t)INT32_MAX, c.getExpiryTime());
+    CPPUNIT_ASSERT(c.getPersistent());
+  }
+  {
+    std::string str = "id=; Max-Age=X;";
+    Cookie c;
+    CPPUNIT_ASSERT(!cookie::parse(c, str, "localhost", "/", creationDate));
+  }
+  {
+    std::string str = "id=; Max-Age=100garbage;";
+    Cookie c;
+    CPPUNIT_ASSERT(!cookie::parse(c, str, "localhost", "/", creationDate));
+  }
+  {
+    std::string str = "id=; Max-Age=100;expires=Sun, 10-Jun-2007 11:00:00 GMT;";
+    Cookie c;
+    CPPUNIT_ASSERT(cookie::parse(c, str, "localhost", "/", creationDate));
+    CPPUNIT_ASSERT_EQUAL((time_t)creationDate+100, c.getExpiryTime());
+    CPPUNIT_ASSERT(c.getPersistent());    
+  }
+  {
+    // Cookie data cannot be parsed.
+    std::string str = "id=; expires=2007-10-01 11:00:00 GMT;";
+    Cookie c;
+    CPPUNIT_ASSERT(!cookie::parse(c, str, "localhost", "/", creationDate));
+  }
+  {
+    std::string str = "id=;";
+    Cookie c;
+    CPPUNIT_ASSERT(cookie::parse(c, str, "localhost", "/", creationDate));
+    CPPUNIT_ASSERT(!c.getPersistent());
+  }
+  {
+    std::string str = "id=; path=abc";
+    Cookie c;
+    CPPUNIT_ASSERT(cookie::parse(c, str, "localhost", "/", creationDate));
+    CPPUNIT_ASSERT_EQUAL(std::string("/"), c.getPath());
+  }
+  {
+    std::string str = "id=; domain=.example.org";
+    Cookie c;
+    CPPUNIT_ASSERT(cookie::parse(c, str, "www.example.org", "/",creationDate));
+  }
+  {
+    // Fails because request host does not domain-match with cookie
+    // domain.
+    std::string str = "id=; domain=www.example.org";
+    Cookie c;
+    CPPUNIT_ASSERT(!cookie::parse(c, str, "example.org", "/", creationDate));
+  }
+  {
+    std::string str = "id=; domain=.";
+    Cookie c;
+    CPPUNIT_ASSERT(!cookie::parse(c, str, "localhost", "/",creationDate));
+  }
+  {
+    std::string str = "";
+    Cookie c;
+    CPPUNIT_ASSERT(!cookie::parse(c, str, "localhost", "/",creationDate));
+  }
+  {
+    std::string str = "=";
+    Cookie c;
+    CPPUNIT_ASSERT(!cookie::parse(c, str, "localhost", "/",creationDate));
+  }
+  {
+    // Use domain last time seen.
+    std::string str = "id=;domain=a.example.org;domain=.example.org";
+    Cookie c;
+    CPPUNIT_ASSERT(cookie::parse(c, str, "b.example.org", "/",creationDate));
+    CPPUNIT_ASSERT_EQUAL(std::string("example.org"), c.getDomain());
+  }
+  {
+    // numeric host
+    std::string str = "id=;";
+    Cookie c;
+    CPPUNIT_ASSERT(cookie::parse(c, str, "192.168.0.1", "/",creationDate));
+    CPPUNIT_ASSERT_EQUAL(std::string("192.168.0.1"), c.getDomain());
+    CPPUNIT_ASSERT(c.getHostOnly());
+  }
+  {
+    // numeric host
+    std::string str = "id=; domain=192.168.0.1";
+    Cookie c;
+    CPPUNIT_ASSERT(cookie::parse(c, str, "192.168.0.1", "/",creationDate));
+    CPPUNIT_ASSERT_EQUAL(std::string("192.168.0.1"), c.getDomain());
+    CPPUNIT_ASSERT(c.getHostOnly());
+  }
+}
+
+void CookieHelperTest::testReverseDomainLevel()
+{
+  CPPUNIT_ASSERT_EQUAL(std::string("net.sourceforge.aria2"),
+                       cookie::reverseDomainLevel("aria2.sourceforge.net"));
+  CPPUNIT_ASSERT_EQUAL(std::string("localhost"),
+                       cookie::reverseDomainLevel("localhost"));
+  // Behavior check
+  CPPUNIT_ASSERT_EQUAL(std::string(""), cookie::reverseDomainLevel(""));
+  CPPUNIT_ASSERT_EQUAL(std::string(""), cookie::reverseDomainLevel("."));
+  CPPUNIT_ASSERT_EQUAL(std::string("foo."), cookie::reverseDomainLevel(".foo"));
+  CPPUNIT_ASSERT_EQUAL(std::string("foo"), cookie::reverseDomainLevel("foo."));
+}
+
+} // namespace aria2

+ 0 - 70
test/CookieParserTest.cc

@@ -1,70 +0,0 @@
-#include "CookieParser.h"
-
-#include <cppunit/extensions/HelperMacros.h>
-
-namespace aria2 {
-
-class CookieParserTest:public CppUnit::TestFixture {
-  CPPUNIT_TEST_SUITE(CookieParserTest);
-  CPPUNIT_TEST(testParse);
-  CPPUNIT_TEST_SUITE_END();
-public:
-  void testParse();
-};
-
-
-CPPUNIT_TEST_SUITE_REGISTRATION( CookieParserTest );
-
-void CookieParserTest::testParse()
-{
-  std::string str = "JSESSIONID=123456789; expires=Sun, 10-Jun-2007 11:00:00 GMT; path=/; domain=localhost; secure";
-  Cookie c = CookieParser().parse(str, "", "");
-  CPPUNIT_ASSERT(c.good());
-  CPPUNIT_ASSERT_EQUAL(std::string("JSESSIONID"), c.getName());
-  CPPUNIT_ASSERT_EQUAL(std::string("123456789"), c.getValue());
-  CPPUNIT_ASSERT_EQUAL((time_t)1181473200, c.getExpiry());  
-  CPPUNIT_ASSERT_EQUAL(std::string("/"), c.getPath());
-  CPPUNIT_ASSERT_EQUAL(std::string(".localhost.local"), c.getDomain());
-  CPPUNIT_ASSERT_EQUAL(true, c.isSecureCookie());
-  CPPUNIT_ASSERT_EQUAL(false, c.isSessionCookie());
-
-  std::string str2 = "JSESSIONID=123456789";
-  c = CookieParser().parse(str2, "default.domain", "/default/path");
-  CPPUNIT_ASSERT(c.good());
-  CPPUNIT_ASSERT_EQUAL(std::string("JSESSIONID"), c.getName());
-  CPPUNIT_ASSERT_EQUAL(std::string("123456789"), c.getValue());
-  CPPUNIT_ASSERT_EQUAL((time_t)0, c.getExpiry());
-  CPPUNIT_ASSERT_EQUAL(std::string("default.domain"), c.getDomain());
-  CPPUNIT_ASSERT_EQUAL(std::string("/default/path"), c.getPath());
-  CPPUNIT_ASSERT_EQUAL(false, c.isSecureCookie());
-  CPPUNIT_ASSERT_EQUAL(true, c.isSessionCookie());
-
-  std::string str3 = "";
-  c = CookieParser().parse(str3, "", "");
-  CPPUNIT_ASSERT(!c.good());
-
-#ifndef __MINGW32__
-  std::string str4 = "UID=300; expires=Wed, 01-Jan-1960 00:00:00 GMT";
-  time_t expire_time = (time_t) -315619200;
-#else
-  std::string str4 = "UID=300; expires=Wed, 01-Jan-1970 00:00:01 GMT";
-  time_t expire_time = (time_t) 1;
-#endif
-  c = CookieParser().parse(str4, "localhost", "/");
-  CPPUNIT_ASSERT(c.good());
-  CPPUNIT_ASSERT(!c.isSessionCookie());
-  CPPUNIT_ASSERT_EQUAL(expire_time, c.getExpiry());
-
-  std::string str5 = "k=v; expires=Sun, 10-Jun-07 11:00:00 GMT";
-  c = CookieParser().parse(str5, "", "");
-  CPPUNIT_ASSERT(c.good());
-  CPPUNIT_ASSERT_EQUAL(std::string("k"), c.getName());
-  CPPUNIT_ASSERT_EQUAL(std::string("v"), c.getValue());
-  CPPUNIT_ASSERT_EQUAL((time_t)1181473200, c.getExpiry());
-  CPPUNIT_ASSERT_EQUAL(std::string(""), c.getDomain());
-  CPPUNIT_ASSERT_EQUAL(std::string(""), c.getPath());
-  CPPUNIT_ASSERT(!c.isSecureCookie());
-  CPPUNIT_ASSERT(!c.isSessionCookie());
-}
-
-} // namespace aria2

+ 155 - 130
test/CookieStorageTest.cc

@@ -8,7 +8,6 @@
 #include "Exception.h"
 #include "util.h"
 #include "TimeA2.h"
-#include "CookieParser.h"
 #include "RecoverableException.h"
 #include "File.h"
 #include "TestUtil.h"
@@ -21,6 +20,7 @@ class CookieStorageTest:public CppUnit::TestFixture {
   CPPUNIT_TEST(testStore);
   CPPUNIT_TEST(testParseAndStore);
   CPPUNIT_TEST(testCriteriaFind);
+  CPPUNIT_TEST(testCriteriaFind_cookieOrder);
   CPPUNIT_TEST(testLoad);
   CPPUNIT_TEST(testLoad_sqlite3);
   CPPUNIT_TEST(testLoad_fileNotfound);
@@ -39,6 +39,7 @@ public:
   void testStore();
   void testParseAndStore();
   void testCriteriaFind();
+  void testCriteriaFind_cookieOrder();
   void testLoad();
   void testLoad_sqlite3();
   void testLoad_fileNotfound();
@@ -60,113 +61,111 @@ void CookieStorageTest::dumpCookie
 
 void CookieStorageTest::testStore()
 {
+  time_t now = 1000;
   CookieStorage st;
-  Cookie goodCookie("k", "v", "/", "localhost", false);
-  CPPUNIT_ASSERT(st.store(goodCookie));
+  Cookie goodCookie(createCookie("k", "v", "localhost", true, "/", false));
+  CPPUNIT_ASSERT(st.store(goodCookie, now));
   CPPUNIT_ASSERT_EQUAL((size_t)1, st.size());
   CPPUNIT_ASSERT(st.contains(goodCookie));
 
-  Cookie anotherCookie("k", "v", "/", "mirror", true);
-  CPPUNIT_ASSERT(st.store(anotherCookie));
+  Cookie anotherCookie(createCookie("k", "v", "mirror",  true, "/", true));
+  CPPUNIT_ASSERT(st.store(anotherCookie, now));
   CPPUNIT_ASSERT_EQUAL((size_t)2, st.size());
   CPPUNIT_ASSERT(st.contains(anotherCookie));
   CPPUNIT_ASSERT(st.contains(goodCookie));
 
-  Cookie updateGoodCookie("k", "v2", "/", "localhost", false);
-  CPPUNIT_ASSERT(st.store(goodCookie));
+  Cookie updateGoodCookie(createCookie("k", "v2", "localhost",  true,
+                                       "/", false));
+  CPPUNIT_ASSERT(st.store(updateGoodCookie, now));
   CPPUNIT_ASSERT_EQUAL((size_t)2, st.size());
   CPPUNIT_ASSERT(st.contains(updateGoodCookie));
   CPPUNIT_ASSERT(st.contains(anotherCookie));
 
-  Cookie expireGoodCookie("k", "v3", 1, "/", "localhost", false);
-  CPPUNIT_ASSERT(!st.store(expireGoodCookie));
+  Cookie expireGoodCookie(createCookie("k", "v3", 0, "localhost", true,
+                                       "/", false));
+  CPPUNIT_ASSERT(!st.store(expireGoodCookie, now));
   CPPUNIT_ASSERT_EQUAL((size_t)1, st.size());
   CPPUNIT_ASSERT(st.contains(anotherCookie));
 
-  Cookie badCookie("", "", "/", "localhost", false);
-  CPPUNIT_ASSERT(!st.store(badCookie));
-  CPPUNIT_ASSERT_EQUAL((size_t)1, st.size());
-  CPPUNIT_ASSERT(st.contains(anotherCookie));
-
-  Cookie fromNumericHost("k", "v", "/", "192.168.1.1", false);
-  CPPUNIT_ASSERT(st.store(fromNumericHost));
+  Cookie fromNumericHost(createCookie("k", "v", "192.168.1.1", true,
+                                      "/", false));
+  CPPUNIT_ASSERT(st.store(fromNumericHost, now));
   CPPUNIT_ASSERT_EQUAL((size_t)2, st.size());
   CPPUNIT_ASSERT(st.contains(fromNumericHost));
-
-  Cookie sessionScopedGoodCookie("k", "v3", 0, "/", "localhost", false);
-  CPPUNIT_ASSERT(st.store(sessionScopedGoodCookie));
-  CPPUNIT_ASSERT_EQUAL((size_t)3, st.size());
-  CPPUNIT_ASSERT(st.contains(sessionScopedGoodCookie));
-
-  Cookie sessionScopedGoodCookie2("k2", "v3", "/", "localhost", false);
-  CPPUNIT_ASSERT(st.store(sessionScopedGoodCookie2));
-  CPPUNIT_ASSERT_EQUAL((size_t)4, st.size());
-  CPPUNIT_ASSERT(st.contains(sessionScopedGoodCookie2));
 }
 
 void CookieStorageTest::testParseAndStore()
 {
   CookieStorage st;
-
+  time_t now = 1000;
   std::string localhostCookieStr = "k=v;"
-    " expires=Fri, 2038-01-01 00:00:00 GMT; path=/; domain=localhost;";
-  
-  CPPUNIT_ASSERT(st.parseAndStore(localhostCookieStr,
-                                  "localhost", "/downloads"));
-  CPPUNIT_ASSERT(!st.parseAndStore(localhostCookieStr,
-                                   "mirror", "/downloads"));
-
-  CPPUNIT_ASSERT(!st.parseAndStore(localhostCookieStr,
-                                   "127.0.0.1", "/downloads"));
+    " expires=Fri, 01 Jan 2038 00:00:00 GMT; path=/; domain=localhost;";
+  CPPUNIT_ASSERT
+    (st.parseAndStore(localhostCookieStr, "localhost", "/downloads", now));
+  CPPUNIT_ASSERT
+    (!st.parseAndStore(localhostCookieStr, "mirror", "/downloads", now));
+  CPPUNIT_ASSERT
+    (!st.parseAndStore(localhostCookieStr, "127.0.0.1", "/downloads", now));
 
   std::string numericHostCookieStr = "k=v;"
-    " expires=Fri, 2038-01-01 00:00:00 GMT; path=/; domain=192.168.1.1;";
-  CPPUNIT_ASSERT(st.parseAndStore(numericHostCookieStr, "192.168.1.1", "/"));
+    " expires=Fri, 01 Jan 2038 00:00:00 GMT; path=/; domain=192.168.1.1;";
+  CPPUNIT_ASSERT
+    (st.parseAndStore(numericHostCookieStr, "192.168.1.1", "/", now));
 
   // No domain and no path are specified.
   std::string noDomainPathCookieStr = "k=v";
   CPPUNIT_ASSERT
-    (st.parseAndStore(noDomainPathCookieStr, "aria2.sf.net", "/downloads"));
+    (st.parseAndStore(noDomainPathCookieStr,
+                      "aria2.sf.net", "/downloads", now));
 }
 
 void CookieStorageTest::testCriteriaFind()
 {
   CookieStorage st;
-
-  Cookie alpha("alpha", "ALPHA", "/", ".aria2.org", false);
-  Cookie bravo("bravo", "BRAVO", Time().getTime()+60, "/foo", ".aria2.org",
-               false);
-  Cookie charlie("charlie", "CHARLIE", "/", ".aria2.org", true);
-  Cookie delta("delta", "DELTA", "/foo/bar", ".aria2.org", false);
-  Cookie echo("echo", "ECHO", "/", "www.dl.aria2.org", false);
-  Cookie foxtrot("foxtrot", "FOXTROT", "/", ".sf.net", false);
-  Cookie golf("golf", "GOLF", "/", "192.168.1.1", false);
-  Cookie hotel1("hotel", "HOTEL1", "/", "samename.x", false);
-  Cookie hotel2("hotel", "HOTEL2", "/hotel", "samename.x", false);
-  Cookie hotel3("hotel", "HOTEL3", "/bar/wine", "samename.x", false);
-  Cookie hotel4("hotel", "HOTEL4", "/bar/", "samename.x", false);
-  Cookie india1("india", "INDIA1", "/foo", "default.domain", false);
-  india1.markOriginServerOnly();
-  Cookie india2("india", "INDIA2", "/", "default.domain", false);
-  Cookie juliet1("juliet", "JULIET1", "/foo", "localhost", false);
-  juliet1.markOriginServerOnly();
-  Cookie juliet2("juliet", "JULIET2", "/", "localhost", false);
-
-  CPPUNIT_ASSERT(st.store(alpha));
-  CPPUNIT_ASSERT(st.store(bravo));
-  CPPUNIT_ASSERT(st.store(charlie));
-  CPPUNIT_ASSERT(st.store(delta));
-  CPPUNIT_ASSERT(st.store(echo));
-  CPPUNIT_ASSERT(st.store(foxtrot));
-  CPPUNIT_ASSERT(st.store(golf));
-  CPPUNIT_ASSERT(st.store(hotel1));
-  CPPUNIT_ASSERT(st.store(hotel2));
-  CPPUNIT_ASSERT(st.store(hotel3));
-  CPPUNIT_ASSERT(st.store(hotel4));
-  CPPUNIT_ASSERT(st.store(india1));
-  CPPUNIT_ASSERT(st.store(india2));
-  CPPUNIT_ASSERT(st.store(juliet1));
-  CPPUNIT_ASSERT(st.store(juliet2));
+  time_t now = 1000;
+
+  Cookie alpha(createCookie("alpha", "ALPHA", "aria2.org", false,  "/", false));
+  Cookie bravo(createCookie("bravo", "BRAVO", 1060, "aria2.org", false,
+                            "/foo", false));
+  Cookie charlie(createCookie("charlie", "CHARLIE", "aria2.org", false,
+                              "/", true));
+  Cookie delta(createCookie("delta", "DELTA", "aria2.org", false,
+                            "/foo/bar", false));
+  Cookie echo(createCookie("echo", "ECHO", "www.dl.aria2.org", false,
+                           "/", false));
+  Cookie foxtrot(createCookie("foxtrot", "FOXTROT", "sf.net", false,
+                              "/", false));
+  Cookie golf(createCookie("golf", "GOLF", "192.168.1.1",  true,
+                           "/", false));
+  Cookie hotel1(createCookie("hotel", "HOTEL1", "samename.x", false,
+                             "/", false));
+  Cookie hotel2(createCookie("hotel", "HOTEL2", "samename.x", false,
+                             "/hotel", false));
+  Cookie hotel3(createCookie("hotel", "HOTEL3", "samename.x", false,
+                             "/bar/wine", false));
+  Cookie hotel4(createCookie("hotel", "HOTEL4", "samename.x", false,
+                             "/bar/", false));
+  Cookie india1(createCookie("india", "INDIA1", "default.domain",  true,
+                             "/foo", false));
+  Cookie india2(createCookie("india", "INDIA2", "default.domain", false,
+                             "/",  false));
+  Cookie juliet1(createCookie("juliet", "JULIET1", "localhost", true,
+                              "/foo", false));
+
+  CPPUNIT_ASSERT(st.store(alpha, now));
+  CPPUNIT_ASSERT(st.store(bravo, now));
+  CPPUNIT_ASSERT(st.store(charlie, now));
+  CPPUNIT_ASSERT(st.store(delta, now));
+  CPPUNIT_ASSERT(st.store(echo, now));
+  CPPUNIT_ASSERT(st.store(foxtrot, now));
+  CPPUNIT_ASSERT(st.store(golf, now));
+  CPPUNIT_ASSERT(st.store(hotel1, now));
+  CPPUNIT_ASSERT(st.store(hotel2, now));
+  CPPUNIT_ASSERT(st.store(hotel3, now));
+  CPPUNIT_ASSERT(st.store(hotel4, now));
+  CPPUNIT_ASSERT(st.store(india1, now));
+  CPPUNIT_ASSERT(st.store(india2, now));
+  CPPUNIT_ASSERT(st.store(juliet1, now));
 
   std::vector<Cookie> aria2Slash = st.criteriaFind("www.dl.aria2.org", "/",
                                                    0, false);
@@ -186,7 +185,7 @@ void CookieStorageTest::testCriteriaFind()
                  != aria2SlashFoo.end());
 
   std::vector<Cookie> aria2Expires = st.criteriaFind("www.dl.aria2.org", "/foo",
-                                                     Time().getTime()+120,
+                                                     1120,
                                                      false);
   CPPUNIT_ASSERT_EQUAL((size_t)2, aria2Expires.size());
   CPPUNIT_ASSERT(std::find(aria2Expires.begin(), aria2Expires.end(), alpha)
@@ -220,23 +219,47 @@ void CookieStorageTest::testCriteriaFind()
   defaultDomainCookies =
     st.criteriaFind("sub.default.domain", "/foo", 0, false);
   CPPUNIT_ASSERT_EQUAL((size_t)1, defaultDomainCookies.size());
-
+  CPPUNIT_ASSERT_EQUAL(std::string("INDIA2"),
+                       defaultDomainCookies[0].getValue());
 
   // localhost.local case
   std::vector<Cookie> localDomainCookies =
     st.criteriaFind("localhost", "/foo", 0, false);
-  CPPUNIT_ASSERT_EQUAL((size_t)2, localDomainCookies.size());
+  CPPUNIT_ASSERT_EQUAL((size_t)1, localDomainCookies.size());
   CPPUNIT_ASSERT_EQUAL(std::string("JULIET1"),
                        localDomainCookies[0].getValue());
-  CPPUNIT_ASSERT_EQUAL(std::string("JULIET2"),
-                       localDomainCookies[1].getValue());
+}
+
+void CookieStorageTest::testCriteriaFind_cookieOrder()
+{
+  CookieStorage st;
+  Cookie a(createCookie("a", "0", "host", true, "/", false));
+  a.setCreationTime(1000);
+  Cookie b(createCookie("b", "0", "host", true, "/foo", false));
+  b.setCreationTime(5000);
+  Cookie c(createCookie("c", "0", "host", true, "/foo", false));
+  c.setCreationTime(4000);
+  Cookie d(createCookie("d", "0", "host", true, "/foo/bar", false));
+  d.setCreationTime(6000);
+
+  st.store(a, 0);
+  st.store(b, 0);
+  st.store(c, 0);
+  st.store(d, 0);
+
+  std::vector<Cookie> cookies = st.criteriaFind("host", "/foo/bar", 0, false);
+  CPPUNIT_ASSERT_EQUAL((size_t)4, cookies.size());
+  CPPUNIT_ASSERT_EQUAL(std::string("d"), cookies[0].getName());
+  CPPUNIT_ASSERT_EQUAL(std::string("c"), cookies[1].getName());
+  CPPUNIT_ASSERT_EQUAL(std::string("b"), cookies[2].getName());
+  CPPUNIT_ASSERT_EQUAL(std::string("a"), cookies[3].getName());
 }
 
 void CookieStorageTest::testLoad()
 {
   CookieStorage st;
 
-  st.load("nscookietest.txt");
+  st.load("nscookietest.txt", 1001);
 
   CPPUNIT_ASSERT_EQUAL((size_t)4, st.size());
 
@@ -244,72 +267,71 @@ void CookieStorageTest::testLoad()
   dumpCookie(cookies, st);
 
   Cookie c = cookies[0];
+  CPPUNIT_ASSERT_EQUAL(std::string("passwd"), c.getName());
+  CPPUNIT_ASSERT_EQUAL(std::string("secret"), c.getValue());
+  CPPUNIT_ASSERT_EQUAL((time_t)INT32_MAX, c.getExpiryTime());
+  CPPUNIT_ASSERT(!c.getPersistent());
+  CPPUNIT_ASSERT_EQUAL(std::string("/cgi-bin"), c.getPath());
+  CPPUNIT_ASSERT_EQUAL(std::string("192.168.0.1"), c.getDomain());
+  CPPUNIT_ASSERT(c.getHostOnly());
+  CPPUNIT_ASSERT(!c.getSecure());
+
+  c = cookies[1];
   CPPUNIT_ASSERT_EQUAL(std::string("novalue"), c.getName());
   CPPUNIT_ASSERT_EQUAL(std::string(""), c.getValue());
-  CPPUNIT_ASSERT_EQUAL((time_t)2147483647, c.getExpiry());
+  CPPUNIT_ASSERT_EQUAL((time_t)2147483647, c.getExpiryTime());
+  CPPUNIT_ASSERT(c.getPersistent());
   CPPUNIT_ASSERT_EQUAL(std::string("/"), c.getPath());
-  CPPUNIT_ASSERT_EQUAL(std::string(".example.org"), c.getDomain());
-  CPPUNIT_ASSERT(!c.isSecureCookie());
+  CPPUNIT_ASSERT_EQUAL(std::string("example.org"), c.getDomain());
+  CPPUNIT_ASSERT(!c.getHostOnly());
+  CPPUNIT_ASSERT(!c.getSecure());
 
-  c = cookies[1];
+  c = cookies[2];
   CPPUNIT_ASSERT_EQUAL(std::string("JSESSIONID"), c.getName());
   CPPUNIT_ASSERT_EQUAL(std::string("123456789"), c.getValue());
-  CPPUNIT_ASSERT_EQUAL((time_t)2147483647, c.getExpiry());
+  CPPUNIT_ASSERT_EQUAL((time_t)2147483647, c.getExpiryTime());
   CPPUNIT_ASSERT_EQUAL(std::string("/"), c.getPath());
-  CPPUNIT_ASSERT_EQUAL(std::string("localhost.local"), c.getDomain());
-  CPPUNIT_ASSERT(c.isSecureCookie());
-
-  c = cookies[2];
-  CPPUNIT_ASSERT_EQUAL(std::string("passwd"), c.getName());
-  CPPUNIT_ASSERT_EQUAL(std::string("secret"), c.getValue());
-  CPPUNIT_ASSERT_EQUAL((time_t)2147483647, c.getExpiry());
-  CPPUNIT_ASSERT_EQUAL(std::string("/cgi-bin"), c.getPath());
-  CPPUNIT_ASSERT_EQUAL(std::string("localhost.local"), c.getDomain());
-  CPPUNIT_ASSERT(!c.isSecureCookie());
+  CPPUNIT_ASSERT_EQUAL(std::string("localhost"), c.getDomain());
+  CPPUNIT_ASSERT(c.getHostOnly());
+  CPPUNIT_ASSERT(c.getSecure());
 
   c = cookies[3];
   CPPUNIT_ASSERT_EQUAL(std::string("TAX"), c.getName());
   CPPUNIT_ASSERT_EQUAL(std::string("1000"), c.getValue());
-  CPPUNIT_ASSERT_EQUAL((time_t)2147483647, c.getExpiry());
+  CPPUNIT_ASSERT((time_t)INT32_MAX <= c.getExpiryTime());
+  CPPUNIT_ASSERT(c.getPersistent());
   CPPUNIT_ASSERT_EQUAL(std::string("/"), c.getPath());
-  CPPUNIT_ASSERT_EQUAL(std::string("overflow.local"), c.getDomain());
-  CPPUNIT_ASSERT(!c.isSecureCookie());
+  CPPUNIT_ASSERT_EQUAL(std::string("overflow"), c.getDomain());
+  CPPUNIT_ASSERT(!c.getSecure());
 }
 
 void CookieStorageTest::testLoad_sqlite3()
 {
   CookieStorage st;
 #ifdef HAVE_SQLITE3
-  st.load("cookies.sqlite");
-  CPPUNIT_ASSERT_EQUAL((size_t)3, st.size());
+  st.load("cookies.sqlite", 1000);
+  CPPUNIT_ASSERT_EQUAL((size_t)2, st.size());
   std::vector<Cookie> cookies;
   dumpCookie(cookies, st);
   Cookie c = cookies[0];
-  CPPUNIT_ASSERT_EQUAL(std::string("uid"), c.getName());
-  CPPUNIT_ASSERT_EQUAL(std::string(""), c.getValue());
-  CPPUNIT_ASSERT_EQUAL((time_t)0, c.getExpiry());
-  CPPUNIT_ASSERT_EQUAL(std::string("/path/to"), c.getPath());
-  CPPUNIT_ASSERT_EQUAL(std::string(".null_value.com"), c.getDomain());
-  CPPUNIT_ASSERT(!c.isSecureCookie());
-  CPPUNIT_ASSERT(c.isSessionCookie());
+  CPPUNIT_ASSERT_EQUAL(std::string("JSESSIONID"), c.getName());
+  CPPUNIT_ASSERT_EQUAL(std::string("123456789"), c.getValue());
+  CPPUNIT_ASSERT_EQUAL((time_t)INT32_MAX, c.getExpiryTime());
+  CPPUNIT_ASSERT(c.getPersistent());
+  CPPUNIT_ASSERT_EQUAL(std::string("localhost"), c.getDomain());
+  CPPUNIT_ASSERT(c.getHostOnly());
+  CPPUNIT_ASSERT_EQUAL(std::string("/"), c.getPath());
+  CPPUNIT_ASSERT(c.getSecure());
 
   c = cookies[1];
   CPPUNIT_ASSERT_EQUAL(std::string("foo"), c.getName());
   CPPUNIT_ASSERT_EQUAL(std::string("bar"), c.getValue());
-  CPPUNIT_ASSERT_EQUAL((time_t)2147483647, c.getExpiry());
+  CPPUNIT_ASSERT((time_t)INT32_MAX <= c.getExpiryTime());
+  CPPUNIT_ASSERT(c.getPersistent());
+  CPPUNIT_ASSERT_EQUAL(std::string("overflow.time_t.org"), c.getDomain());
+  CPPUNIT_ASSERT(!c.getHostOnly());
   CPPUNIT_ASSERT_EQUAL(std::string("/path/to"), c.getPath());
-  CPPUNIT_ASSERT_EQUAL(std::string(".overflow.time_t.org"), c.getDomain());
-  CPPUNIT_ASSERT(!c.isSecureCookie());
-  CPPUNIT_ASSERT(!c.isSessionCookie());
-
-  c = cookies[2];
-  CPPUNIT_ASSERT_EQUAL(std::string("JSESSIONID"), c.getName());
-  CPPUNIT_ASSERT_EQUAL(std::string("123456789"), c.getValue());
-  CPPUNIT_ASSERT_EQUAL((time_t)2147483647, c.getExpiry());
-  CPPUNIT_ASSERT_EQUAL(std::string("/"), c.getPath());
-  CPPUNIT_ASSERT_EQUAL(std::string("localhost.local"), c.getDomain());
-  CPPUNIT_ASSERT(c.isSecureCookie());
-  CPPUNIT_ASSERT(!c.isSessionCookie());
+  CPPUNIT_ASSERT(!c.getSecure());
     
 #else // !HAVE_SQLITE3
   CPPUNIT_ASSERT(!st.load("cookies.sqlite"));
@@ -319,7 +341,7 @@ void CookieStorageTest::testLoad_sqlite3()
 void CookieStorageTest::testLoad_fileNotfound()
 {
   CookieStorage st;
-  CPPUNIT_ASSERT(!st.load("./aria2_CookieStorageTest_testLoad_fileNotfound"));
+  CPPUNIT_ASSERT(!st.load("./aria2_CookieStorageTest_testLoad_fileNotfound",0));
 }
 
 void CookieStorageTest::testSaveNsFormat()
@@ -328,19 +350,20 @@ void CookieStorageTest::testSaveNsFormat()
   std::string filename = "./aria2_CookieStorageTest_testSaveNsFormat";
   File(filename).remove();
   CookieStorage st;
-  st.store(Cookie("favorite","classic","/config",".domain.org",true));
-  st.store(Cookie("uid","tujikawa","/",".domain.org",false));
+  time_t now = 1000;
+  st.store(Cookie(createCookie("favorite", "classic", "domain.org", false,
+                               "/config",true)), now);
+  st.store(Cookie(createCookie("uid", "tujikawa", now, "domain.org", true,
+                               "/",false)), now);
   st.saveNsFormat(filename);
   CookieStorage loadst;
-  loadst.load(filename);
+  loadst.load(filename, now);
   CPPUNIT_ASSERT_EQUAL((size_t)2, loadst.size());
 
   std::vector<Cookie> cookies;
   dumpCookie(cookies, loadst);
 
   CPPUNIT_ASSERT_EQUAL(std::string("favorite"), cookies[0].getName());
-  CPPUNIT_ASSERT_EQUAL((time_t)0, cookies[0].getExpiry());
-  CPPUNIT_ASSERT(cookies[0].isSessionCookie());
   CPPUNIT_ASSERT_EQUAL(std::string("uid"), cookies[1].getName());
 }
 
@@ -359,8 +382,9 @@ void CookieStorageTest::testCookieIsFull()
 {
   CookieStorage st;
   for(size_t i = 0; i < CookieStorage::MAX_COOKIE_PER_DOMAIN+1; ++i) {
-    Cookie c("k"+util::itos(i), "v", "/", ".aria2.org", false);
-    st.store(c);
+    Cookie c(createCookie("k"+util::itos(i), "v", "aria2.org", false,
+                          "/", false));
+    st.store(c, 0);
   }
   CPPUNIT_ASSERT_EQUAL((size_t)CookieStorage::MAX_COOKIE_PER_DOMAIN, st.size());
 }
@@ -371,8 +395,9 @@ void CookieStorageTest::testDomainIsFull()
   // CookieStorage.cc
   CookieStorage st;
   for(size_t i = 0; i < 2001; ++i) {
-    Cookie c("k", "v", "/", "domain"+util::itos(i), false);
-    st.store(c);
+    Cookie c(createCookie("k", "v", "domain"+util::itos(i), true,
+                          "/", false));
+    st.store(c, 0);
   }
   CPPUNIT_ASSERT_EQUAL((size_t)1801, st.size());
 }

+ 37 - 129
test/CookieTest.cc

@@ -7,200 +7,108 @@
 #include "Exception.h"
 #include "util.h"
 #include "TimeA2.h"
+#include "TestUtil.h"
 
 namespace aria2 {
 
 class CookieTest:public CppUnit::TestFixture {
 
   CPPUNIT_TEST_SUITE(CookieTest);
-  CPPUNIT_TEST(testValidate);
   CPPUNIT_TEST(testOperatorEqual);
   CPPUNIT_TEST(testMatch);
   CPPUNIT_TEST(testIsExpired);
-  CPPUNIT_TEST(testNormalizeDomain);
   CPPUNIT_TEST(testToNsCookieFormat);
-  CPPUNIT_TEST(testMarkOriginServerOnly);
   CPPUNIT_TEST_SUITE_END();
 public:
   void setUp() {}
 
   void tearDown() {}
 
-  void testValidate();
   void testOperatorEqual();
   void testMatch();
   void testIsExpired();
-  void testNormalizeDomain();
   void testToNsCookieFormat();
-  void testMarkOriginServerOnly();
 };
 
 
 CPPUNIT_TEST_SUITE_REGISTRATION(CookieTest);
 
-void CookieTest::testValidate()
-{
-  {
-    Cookie defaultDomainPath("k", "v", "/", "localhost", false);
-    CPPUNIT_ASSERT(defaultDomainPath.validate("localhost", "/"));
-  }
-  {
-    Cookie domainStartsWithoutDot("k", "v", "/", "aria2.org", false);
-    CPPUNIT_ASSERT(domainStartsWithoutDot.validate("www.aria2.org", "/"));
-    Cookie domainStartsWithDot("k", "v", "/", ".aria2.org", false);
-    CPPUNIT_ASSERT(domainStartsWithDot.validate("www.aria2.org", "/"));
-  }
-  {
-    Cookie domainWithoutEmbeddedDot("k", "v", "/", ".org", false);
-    CPPUNIT_ASSERT(!domainWithoutEmbeddedDot.validate("aria2.org", "/"));
-  }
-  {
-    Cookie domainEndsWithDot("k", "v", "/", ".aria2.org.", false);
-    CPPUNIT_ASSERT(!domainEndsWithDot.validate("www.aria2.org", "/"));
-  }
-  {
-    Cookie domainHD("k", "v", "/", ".aria2.org", false);
-    CPPUNIT_ASSERT(!domainHD.validate("aria2.www.aria2.org", "/"));
-  }
-  {
-    Cookie pathNotStartsWith("k", "v", "/downloads", "localhost", false);
-    CPPUNIT_ASSERT(!pathNotStartsWith.validate("localhost", "/examples"));
-  }
-  {
-    Cookie pathStartsWith("k", "v", "/downloads", "localhost", false);
-    CPPUNIT_ASSERT(pathStartsWith.validate("localhost", "/downloads/latest/"));
-  }
-  {
-    Cookie pathSlash("k", "v", "/downloads", "localhost", false);
-    CPPUNIT_ASSERT(pathSlash.validate("localhost", "/downloads/"));
-  }
-  {
-    Cookie pathSlash("k", "v", "/downloads/", "localhost", false);
-    CPPUNIT_ASSERT(pathSlash.validate("localhost", "/downloads"));
-  }
-  {
-    Cookie pathNotMatch("k", "v", "/downloads", "localhost", false);
-    CPPUNIT_ASSERT(!pathNotMatch.validate("localhost", "/downloadss"));
-  }
-  {
-    Cookie pathNotMatch("k", "v", "/downloads/", "localhost", false);
-    CPPUNIT_ASSERT(!pathNotMatch.validate("localhost", "/downloadss"));
-  }
-  {
-    Cookie pathNotMatch("k", "v", "/downloads", "localhost", false);
-    CPPUNIT_ASSERT(!pathNotMatch.validate("localhost", "/downloadss/"));
-  }
-  {
-    Cookie nameEmpty("", "v", "/", "localhost", false);
-    CPPUNIT_ASSERT(!nameEmpty.validate("localhost", "/"));
-  }
-  {
-    Cookie fromNumericHost("k", "v", "/", "192.168.1.1", false);
-    CPPUNIT_ASSERT(fromNumericHost.validate("192.168.1.1", "/"));
-    CPPUNIT_ASSERT(!fromNumericHost.validate("www.aria2.org", "/"));
-  }
-}
-
 void CookieTest::testOperatorEqual()
 {
-  Cookie a("k", "v", "/", "localhost", false);
-  Cookie b("k", "v", "/", "LOCALHOST", true);
-  Cookie wrongPath("k", "v", "/a", "localhost", false);
-  Cookie wrongDomain("k", "v", "/", "mydomain", false);
-  Cookie wrongName("h", "v", "/a", "localhost", false);
-  Cookie caseSensitiveName("K", "v", "/a", "localhost", false);
+  Cookie a(createCookie("k", "v", "localhost", true, "/", false));
+  Cookie b(createCookie("k", "v", "localhost", true, "/", true));
+  Cookie wrongPath(createCookie("k", "v", "localhost", true, "/a", false));
+  Cookie wrongDomain(createCookie("k", "v", "mydomain", true, "/", false));
+  Cookie wrongName(createCookie("h", "v", "localhost", true, "/a", false));
+  Cookie caseSensitiveName(createCookie("K", "v", "localhost", true,
+                                        "/a", false));
   CPPUNIT_ASSERT(a == b);
   CPPUNIT_ASSERT(!(a == wrongPath));
   CPPUNIT_ASSERT(!(a == wrongDomain));
   CPPUNIT_ASSERT(!(a == wrongName));
   CPPUNIT_ASSERT(!(a == caseSensitiveName));
-  // normalize
-  Cookie n1("k", "v", "/", "localhost", false);
-  Cookie n2("k", "v", "/", ".localhost", false);
-  Cookie n3("k", "v", "/", "localhost.local", false);
-  Cookie n4("k", "v", "/", ".localhost.local", false);
-  CPPUNIT_ASSERT(n1 == n2);
-  CPPUNIT_ASSERT(n1 == n3);
-  CPPUNIT_ASSERT(n1 == n4);
 }
 
 void CookieTest::testMatch()
 {
-  Cookie c("k", "v", "/downloads", ".aria2.org", false);
-  Cookie c2("k", "v", "/downloads/", ".aria2.org", false);
-  Cookie c3("k", "v", "/downloads/", "aria2.org", false);
-  Cookie c4("k", "v", "/downloads/", "localhost", false);
+  Cookie c(createCookie("k", "v", "aria2.org", false, "/downloads", false));
+  Cookie c2(createCookie("k", "v", "aria2.org", false, "/downloads", false));
+  Cookie c3(createCookie("k", "v", "aria2.org", true, "/downloads", false));
+  Cookie c4(createCookie("k", "v", "localhost", true, "/downloads", false));
   CPPUNIT_ASSERT(c.match("www.aria2.org", "/downloads", 0, false));
-  CPPUNIT_ASSERT(c.match("www.aria2.org", "/downloads/", 0, false));
   CPPUNIT_ASSERT(c2.match("www.aria2.org", "/downloads", 0, false));
-  CPPUNIT_ASSERT(c.match("WWW.ARIA2.ORG", "/downloads", 0, false));
   CPPUNIT_ASSERT(!c.match("www.aria.org", "/downloads", 0, false));
   CPPUNIT_ASSERT(!c.match("www.aria2.org", "/examples", 0, false));
   CPPUNIT_ASSERT(c.match("www.aria2.org", "/downloads", 0, true));
   CPPUNIT_ASSERT(c.match("www.aria2.org", "/downloads/latest", 0, false));
   CPPUNIT_ASSERT(!c.match("www.aria2.org", "/downloadss/latest", 0, false));
   CPPUNIT_ASSERT(!c.match("www.aria2.org", "/DOWNLOADS", 0, false));
-  CPPUNIT_ASSERT(c3.match("www.aria2.org", "/downloads", 0, false));
+  CPPUNIT_ASSERT(!c3.match("www.aria2.org", "/downloads", 0, false));
   CPPUNIT_ASSERT(c4.match("localhost", "/downloads", 0, false));
 
-  Cookie secureCookie("k", "v", "/", "secure.aria2.org", true);
+  Cookie secureCookie(createCookie("k", "v", "secure.aria2.org", false,
+                                   "/", true));
   CPPUNIT_ASSERT(secureCookie.match("secure.aria2.org", "/", 0, true));
   CPPUNIT_ASSERT(!secureCookie.match("secure.aria2.org", "/", 0, false));
   CPPUNIT_ASSERT(!secureCookie.match("ssecure.aria2.org", "/", 0, true));
   CPPUNIT_ASSERT(secureCookie.match("www.secure.aria2.org", "/", 0, true));
 
-  Cookie expireTest("k", "v", 1000, "/", ".aria2.org", false);
+  Cookie expireTest(createCookie("k", "v", 1000, "aria2.org", false,
+                                 "/", false));
   CPPUNIT_ASSERT(expireTest.match("www.aria2.org", "/", 999, false));
-  CPPUNIT_ASSERT(!expireTest.match("www.aria2.org", "/", 1000, false));
+  CPPUNIT_ASSERT(expireTest.match("www.aria2.org", "/", 1000, false));
   CPPUNIT_ASSERT(!expireTest.match("www.aria2.org", "/", 1001, false));
   
-  Cookie fromNumericHost("k", "v", "/", "192.168.1.1", false);
-  CPPUNIT_ASSERT(fromNumericHost.match("192.168.1.1", "/", 0, false));
-  CPPUNIT_ASSERT(!fromNumericHost.match("www.aria2.org", "/", 0, false));
+  Cookie fromNumericHost(createCookie("k", "v", "192.168.1.1", true,
+                                      "/foo", false));
+  CPPUNIT_ASSERT(fromNumericHost.match("192.168.1.1", "/foo", 0, false));
+  CPPUNIT_ASSERT(!fromNumericHost.match("www.aria2.org", "/foo", 0, false));
+  CPPUNIT_ASSERT(!fromNumericHost.match("1.192.168.1.1", "/foo", 0, false));
+  CPPUNIT_ASSERT(!fromNumericHost.match("192.168.1.1", "/", 0, false));
 }
 
 void CookieTest::testIsExpired()
 {
-  Cookie expiredCookie("k", "v", 1000, "/", "localhost", false);
-  CPPUNIT_ASSERT(expiredCookie.isExpired());
-  Cookie validCookie("k", "v", Time().getTime()+60, "/", "localhost", false);
-  CPPUNIT_ASSERT(!validCookie.isExpired());
-  Cookie sessionCookie("k", "v", "/", "localhost", false);
-  CPPUNIT_ASSERT(!sessionCookie.isExpired());
-  Cookie sessionCookie2("k", "v", 0, "/", "localhost", false);
-  CPPUNIT_ASSERT(!sessionCookie2.isExpired());
-
-}
-
-void CookieTest::testNormalizeDomain()
-{
-  Cookie dot("k", "v", "/", ".", false);
-  CPPUNIT_ASSERT_EQUAL(std::string("..local"), dot.getDomain());
-  Cookie ip("k", "v", "/", "192.168.1.1", false);
-  CPPUNIT_ASSERT_EQUAL(std::string("192.168.1.1"), ip.getDomain());
-  Cookie ipv6("k", "v", "/", "::1", false);
-  CPPUNIT_ASSERT_EQUAL(std::string("::1"), ipv6.getDomain());  
+  Cookie cookie(createCookie("k", "v", 1000, "localhost", true,
+                                    "/", false));
+  CPPUNIT_ASSERT(cookie.isExpired(1001));
+  CPPUNIT_ASSERT(!cookie.isExpired(1000));
+  CPPUNIT_ASSERT(!cookie.isExpired(999));
+  Cookie sessionCookie(createCookie("k", "v", "localhost", true, "/", false));
+  CPPUNIT_ASSERT(!sessionCookie.isExpired(INT32_MAX));
 }
 
 void CookieTest::testToNsCookieFormat()
 {
   CPPUNIT_ASSERT_EQUAL
     (std::string(".domain.org\tTRUE\t/\tFALSE\t12345678\thello\tworld"),
-     Cookie("hello","world",12345678,"/",".domain.org",false).toNsCookieFormat());
-  // Session cookie's expiry is 0
+     createCookie("hello", "world", 12345678, "domain.org", false, "/",false)
+     .toNsCookieFormat());
+  // Session cookie
   CPPUNIT_ASSERT_EQUAL
-    (std::string(".domain.org\tTRUE\t/\tTRUE\t0\thello\tworld"),
-     Cookie("hello","world","/","domain.org",true).toNsCookieFormat());
-}
-
-void CookieTest::testMarkOriginServerOnly()
-{
-  Cookie c("k", "v", "/", "aria2.sf.net", false);
-  c.markOriginServerOnly();
-  CPPUNIT_ASSERT_EQUAL(std::string("aria2.sf.net"), c.getDomain());
-  Cookie ip("k", "v", "/", "192.168.0.1", false);
-  ip.markOriginServerOnly();
-  CPPUNIT_ASSERT_EQUAL(std::string("192.168.0.1"), ip.getDomain());
+    (std::string("domain.org\tFALSE\t/\tTRUE\t0\thello\tworld"),
+     createCookie("hello", "world", "domain.org", true, "/", true)
+     .toNsCookieFormat());
 }
 
 } // namespace aria2

+ 14 - 8
test/HttpRequestTest.cc

@@ -15,6 +15,7 @@
 #include "CookieStorage.h"
 #include "util.h"
 #include "AuthConfig.h"
+#include "TestUtil.h"
 
 namespace aria2 {
 
@@ -380,16 +381,21 @@ void HttpRequestTest::testCreateRequest_with_cookie()
     (new PiecedSegment(1024*1024, p));
   SharedHandle<FileEntry> fileEntry(new FileEntry("file", 1024*1024*10, 0));
 
-  Cookie cookie1("name1", "value1", "/archives", "localhost", false);
-  Cookie cookie2("name2", "value2", "/archives/download", "localhost", false);
-  Cookie cookie3("name3", "value3", "/archives/download", ".aria2.org", false);
-  Cookie cookie4("name4", "value4", "/archives/", ".aria2.org", true);
+  Cookie cookie1(createCookie("name1", "value1", "localhost", true,
+                              "/archives", false));
+  Cookie cookie2(createCookie("name2", "value2", "localhost", true,
+                              "/archives/download",  false));
+  Cookie cookie3(createCookie("name3", "value3", "aria2.org", false,
+                              "/archives/download",  false));
+  Cookie cookie4(createCookie("name4", "value4", "aria2.org", false,
+                              "/archives/", true));
 
+  time_t now = time(0);
   SharedHandle<CookieStorage> st(new CookieStorage());
-  CPPUNIT_ASSERT(st->store(cookie1));
-  CPPUNIT_ASSERT(st->store(cookie2));
-  CPPUNIT_ASSERT(st->store(cookie3));
-  CPPUNIT_ASSERT(st->store(cookie4));
+  CPPUNIT_ASSERT(st->store(cookie1, now));
+  CPPUNIT_ASSERT(st->store(cookie2, now));
+  CPPUNIT_ASSERT(st->store(cookie3, now));
+  CPPUNIT_ASSERT(st->store(cookie4, now));
 
   HttpRequest httpRequest;
 

+ 2 - 2
test/Makefile.am

@@ -22,7 +22,6 @@ aria2c_SOURCES = AllTest.cc\
 	UriListParserTest.cc\
 	HttpHeaderProcessorTest.cc\
 	RequestTest.cc\
-	CookieParserTest.cc\
 	HttpRequestTest.cc\
 	RequestGroupManTest.cc\
 	AuthConfigFactoryTest.cc\
@@ -75,7 +74,8 @@ aria2c_SOURCES = AllTest.cc\
 	ChunkedDecodingStreamFilterTest.cc\
 	UriTest.cc\
 	MockSegment.h\
-	TripletTest.cc
+	TripletTest.cc\
+	CookieHelperTest.cc
 
 if ENABLE_XML_RPC
 aria2c_SOURCES += XmlRpcRequestParserControllerTest.cc\

+ 32 - 32
test/Makefile.in

@@ -193,17 +193,16 @@ am__aria2c_SOURCES_DIST = AllTest.cc TestUtil.cc TestUtil.h \
 	DefaultBtProgressInfoFileTest.cc RequestGroupTest.cc \
 	PStringBuildVisitorTest.cc ParameterizedStringParserTest.cc \
 	UtilTest.cc AlphaNumberDecoratorTest.cc UriListParserTest.cc \
-	HttpHeaderProcessorTest.cc RequestTest.cc CookieParserTest.cc \
-	HttpRequestTest.cc RequestGroupManTest.cc \
-	AuthConfigFactoryTest.cc NetrcAuthResolverTest.cc \
-	DefaultAuthResolverTest.cc OptionHandlerTest.cc \
-	SegmentManTest.cc BitfieldManTest.cc NetrcTest.cc \
-	SingletonHolderTest.cc HttpHeaderTest.cc HttpResponseTest.cc \
-	SharedHandleTest.cc FileTest.cc OptionTest.cc \
-	DefaultDiskWriterTest.cc FeatureConfigTest.cc SpeedCalcTest.cc \
-	MultiDiskAdaptorTest.cc MultiFileAllocationIteratorTest.cc \
-	FixedNumberRandomizer.h ProtocolDetectorTest.cc \
-	StringFormatTest.cc ExceptionTest.cc \
+	HttpHeaderProcessorTest.cc RequestTest.cc HttpRequestTest.cc \
+	RequestGroupManTest.cc AuthConfigFactoryTest.cc \
+	NetrcAuthResolverTest.cc DefaultAuthResolverTest.cc \
+	OptionHandlerTest.cc SegmentManTest.cc BitfieldManTest.cc \
+	NetrcTest.cc SingletonHolderTest.cc HttpHeaderTest.cc \
+	HttpResponseTest.cc SharedHandleTest.cc FileTest.cc \
+	OptionTest.cc DefaultDiskWriterTest.cc FeatureConfigTest.cc \
+	SpeedCalcTest.cc MultiDiskAdaptorTest.cc \
+	MultiFileAllocationIteratorTest.cc FixedNumberRandomizer.h \
+	ProtocolDetectorTest.cc StringFormatTest.cc ExceptionTest.cc \
 	DownloadHandlerFactoryTest.cc SignatureTest.cc \
 	ServerStatManTest.cc FeedbackURISelectorTest.cc \
 	InOrderURISelectorTest.cc ServerStatTest.cc \
@@ -216,7 +215,8 @@ am__aria2c_SOURCES_DIST = AllTest.cc TestUtil.cc TestUtil.h \
 	bitfieldTest.cc DownloadContextTest.cc \
 	SessionSerializerTest.cc ValueBaseTest.cc \
 	ChunkedDecodingStreamFilterTest.cc UriTest.cc MockSegment.h \
-	TripletTest.cc XmlRpcRequestParserControllerTest.cc \
+	TripletTest.cc CookieHelperTest.cc \
+	XmlRpcRequestParserControllerTest.cc \
 	XmlRpcRequestProcessorTest.cc XmlRpcMethodTest.cc \
 	FallocFileAllocationIteratorTest.cc GZipDecoderTest.cc \
 	GZipEncoderTest.cc GZipDecodingStreamFilterTest.cc \
@@ -384,8 +384,8 @@ am_aria2c_OBJECTS = AllTest.$(OBJEXT) TestUtil.$(OBJEXT) \
 	ParameterizedStringParserTest.$(OBJEXT) UtilTest.$(OBJEXT) \
 	AlphaNumberDecoratorTest.$(OBJEXT) UriListParserTest.$(OBJEXT) \
 	HttpHeaderProcessorTest.$(OBJEXT) RequestTest.$(OBJEXT) \
-	CookieParserTest.$(OBJEXT) HttpRequestTest.$(OBJEXT) \
-	RequestGroupManTest.$(OBJEXT) AuthConfigFactoryTest.$(OBJEXT) \
+	HttpRequestTest.$(OBJEXT) RequestGroupManTest.$(OBJEXT) \
+	AuthConfigFactoryTest.$(OBJEXT) \
 	NetrcAuthResolverTest.$(OBJEXT) \
 	DefaultAuthResolverTest.$(OBJEXT) OptionHandlerTest.$(OBJEXT) \
 	SegmentManTest.$(OBJEXT) BitfieldManTest.$(OBJEXT) \
@@ -412,9 +412,10 @@ am_aria2c_OBJECTS = AllTest.$(OBJEXT) TestUtil.$(OBJEXT) \
 	DownloadContextTest.$(OBJEXT) SessionSerializerTest.$(OBJEXT) \
 	ValueBaseTest.$(OBJEXT) \
 	ChunkedDecodingStreamFilterTest.$(OBJEXT) UriTest.$(OBJEXT) \
-	TripletTest.$(OBJEXT) $(am__objects_1) $(am__objects_2) \
-	$(am__objects_3) $(am__objects_4) $(am__objects_5) \
-	$(am__objects_6) $(am__objects_7)
+	TripletTest.$(OBJEXT) CookieHelperTest.$(OBJEXT) \
+	$(am__objects_1) $(am__objects_2) $(am__objects_3) \
+	$(am__objects_4) $(am__objects_5) $(am__objects_6) \
+	$(am__objects_7)
 aria2c_OBJECTS = $(am_aria2c_OBJECTS)
 am__DEPENDENCIES_1 =
 aria2c_DEPENDENCIES = ../src/libaria2c.a $(am__DEPENDENCIES_1)
@@ -626,17 +627,16 @@ aria2c_SOURCES = AllTest.cc TestUtil.cc TestUtil.h SocketCoreTest.cc \
 	DefaultBtProgressInfoFileTest.cc RequestGroupTest.cc \
 	PStringBuildVisitorTest.cc ParameterizedStringParserTest.cc \
 	UtilTest.cc AlphaNumberDecoratorTest.cc UriListParserTest.cc \
-	HttpHeaderProcessorTest.cc RequestTest.cc CookieParserTest.cc \
-	HttpRequestTest.cc RequestGroupManTest.cc \
-	AuthConfigFactoryTest.cc NetrcAuthResolverTest.cc \
-	DefaultAuthResolverTest.cc OptionHandlerTest.cc \
-	SegmentManTest.cc BitfieldManTest.cc NetrcTest.cc \
-	SingletonHolderTest.cc HttpHeaderTest.cc HttpResponseTest.cc \
-	SharedHandleTest.cc FileTest.cc OptionTest.cc \
-	DefaultDiskWriterTest.cc FeatureConfigTest.cc SpeedCalcTest.cc \
-	MultiDiskAdaptorTest.cc MultiFileAllocationIteratorTest.cc \
-	FixedNumberRandomizer.h ProtocolDetectorTest.cc \
-	StringFormatTest.cc ExceptionTest.cc \
+	HttpHeaderProcessorTest.cc RequestTest.cc HttpRequestTest.cc \
+	RequestGroupManTest.cc AuthConfigFactoryTest.cc \
+	NetrcAuthResolverTest.cc DefaultAuthResolverTest.cc \
+	OptionHandlerTest.cc SegmentManTest.cc BitfieldManTest.cc \
+	NetrcTest.cc SingletonHolderTest.cc HttpHeaderTest.cc \
+	HttpResponseTest.cc SharedHandleTest.cc FileTest.cc \
+	OptionTest.cc DefaultDiskWriterTest.cc FeatureConfigTest.cc \
+	SpeedCalcTest.cc MultiDiskAdaptorTest.cc \
+	MultiFileAllocationIteratorTest.cc FixedNumberRandomizer.h \
+	ProtocolDetectorTest.cc StringFormatTest.cc ExceptionTest.cc \
 	DownloadHandlerFactoryTest.cc SignatureTest.cc \
 	ServerStatManTest.cc FeedbackURISelectorTest.cc \
 	InOrderURISelectorTest.cc ServerStatTest.cc \
@@ -649,9 +649,9 @@ aria2c_SOURCES = AllTest.cc TestUtil.cc TestUtil.h SocketCoreTest.cc \
 	bitfieldTest.cc DownloadContextTest.cc \
 	SessionSerializerTest.cc ValueBaseTest.cc \
 	ChunkedDecodingStreamFilterTest.cc UriTest.cc MockSegment.h \
-	TripletTest.cc $(am__append_1) $(am__append_2) $(am__append_3) \
-	$(am__append_4) $(am__append_5) $(am__append_6) \
-	$(am__append_7)
+	TripletTest.cc CookieHelperTest.cc $(am__append_1) \
+	$(am__append_2) $(am__append_3) $(am__append_4) \
+	$(am__append_5) $(am__append_6) $(am__append_7)
 
 #aria2c_CXXFLAGS = ${CPPUNIT_CFLAGS} -I../src -I../lib -Wall -D_FILE_OFFSET_BITS=64
 #aria2c_LDFLAGS = ${CPPUNIT_LIBS}
@@ -783,7 +783,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtUnchokeMessageTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ByteArrayDiskWriterTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ChunkedDecodingStreamFilterTest.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CookieParserTest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CookieHelperTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CookieStorageTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CookieTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DHKeyExchangeTest.Po@am__quote@

+ 30 - 12
test/NsCookieParserTest.cc

@@ -6,6 +6,7 @@
 
 #include "RecoverableException.h"
 #include "util.h"
+#include "Cookie.h"
 
 namespace aria2 {
 
@@ -30,50 +31,67 @@ CPPUNIT_TEST_SUITE_REGISTRATION(NsCookieParserTest);
 void NsCookieParserTest::testParse()
 {
   NsCookieParser parser;
-  std::vector<Cookie> cookies = parser.parse("nscookietest.txt");
+  time_t now = 0;
+  std::vector<Cookie> cookies = parser.parse("nscookietest.txt", now);
   CPPUNIT_ASSERT_EQUAL((size_t)5, cookies.size());
 
   Cookie c = cookies[0];
   CPPUNIT_ASSERT_EQUAL(std::string("JSESSIONID"), c.getName());
   CPPUNIT_ASSERT_EQUAL(std::string("123456789"), c.getValue());
-  CPPUNIT_ASSERT_EQUAL((time_t)2147483647, c.getExpiry());
+  CPPUNIT_ASSERT_EQUAL((time_t)INT32_MAX, c.getExpiryTime());
+  CPPUNIT_ASSERT(c.getPersistent());
+  CPPUNIT_ASSERT_EQUAL(std::string("localhost"), c.getDomain());
+  CPPUNIT_ASSERT(c.getHostOnly());
   CPPUNIT_ASSERT_EQUAL(std::string("/"), c.getPath());
-  CPPUNIT_ASSERT_EQUAL(std::string("localhost.local"), c.getDomain());
+  CPPUNIT_ASSERT(c.getSecure());
 
   c = cookies[1];
   CPPUNIT_ASSERT_EQUAL(std::string("user"), c.getName());
   CPPUNIT_ASSERT_EQUAL(std::string("me"), c.getValue());
-  CPPUNIT_ASSERT_EQUAL((time_t)1181473200, c.getExpiry());
+  CPPUNIT_ASSERT_EQUAL((time_t)1000, c.getExpiryTime());
+  CPPUNIT_ASSERT(c.getPersistent());
+  CPPUNIT_ASSERT_EQUAL(std::string("expired"), c.getDomain());
+  CPPUNIT_ASSERT(c.getHostOnly());
   CPPUNIT_ASSERT_EQUAL(std::string("/"), c.getPath());
-  CPPUNIT_ASSERT_EQUAL(std::string("expired.local"), c.getDomain());
+  CPPUNIT_ASSERT(!c.getSecure());
 
   c = cookies[2];
   CPPUNIT_ASSERT_EQUAL(std::string("passwd"), c.getName());
   CPPUNIT_ASSERT_EQUAL(std::string("secret"), c.getValue());
-  CPPUNIT_ASSERT_EQUAL((time_t)2147483647, c.getExpiry());
+  CPPUNIT_ASSERT_EQUAL((time_t)INT32_MAX, c.getExpiryTime());
+  CPPUNIT_ASSERT(!c.getPersistent());
+  CPPUNIT_ASSERT_EQUAL(std::string("192.168.0.1"), c.getDomain());
+  CPPUNIT_ASSERT(c.getHostOnly());
   CPPUNIT_ASSERT_EQUAL(std::string("/cgi-bin"), c.getPath());
-  CPPUNIT_ASSERT_EQUAL(std::string("localhost.local"), c.getDomain());
+  CPPUNIT_ASSERT(!c.getSecure());
 
   c = cookies[3];
   CPPUNIT_ASSERT_EQUAL(std::string("TAX"), c.getName());
   CPPUNIT_ASSERT_EQUAL(std::string("1000"), c.getValue());
-  CPPUNIT_ASSERT_EQUAL((time_t)2147483647, c.getExpiry());
+  CPPUNIT_ASSERT((time_t)INT32_MAX <= c.getExpiryTime());
+  CPPUNIT_ASSERT(c.getPersistent());  
+  CPPUNIT_ASSERT_EQUAL(std::string("overflow"), c.getDomain());
+  CPPUNIT_ASSERT(c.getHostOnly());
   CPPUNIT_ASSERT_EQUAL(std::string("/"), c.getPath());
-  CPPUNIT_ASSERT_EQUAL(std::string("overflow.local"), c.getDomain());
+  CPPUNIT_ASSERT(!c.getSecure());
 
   c = cookies[4];
   CPPUNIT_ASSERT_EQUAL(std::string("novalue"), c.getName());
   CPPUNIT_ASSERT_EQUAL(std::string(""), c.getValue());
-  CPPUNIT_ASSERT_EQUAL((time_t)2147483647, c.getExpiry());
+  CPPUNIT_ASSERT_EQUAL((time_t)INT32_MAX, c.getExpiryTime());
+  CPPUNIT_ASSERT(c.getPersistent());  
+  CPPUNIT_ASSERT_EQUAL(std::string("example.org"), c.getDomain());
+  CPPUNIT_ASSERT(!c.getHostOnly());
   CPPUNIT_ASSERT_EQUAL(std::string("/"), c.getPath());
-  CPPUNIT_ASSERT_EQUAL(std::string(".example.org"), c.getDomain());
+  CPPUNIT_ASSERT(!c.getSecure());
 }
 
 void NsCookieParserTest::testParse_fileNotFound()
 {
   NsCookieParser parser;
   try {
-    parser.parse("fileNotFound");
+    time_t now = 0;
+    parser.parse("fileNotFound", now);
     CPPUNIT_FAIL("exception must be thrown.");
   } catch(RecoverableException& e) {
     // SUCCESS

+ 44 - 26
test/Sqlite3CookieParserTest.cc

@@ -35,35 +35,43 @@ void Sqlite3CookieParserTest::testMozParse()
 {
   Sqlite3MozCookieParser parser("cookies.sqlite");
   std::vector<Cookie> cookies;
-  parser.parse(cookies);
+  parser.parse(cookies, 0);
   CPPUNIT_ASSERT_EQUAL((size_t)3, cookies.size());
 
   const Cookie& localhost = cookies[0];
-  CPPUNIT_ASSERT_EQUAL(std::string("localhost.local"), localhost.getDomain());
-  CPPUNIT_ASSERT_EQUAL(std::string("/"), localhost.getPath());
   CPPUNIT_ASSERT_EQUAL(std::string("JSESSIONID"), localhost.getName());
   CPPUNIT_ASSERT_EQUAL(std::string("123456789"), localhost.getValue());
-  CPPUNIT_ASSERT_EQUAL((time_t)INT32_MAX, localhost.getExpiry());
-  CPPUNIT_ASSERT_EQUAL(true, localhost.isSecureCookie());
+  CPPUNIT_ASSERT_EQUAL((time_t)INT32_MAX, localhost.getExpiryTime());
+  CPPUNIT_ASSERT(localhost.getPersistent());
+  CPPUNIT_ASSERT_EQUAL(std::string("localhost"), localhost.getDomain());
+  CPPUNIT_ASSERT(localhost.getHostOnly());
+  CPPUNIT_ASSERT_EQUAL(std::string("/"), localhost.getPath());
+  CPPUNIT_ASSERT(localhost.getSecure());
 
   const Cookie& nullValue = cookies[1];
-  CPPUNIT_ASSERT_EQUAL(std::string(".null_value.com"), nullValue.getDomain());
-  CPPUNIT_ASSERT_EQUAL(std::string("/path/to"), nullValue.getPath());
   CPPUNIT_ASSERT_EQUAL(std::string("uid"), nullValue.getName());
   CPPUNIT_ASSERT_EQUAL(std::string(""), nullValue.getValue());
-  CPPUNIT_ASSERT_EQUAL((time_t)0, nullValue.getExpiry());
-  CPPUNIT_ASSERT_EQUAL(false, nullValue.isSecureCookie());
+  CPPUNIT_ASSERT_EQUAL((time_t)0, nullValue.getExpiryTime());
+  CPPUNIT_ASSERT(nullValue.getPersistent());
+  CPPUNIT_ASSERT_EQUAL(std::string("null_value.com"), nullValue.getDomain());
+  CPPUNIT_ASSERT(!nullValue.getHostOnly());
+  CPPUNIT_ASSERT_EQUAL(std::string("/path/to"), nullValue.getPath());
+  CPPUNIT_ASSERT(!nullValue.getSecure());
 
   // See row id=3 has no name, so it is skipped.
 
   const Cookie& overflowTime = cookies[2];
-  CPPUNIT_ASSERT_EQUAL(std::string(".overflow.time_t.org"),
-                       overflowTime.getDomain());
-  CPPUNIT_ASSERT_EQUAL(std::string("/path/to"), overflowTime.getPath());
   CPPUNIT_ASSERT_EQUAL(std::string("foo"), overflowTime.getName());
   CPPUNIT_ASSERT_EQUAL(std::string("bar"), overflowTime.getValue());
-  CPPUNIT_ASSERT_EQUAL((time_t)INT32_MAX, overflowTime.getExpiry());
-  CPPUNIT_ASSERT_EQUAL(false, overflowTime.isSecureCookie());
+  CPPUNIT_ASSERT((time_t)INT32_MAX <= overflowTime.getExpiryTime());
+  CPPUNIT_ASSERT(overflowTime.getPersistent());
+  CPPUNIT_ASSERT_EQUAL(std::string("overflow.time_t.org"),
+                       overflowTime.getDomain());
+  CPPUNIT_ASSERT(!overflowTime.getHostOnly());
+  CPPUNIT_ASSERT_EQUAL(std::string("/path/to"), overflowTime.getPath());
+  CPPUNIT_ASSERT(!overflowTime.getSecure());
+
+  // See row id=5 has bad path, so it is skipped.
 }
 
 void Sqlite3CookieParserTest::testMozParse_fileNotFound()
@@ -71,7 +79,7 @@ void Sqlite3CookieParserTest::testMozParse_fileNotFound()
   Sqlite3MozCookieParser parser("fileNotFound");
   try {
     std::vector<Cookie> cookies;
-    parser.parse(cookies);
+    parser.parse(cookies, 0);
     CPPUNIT_FAIL("exception must be thrown.");
   } catch(RecoverableException& e) {
     // SUCCESS
@@ -85,7 +93,7 @@ void Sqlite3CookieParserTest::testMozParse_badfile()
   Sqlite3MozCookieParser parser("badcookies.sqlite");
   try {
     std::vector<Cookie> cookies;
-    parser.parse(cookies);
+    parser.parse(cookies, 0);
     CPPUNIT_FAIL("exception must be thrown.");
   } catch(RecoverableException& e) {
     // SUCCESS
@@ -96,23 +104,33 @@ void Sqlite3CookieParserTest::testChromumParse()
 {
   Sqlite3ChromiumCookieParser parser("chromium_cookies.sqlite");
   std::vector<Cookie> cookies;
-  parser.parse(cookies);
+  parser.parse(cookies, 0);
+  CPPUNIT_ASSERT_EQUAL((size_t)3, cookies.size());
+
   const Cookie& sfnet = cookies[0];
-  CPPUNIT_ASSERT_EQUAL(std::string(".aria2.sourceforge.net"),
-                       sfnet.getDomain());
-  CPPUNIT_ASSERT_EQUAL(std::string("/"), sfnet.getPath());
   CPPUNIT_ASSERT_EQUAL(std::string("mykey"), sfnet.getName());
   CPPUNIT_ASSERT_EQUAL(std::string("pass"), sfnet.getValue());
-  CPPUNIT_ASSERT_EQUAL((time_t)12345679, sfnet.getExpiry());
-  CPPUNIT_ASSERT_EQUAL(false, sfnet.isSecureCookie());
+  CPPUNIT_ASSERT_EQUAL((time_t)12345679, sfnet.getExpiryTime());
+  CPPUNIT_ASSERT(sfnet.getPersistent());
+  CPPUNIT_ASSERT_EQUAL(std::string("aria2.sourceforge.net"),
+                       sfnet.getDomain());
+  CPPUNIT_ASSERT(!sfnet.getHostOnly());
+  CPPUNIT_ASSERT_EQUAL(std::string("/"), sfnet.getPath());
+  CPPUNIT_ASSERT(!sfnet.getSecure());
 
   const Cookie& sfjp = cookies[1];
-  CPPUNIT_ASSERT_EQUAL(std::string(".aria2.sourceforge.jp"), sfjp.getDomain());
-  CPPUNIT_ASSERT_EQUAL(std::string("/profile"), sfjp.getPath());
   CPPUNIT_ASSERT_EQUAL(std::string("myseckey"), sfjp.getName());
   CPPUNIT_ASSERT_EQUAL(std::string("pass2"), sfjp.getValue());
-  CPPUNIT_ASSERT_EQUAL((time_t)0, sfjp.getExpiry());
-  CPPUNIT_ASSERT_EQUAL(true, sfjp.isSecureCookie());
+  CPPUNIT_ASSERT_EQUAL((time_t)0, sfjp.getExpiryTime());
+  CPPUNIT_ASSERT(sfjp.getPersistent());
+  CPPUNIT_ASSERT_EQUAL(std::string("aria2.sourceforge.jp"), sfjp.getDomain());
+  CPPUNIT_ASSERT(sfjp.getHostOnly());
+  CPPUNIT_ASSERT_EQUAL(std::string("/profile"), sfjp.getPath());
+  CPPUNIT_ASSERT(sfjp.getSecure());
+
+  const Cookie& localnet = cookies[2];
+  CPPUNIT_ASSERT_EQUAL(std::string("192.168.0.1"), localnet.getDomain());
+  CPPUNIT_ASSERT(sfjp.getHostOnly());  
 }
 
 } // namespace aria2

+ 27 - 1
test/TestUtil.cc

@@ -12,6 +12,7 @@
 #include "File.h"
 #include "StringFormat.h"
 #include "FatalException.h"
+#include "Cookie.h"
 
 namespace aria2 {
 
@@ -45,4 +46,29 @@ std::string readFile(const std::string& path)
   return ss.str();
 }
 
-};
+Cookie createCookie
+(const std::string& name,
+ const std::string& value,
+ const std::string& domain,
+ bool hostOnly,
+ const std::string& path,
+ bool secure)
+{
+  return Cookie
+    (name, value, 0, false, domain, hostOnly, path, secure, false, 0);
+}
+
+Cookie createCookie
+(const std::string& name,
+ const std::string& value,
+ time_t expiryTime,
+ const std::string& domain,
+ bool hostOnly,
+ const std::string& path,
+ bool secure)
+{
+  return Cookie
+    (name, value, expiryTime, true, domain, hostOnly, path, secure, false, 0);
+}
+
+} // namespace aria2

+ 17 - 0
test/TestUtil.h

@@ -22,4 +22,21 @@ public:
   }
 };
 
+Cookie createCookie
+(const std::string& name,
+ const std::string& value,
+ const std::string& domain,
+ bool hostOnly,
+ const std::string& path,
+ bool secure);
+
+Cookie createCookie
+(const std::string& name,
+ const std::string& value,
+ time_t expiryTime,
+ const std::string& domain,
+ bool hostOnly,
+ const std::string& path,
+ bool secure);
+
 } // namespace aria2

+ 1 - 1
test/a2functionalTest.cc

@@ -55,7 +55,7 @@ public:
     time_t lastAccess_;
     LastAccess(time_t lastAccess):lastAccess_(lastAccess) {}
 
-    time_t getLastAccess() const
+    time_t getLastAccessTime() const
     {
       return lastAccess_;
     }

BIN
test/chromium_cookies.sqlite


BIN
test/cookies.sqlite


+ 3 - 3
test/nscookietest.txt

@@ -1,9 +1,9 @@
 # name=this is a omment line;
 localhost	FALSE	/	TRUE	2147483647	JSESSIONID	123456789
 
-expired	FALSE	/	FALSE	1181473200	user	me
+expired	FALSE	/	FALSE	1000	user	me
 
-localhost	FALSE	/cgi-bin	FALSE	2147483647	passwd	secret
+192.168.0.1	TRUE	/cgi-bin	FALSE	0	passwd	secret
 badformat
 overflow	FALSE	/	FALSE	9223372036854775807	TAX	1000
-.example.org	TRUE	/	FALSE	2147483648	novalue	
+.example.org	TRUE	/	FALSE	2147483647	novalue