Browse Source

Merge branch 'master' into dynamic-select-file

Tatsuhiro Tsujikawa 9 years ago
parent
commit
9727b5b256

+ 5 - 1
AUTHORS

@@ -16,13 +16,17 @@ ITriskTI
 Igor Khomyakov
 Jarda Snajdr
 Kcchouette
+Kurt Kartaltepe
+Mingye Wang
 Nils Maier
 ORiON-
+ReadmeCritic
 Ross Smith II
 Ryan Steinmetz
 Ryo ONODERA
 Sarim Khan
 Sergey Zolotarev
+Sonny Piers
 Tatsuhiro Tsujikawa
 Vasilij Schneidermann
 Zoltan Toth-Czifra
@@ -36,6 +40,6 @@ luokar
 mozillazg
 multisnow
 oliviercommelarbre
-Sonny Piers
+rotor
 
 [1] https://gist.github.com/tatsuhiro-t/deaffeb064652104ad11

+ 63 - 21
NEWS

@@ -1,37 +1,79 @@
-aria2 1.22.0
+aria2 1.23.0
 ============
 
 Release Note
 ------------
 
-This release adds new feature that manages the number of concurrent
-downloads dynamically.  --stream-piece-selector option gets new value
-"random" which randomizes the piece selection for HTTP/FTP downloads.
-This effectively randomizes the order of files on multi-file Web
-Seeding.  Now all contributor's names are in AUTHORS file.
-Previously, aria2 shows error when it sees floating point number in a
-torrent file because torrent file specification does not allow
-floating point number.  In this release, they are just ignored, and
-aria2 continues to parse the rest of the torrent file as if there is
-nothing wrong.
+This release fixes several bugs reported by users, and adds several
+new features.  Read the following section for details.
 
 Changes
 -------
 
-* Add description about possible fragmentation with
-  --file-allocation=trunc
+* Simplify cache write
 
-* Make single-entry metalink download with multi-file torrent work
+  The previous cache write routine was too complex.  I'm sure I can
+  rewrite it to more elegantly.  But the primary motivation of this
+  complex logic is for disk activity reduction on Windows 7, and I
+  observed it on my old IDE disk.  I checked it again recently, and
+  there is no difference between with and without this complex logic.
+  For this reason, it was removed.  Will revert this change if many of
+  users are not happy with this.
 
-* Add all contributor's names in AUTHORS
+* Allow subsecond value in ns cookie.txt file's expiry time field
 
-* Ignore floating number in torrent file
+  Fixes GH-655
 
-* Added support for a dynamic management of the number of concurrent
-  downloads as a function of the overall bandwidth observed
+* Adjust chromium cookie time
 
-  This change adds --optimize-concurrent-downloads option.
+* import-po: iterate on glob, not ls output
 
-  Patch from oliviercommelarbre
+  Patch from Mingye Wang
 
-* Add --stream-piece-selector=random
+* Add --stderr option to redirect all stdout log output to stderr
+
+  Fixes GH-638
+
+* Add "hide" to --download-result option
+
+  Fixes GH-639
+
+* Fix downloaded metaurl torrent filename
+
+* Add a little bit of color to have a better visual of important
+  informations
+
+  Patch from rotor
+
+* Update README URLs based on HTTP redirects
+
+  Patch from ReadmeCritic
+
+* Relocate from github.com/tatsuhiro-t/aria2 to github.com/aria2/aria2
+
+  Fixes GH-602
+
+* mingw: Defer the falloc warning until falloc is specified by option
+
+  Fixes GH-594
+
+* Add bittorrent key to aria2.tellStopped status
+
+  Fixes GH-612
+
+* Addsystem.listNotifications RPC method
+
+  Merges GH-620
+
+  Patch from Sonny Piers
+
+* Report CheckIntegrity info in tellStatus
+
+  - Adds verifiedLength to tellStatus. Reports the length of data that
+    has been verified of the current RequestGroup being verified.
+  - Adds verifyPending to tellStatus. Reports if the RequestGroup has
+    a verification of integrity pending.
+
+  Closes GH-561
+
+  Patch from Kurt Kartaltepe

+ 1 - 1
README.android

@@ -42,7 +42,7 @@ aria2c executable was generated using android-ndk-r10d.
 
 The following libraries were statically linked.
 
-* openssl 1.0.2g
+* openssl 1.0.2h
 * expat 2.1.0
 * c-ares 1.11.0
 * libssh2 1.7.0

+ 1 - 1
configure.ac

@@ -2,7 +2,7 @@
 # Process this file with autoconf to produce a configure script.
 #
 AC_PREREQ([2.67])
-AC_INIT([aria2],[1.22.0],[https://github.com/aria2/aria2/issues],[aria2],[https://aria2.github.io/])
+AC_INIT([aria2],[1.23.0],[https://github.com/aria2/aria2/issues],[aria2],[https://aria2.github.io/])
 
 AC_CANONICAL_HOST
 AC_CANONICAL_TARGET

File diff suppressed because it is too large
+ 1 - 1
doc/bash_completion/aria2c


+ 1 - 1
import-po

@@ -30,7 +30,7 @@ echo "Renaming po files..."
 mv "$WORK_DIR"/aria2/*.po "$WORK_DIR"
 
 echo -n "en@quot en@boldquot" > "$PO_DIR"/LINGUAS
-for file in `ls "$WORK_DIR"/*.po`; do
+for file in "$WORK_DIR"/*.po; do
     # First remove useless '\r' in messages
     sed -i -e 's/\\r//' "$file"
     bn=`basename "$file"`

+ 1 - 1
po/README.rst

@@ -21,5 +21,5 @@ Importing PO files from launchpad-export.tar.gz
 The downloaded file is named as launchpad-export.tar.gz at the time of
 this writing.  It includes all PO files translated at launchpad.  To
 import those files, use ``import-po`` script in the top directory. It
-will deflate the tar.gz file and rename PO files and move them to po
+will inflate the tar.gz file and rename PO files and move them to po
 directory and run ``make update-po``.

+ 3 - 42
src/AbstractSingleDiskAdaptor.cc

@@ -96,49 +96,10 @@ ssize_t AbstractSingleDiskAdaptor::readDataDropCache(unsigned char* data,
 
 void AbstractSingleDiskAdaptor::writeCache(const WrDiskCacheEntry* entry)
 {
-  // Write cached data in 4KiB aligned offset. This reduces disk
-  // activity especially on Windows 7 NTFS. In this code, we assume
-  // that maximum length of DataCell data is 16KiB to simplify the
-  // code.
-  unsigned char buf[16_k];
-  int64_t start = 0;
-  size_t buflen = 0;
-  size_t buffoffset = 0;
-  const WrDiskCacheEntry::DataCellSet& dataSet = entry->getDataSet();
-  for (auto& d : dataSet) {
-    if (start + static_cast<ssize_t>(buflen) < d->goff) {
-      A2_LOG_DEBUG(fmt("Cache flush goff=%" PRId64 ", len=%lu", start,
-                       static_cast<unsigned long>(buflen)));
-      writeData(buf + buffoffset, buflen - buffoffset, start);
-      start = d->goff;
-      buflen = buffoffset = 0;
-    }
-    if (buflen == 0 && (d->goff & 0xfff) == 0 && (d->len & 0xfff) == 0) {
-      // Already aligned. Write it without copy.
-      A2_LOG_DEBUG(fmt("Cache flush goff=%" PRId64 ", len=%lu", start,
-                       static_cast<unsigned long>(d->len)));
-      writeData(d->data + d->offset, d->len, start);
-      start += d->len;
-    }
-    else {
-      if (buflen == 0) {
-        buflen = buffoffset = d->goff & 0xfff;
-      }
-      size_t wlen = std::min(sizeof(buf) - buflen, d->len);
-      memcpy(buf + buflen, d->data + d->offset, wlen);
-      buflen += wlen;
-      if (buflen == sizeof(buf)) {
-        A2_LOG_DEBUG(fmt("Cache flush goff=%" PRId64 ", len=%lu", start,
-                         static_cast<unsigned long>(buflen)));
-        writeData(buf + buffoffset, buflen - buffoffset, start);
-        memcpy(buf, d->data + d->offset + wlen, d->len - wlen);
-        start += sizeof(buf) - buffoffset;
-        buflen = d->len - wlen;
-        buffoffset = 0;
-      }
-    }
+  for (auto& d : entry->getDataSet()) {
+    A2_LOG_DEBUG(fmt("Cache flush goff=%" PRId64 ", len=%lu", d->goff, d->len));
+    writeData(d->data + d->offset, d->len, d->goff);
   }
-  writeData(buf + buffoffset, buflen - buffoffset, start);
 }
 
 bool AbstractSingleDiskAdaptor::fileExists()

+ 25 - 28
src/BtHandshakeMessage.cc

@@ -37,16 +37,13 @@
 #include <cstring>
 
 #include "util.h"
-#include "BtConstants.h"
 #include "a2functional.h"
 
 namespace aria2 {
 
+const unsigned char BtHandshakeMessage::BT_PSTR[] = "BitTorrent protocol";
 const char BtHandshakeMessage::NAME[] = "handshake";
 
-const unsigned char* BtHandshakeMessage::BT_PSTR =
-    reinterpret_cast<const unsigned char*>("BitTorrent protocol");
-
 BtHandshakeMessage::BtHandshakeMessage() : SimpleBtMessage(ID, NAME) { init(); }
 
 BtHandshakeMessage::BtHandshakeMessage(const unsigned char* infoHash,
@@ -54,19 +51,17 @@ BtHandshakeMessage::BtHandshakeMessage(const unsigned char* infoHash,
     : SimpleBtMessage(ID, NAME)
 {
   init();
-  memcpy(infoHash_, infoHash, INFO_HASH_LENGTH);
-  memcpy(peerId_, peerId, PEER_ID_LENGTH);
+  std::copy_n(infoHash, infoHash_.size(), std::begin(infoHash_));
+  std::copy_n(peerId, peerId_.size(), std::begin(peerId_));
 }
 
+BtHandshakeMessage::~BtHandshakeMessage() {}
+
 void BtHandshakeMessage::init()
 {
   pstrlen_ = 19;
-  pstr_ = new unsigned char[PSTR_LENGTH];
-  reserved_ = new unsigned char[RESERVED_LENGTH];
-  infoHash_ = new unsigned char[INFO_HASH_LENGTH];
-  peerId_ = new unsigned char[PEER_ID_LENGTH];
-  memcpy(pstr_, BT_PSTR, PSTR_LENGTH);
-  memset(reserved_, 0, RESERVED_LENGTH);
+  std::copy_n(BT_PSTR, pstr_.size(), std::begin(pstr_));
+  std::fill(std::begin(reserved_), std::end(reserved_), 0);
   // fast extension
   reserved_[7] |= 0x04u;
   // extended messaging
@@ -76,23 +71,25 @@ void BtHandshakeMessage::init()
 std::unique_ptr<BtHandshakeMessage>
 BtHandshakeMessage::create(const unsigned char* data, size_t dataLength)
 {
-  auto message = make_unique<BtHandshakeMessage>();
-  message->pstrlen_ = data[0];
-  memcpy(message->pstr_, &data[1], PSTR_LENGTH);
-  memcpy(message->reserved_, &data[20], RESERVED_LENGTH);
-  memcpy(message->infoHash_, &data[28], INFO_HASH_LENGTH);
-  memcpy(message->peerId_, &data[48], PEER_ID_LENGTH);
-  return message;
+  auto msg = make_unique<BtHandshakeMessage>();
+  msg->pstrlen_ = data[0];
+  std::copy_n(&data[1], msg->pstr_.size(), std::begin(msg->pstr_));
+  std::copy_n(&data[20], msg->reserved_.size(), std::begin(msg->reserved_));
+  std::copy_n(&data[28], msg->infoHash_.size(), std::begin(msg->infoHash_));
+  std::copy_n(&data[48], msg->peerId_.size(), std::begin(msg->peerId_));
+
+  return msg;
 }
 
 unsigned char* BtHandshakeMessage::createMessage()
 {
   auto msg = new unsigned char[MESSAGE_LENGTH];
-  msg[0] = pstrlen_;
-  memcpy(msg + 1, pstr_, PSTR_LENGTH);
-  memcpy(msg + 20, reserved_, RESERVED_LENGTH);
-  memcpy(msg + 28, infoHash_, INFO_HASH_LENGTH);
-  memcpy(msg + 48, peerId_, PEER_ID_LENGTH);
+  auto dst = msg;
+  *dst++ = pstrlen_;
+  dst = std::copy(std::begin(pstr_), std::end(pstr_), dst);
+  dst = std::copy(std::begin(reserved_), std::end(reserved_), dst);
+  dst = std::copy(std::begin(infoHash_), std::end(infoHash_), dst);
+  std::copy(std::begin(peerId_), std::end(peerId_), dst);
   return msg;
 }
 
@@ -101,8 +98,8 @@ size_t BtHandshakeMessage::getMessageLength() { return MESSAGE_LENGTH; }
 std::string BtHandshakeMessage::toString() const
 {
   return fmt("%s peerId=%s, reserved=%s", NAME,
-             util::percentEncode(peerId_, PEER_ID_LENGTH).c_str(),
-             util::toHex(reserved_, RESERVED_LENGTH).c_str());
+             util::percentEncode(peerId_.data(), peerId_.size()).c_str(),
+             util::toHex(reserved_.data(), reserved_.size()).c_str());
 }
 
 bool BtHandshakeMessage::isFastExtensionSupported() const
@@ -119,12 +116,12 @@ bool BtHandshakeMessage::isDHTEnabled() const { return reserved_[7] & 0x01u; }
 
 void BtHandshakeMessage::setInfoHash(const unsigned char* infoHash)
 {
-  memcpy(infoHash_, infoHash, INFO_HASH_LENGTH);
+  std::copy_n(infoHash, infoHash_.size(), std::begin(infoHash_));
 }
 
 void BtHandshakeMessage::setPeerId(const unsigned char* peerId)
 {
-  memcpy(peerId_, peerId, PEER_ID_LENGTH);
+  std::copy_n(peerId, peerId_.size(), std::begin(peerId_));
 }
 
 } // namespace aria2

+ 19 - 20
src/BtHandshakeMessage.h

@@ -37,21 +37,26 @@
 
 #include "SimpleBtMessage.h"
 
+#include <array>
+
+#include "BtConstants.h"
+
 namespace aria2 {
 
 class BtHandshakeMessage : public SimpleBtMessage {
 public:
-  static const size_t PSTR_LENGTH = 19;
-  static const unsigned char* BT_PSTR;
-  static const size_t RESERVED_LENGTH = 8;
-  static const size_t MESSAGE_LENGTH = 68;
+  constexpr static size_t PSTR_LENGTH = 19;
+  constexpr static size_t RESERVED_LENGTH = 8;
+  constexpr static size_t MESSAGE_LENGTH = 68;
+  const static unsigned char BT_PSTR[];
 
 private:
   uint8_t pstrlen_;
-  unsigned char* pstr_;
-  unsigned char* reserved_;
-  unsigned char* infoHash_;
-  unsigned char* peerId_;
+  std::array<unsigned char, PSTR_LENGTH> pstr_;
+  std::array<unsigned char, RESERVED_LENGTH> reserved_;
+  std::array<unsigned char, INFO_HASH_LENGTH> infoHash_;
+  std::array<unsigned char, PEER_ID_LENGTH> peerId_;
+
   void init();
 
 public:
@@ -66,17 +71,11 @@ public:
   static std::unique_ptr<BtHandshakeMessage> create(const unsigned char* data,
                                                     size_t dataLength);
 
-  virtual ~BtHandshakeMessage()
-  {
-    delete[] pstr_;
-    delete[] reserved_;
-    delete[] infoHash_;
-    delete[] peerId_;
-  }
+  virtual ~BtHandshakeMessage();
 
   static const uint8_t ID = INT8_MAX;
 
-  static const char NAME[];
+  const static char NAME[];
 
   virtual void doReceivedAction() CXX11_OVERRIDE{};
 
@@ -104,15 +103,15 @@ public:
 
   uint8_t getPstrlen() const { return pstrlen_; }
 
-  const unsigned char* getPstr() const { return pstr_; }
+  const unsigned char* getPstr() const { return pstr_.data(); }
 
-  const unsigned char* getReserved() const { return reserved_; }
+  const unsigned char* getReserved() const { return reserved_.data(); }
 
-  const unsigned char* getInfoHash() const { return infoHash_; }
+  const unsigned char* getInfoHash() const { return infoHash_.data(); }
 
   void setInfoHash(const unsigned char* infoHash);
 
-  const unsigned char* getPeerId() const { return peerId_; }
+  const unsigned char* getPeerId() const { return peerId_.data(); }
 
   void setPeerId(const unsigned char* peerId);
 };

+ 22 - 21
src/DHTGetPeersReplyMessage.cc

@@ -35,6 +35,7 @@
 #include "DHTGetPeersReplyMessage.h"
 
 #include <cstring>
+#include <array>
 
 #include "DHTNode.h"
 #include "DHTBucket.h"
@@ -81,24 +82,24 @@ std::unique_ptr<Dict> DHTGetPeersReplyMessage::getResponse()
   rDict->put(TOKEN, token_);
   // TODO want parameter
   if (!closestKNodes_.empty()) {
-    unsigned char buffer[DHTBucket::K * 38];
-    const int clen = bittorrent::getCompactLength(family_);
-    const int unit = clen + 20;
-    size_t offset = 0;
+    std::array<unsigned char, DHTBucket::K * 38> buffer;
+    const auto clen = bittorrent::getCompactLength(family_);
+    const auto unit = clen + DHT_ID_LENGTH;
+    auto last = std::begin(buffer);
     size_t k = 0;
-    for (auto i = std::begin(closestKNodes_), eoi = std::end(closestKNodes_);
-         i != eoi && k < DHTBucket::K; ++i) {
-      memcpy(buffer + offset, (*i)->getID(), DHT_ID_LENGTH);
-      unsigned char compact[COMPACT_LEN_IPV6];
-      int compactlen = bittorrent::packcompact(compact, (*i)->getIPAddress(),
-                                               (*i)->getPort());
+    for (auto i = std::begin(closestKNodes_);
+         i != std::end(closestKNodes_) && k < DHTBucket::K; ++i) {
+      std::array<unsigned char, COMPACT_LEN_IPV6> compact;
+      auto compactlen = bittorrent::packcompact(
+          compact.data(), (*i)->getIPAddress(), (*i)->getPort());
       if (compactlen == clen) {
-        memcpy(buffer + 20 + offset, compact, compactlen);
-        offset += unit;
+        last = std::copy_n((*i)->getID(), DHT_ID_LENGTH, last);
+        last = std::copy_n(std::begin(compact), compactlen, last);
         ++k;
       }
     }
-    rDict->put(family_ == AF_INET ? NODES : NODES6, String::g(buffer, offset));
+    rDict->put(family_ == AF_INET ? NODES : NODES6,
+               String::g(std::begin(buffer), last));
   }
   if (!values_.empty()) {
     // Limit the size of values list.  The maximum size of UDP datagram
@@ -123,16 +124,16 @@ std::unique_ptr<Dict> DHTGetPeersReplyMessage::getResponse()
     // doesn't specify the maximum size of token, reply message
     // template may get bigger than 395 bytes. So we use 25 as maximum
     // number of peer info that a message can carry.
-    static const size_t MAX_VALUES_SIZE = 25;
+    constexpr size_t MAX_VALUES_SIZE = 25;
     auto valuesList = List::g();
-    for (auto i = std::begin(values_), eoi = std::end(values_);
-         i != eoi && valuesList->size() < MAX_VALUES_SIZE; ++i) {
-      unsigned char compact[COMPACT_LEN_IPV6];
-      const int clen = bittorrent::getCompactLength(family_);
-      int compactlen = bittorrent::packcompact(compact, (*i)->getIPAddress(),
-                                               (*i)->getPort());
+    for (auto i = std::begin(values_);
+         i != std::end(values_) && valuesList->size() < MAX_VALUES_SIZE; ++i) {
+      std::array<unsigned char, COMPACT_LEN_IPV6> compact;
+      const auto clen = bittorrent::getCompactLength(family_);
+      auto compactlen = bittorrent::packcompact(
+          compact.data(), (*i)->getIPAddress(), (*i)->getPort());
       if (compactlen == clen) {
-        valuesList->append(String::g(compact, compactlen));
+        valuesList->append(String::g(compact.data(), compactlen));
       }
     }
     rDict->put(VALUES, std::move(valuesList));

+ 3 - 91
src/MultiDiskAdaptor.cc

@@ -412,98 +412,10 @@ ssize_t MultiDiskAdaptor::readData(unsigned char* data, size_t len,
 
 void MultiDiskAdaptor::writeCache(const WrDiskCacheEntry* entry)
 {
-  // Write cached data in 4KiB aligned offset. This reduces disk
-  // activity especially on Windows 7 NTFS.
-  unsigned char buf[16_k];
-  size_t buflen = 0;
-  size_t buffoffset = 0;
-  auto& dataSet = entry->getDataSet();
-  if (dataSet.empty()) {
-    return;
-  }
-  auto dent =
-      findFirstDiskWriterEntry(diskWriterEntries_, (*dataSet.begin())->goff),
-       eod = diskWriterEntries_.cend();
-  auto i = std::begin(dataSet), eoi = std::end(dataSet);
-  size_t celloff = 0;
-  for (; dent != eod; ++dent) {
-    int64_t lstart = 0, lp = 0;
-    auto& fent = (*dent)->getFileEntry();
-    if (fent->getLength() == 0) {
-      continue;
-    }
-    for (; i != eoi;) {
-      if (std::max(fent->getOffset(),
-                   static_cast<int64_t>((*i)->goff + celloff)) <
-          std::min(fent->getLastOffset(),
-                   static_cast<int64_t>((*i)->goff + (*i)->len))) {
-        openIfNot((*dent).get(), &DiskWriterEntry::openFile);
-        if (!(*dent)->isOpen()) {
-          throwOnDiskWriterNotOpened((*dent).get(), (*i)->goff + celloff);
-        }
-      }
-      else {
-        A2_LOG_DEBUG(fmt("%s Cache flush loff=%" PRId64 ", len=%lu",
-                         fent->getPath().c_str(), lstart,
-                         static_cast<unsigned long>(buflen - buffoffset)));
-        (*dent)->getDiskWriter()->writeData(buf + buffoffset,
-                                            buflen - buffoffset, lstart);
-        buflen = buffoffset = 0;
-        break;
-      }
-      int64_t loff = fent->gtoloff((*i)->goff + celloff);
-      if (static_cast<int64_t>(lstart + buflen) < loff) {
-        A2_LOG_DEBUG(fmt("%s Cache flush loff=%" PRId64 ", len=%lu",
-                         fent->getPath().c_str(), lstart,
-                         static_cast<unsigned long>(buflen - buffoffset)));
-        (*dent)->getDiskWriter()->writeData(buf + buffoffset,
-                                            buflen - buffoffset, lstart);
-        lstart = lp = loff;
-        buflen = buffoffset = 0;
-      }
-      // If the position of the cache data is not aligned, offset
-      // buffer so that next write can be aligned.
-      if (buflen == 0) {
-        buflen = buffoffset = loff & 0xfff;
-      }
-      assert((*i)->len > celloff);
-      for (;;) {
-        size_t wlen = std::min(static_cast<int64_t>((*i)->len - celloff),
-                               fent->getLength() - lp);
-        wlen = std::min(wlen, sizeof(buf) - buflen);
-        memcpy(buf + buflen, (*i)->data + (*i)->offset + celloff, wlen);
-        buflen += wlen;
-        celloff += wlen;
-        lp += wlen;
-        if (lp == fent->getLength() || buflen == sizeof(buf)) {
-          A2_LOG_DEBUG(fmt("%s Cache flush loff=%" PRId64 ", len=%lu",
-                           fent->getPath().c_str(), lstart,
-                           static_cast<unsigned long>(buflen - buffoffset)));
-          (*dent)->getDiskWriter()->writeData(buf + buffoffset,
-                                              buflen - buffoffset, lstart);
-          lstart += buflen - buffoffset;
-          lp = lstart;
-          buflen = buffoffset = 0;
-        }
-        if (lp == fent->getLength() || celloff == (*i)->len) {
-          break;
-        }
-      }
-      if (celloff == (*i)->len) {
-        ++i;
-        celloff = 0;
-      }
-    }
-    if (i == eoi) {
-      A2_LOG_DEBUG(fmt("%s Cache flush loff=%" PRId64 ", len=%lu",
-                       fent->getPath().c_str(), lstart,
-                       static_cast<unsigned long>(buflen - buffoffset)));
-      (*dent)->getDiskWriter()->writeData(buf + buffoffset, buflen - buffoffset,
-                                          lstart);
-      break;
-    }
+  for (auto& d : entry->getDataSet()) {
+    A2_LOG_DEBUG(fmt("Cache flush goff=%" PRId64 ", len=%lu", d->goff, d->len));
+    writeData(d->data + d->offset, d->len, d->goff);
   }
-  assert(i == eoi);
 }
 
 bool MultiDiskAdaptor::fileExists()

+ 8 - 3
src/NsCookieParser.cc

@@ -69,9 +69,14 @@ std::unique_ptr<Cookie> parseNsCookie(const std::string& cookieStr,
     return nullptr;
   }
   int64_t expiryTime;
-  if (!util::parseLLIntNoThrow(expiryTime,
-                               std::string(vs[4].first, vs[4].second))) {
-    return nullptr;
+  {
+    // chrome extension uses subsecond resolution for expiry time.
+    double expiryTimeDouble;
+    if (!util::parseDoubleNoThrow(expiryTimeDouble,
+                                  std::string(vs[4].first, vs[4].second))) {
+      return nullptr;
+    }
+    expiryTime = static_cast<int64_t>(expiryTimeDouble);
   }
   if (std::numeric_limits<time_t>::max() < expiryTime) {
     expiryTime = std::numeric_limits<time_t>::max();

+ 7 - 2
src/Sqlite3CookieParserImpl.cc

@@ -59,8 +59,13 @@ Sqlite3ChromiumCookieParser::~Sqlite3ChromiumCookieParser() {}
 
 const char* Sqlite3ChromiumCookieParser::getQuery() const
 {
-  return "SELECT host_key, path, secure, expires_utc, name, value, "
-         "last_access_utc"
+  // chrome's time is microsecond resolution, and its epoc is Jan 1
+  // 00:00:00 +0000 1601, so we have to convert it to second from UNIX
+  // epoc.  11644473600 is the second between chrome's epoc and UNIX
+  // epoc.  e.g., date +%s -d 'Jan 1 00:00:00 +0000 1601'
+  return "SELECT host_key, path, secure, expires_utc / 1000000 - 11644473600 "
+         "as expires_utc, name, value, "
+         "last_access_utc / 1000000 - 11644473600 as last_access_utc"
          " FROM cookies";
 }
 

+ 27 - 0
src/util.cc

@@ -596,6 +596,33 @@ bool parseLLIntNoThrow(int64_t& res, const std::string& s, int base)
   }
 }
 
+bool parseDoubleNoThrow(double& res, const std::string& s)
+{
+  if (s.empty()) {
+    return false;
+  }
+
+  errno = 0;
+  char* endptr;
+  auto d = strtod(s.c_str(), &endptr);
+
+  if (errno == ERANGE) {
+    return false;
+  }
+
+  if (endptr != s.c_str() + s.size()) {
+    for (auto i = std::begin(s) + (endptr - s.c_str()); i != std::end(s); ++i) {
+      if (!isspace(*i)) {
+        return false;
+      }
+    }
+  }
+
+  res = d;
+
+  return true;
+}
+
 SegList<int> parseIntSegments(const std::string& src)
 {
   SegList<int> sgl;

+ 4 - 0
src/util.h

@@ -280,6 +280,10 @@ bool parseUIntNoThrow(uint32_t& res, const std::string& s, int base = 10);
 
 bool parseLLIntNoThrow(int64_t& res, const std::string& s, int base = 10);
 
+// Parses |s| as floating point number, and stores the result into
+// |res|.  This function returns true if it succeeds.
+bool parseDoubleNoThrow(double& res, const std::string& s);
+
 SegList<int> parseIntSegments(const std::string& src);
 
 // Parses string which specifies the range of piece index for higher

+ 2 - 2
test/CookieStorageTest.cc

@@ -328,10 +328,10 @@ void CookieStorageTest::testLoad()
   c = cookies[3];
   CPPUNIT_ASSERT_EQUAL(std::string("TAX"), c->getName());
   CPPUNIT_ASSERT_EQUAL(std::string("1000"), c->getValue());
-  CPPUNIT_ASSERT((time_t)INT32_MAX <= c->getExpiryTime());
+  CPPUNIT_ASSERT_EQUAL((time_t)1463304912, c->getExpiryTime());
   CPPUNIT_ASSERT(c->getPersistent());
   CPPUNIT_ASSERT_EQUAL(std::string("/"), c->getPath());
-  CPPUNIT_ASSERT_EQUAL(std::string("overflow"), c->getDomain());
+  CPPUNIT_ASSERT_EQUAL(std::string("something"), c->getDomain());
   CPPUNIT_ASSERT(!c->getSecure());
 }
 

+ 2 - 2
test/NsCookieParserTest.cc

@@ -69,9 +69,9 @@ void NsCookieParserTest::testParse()
   c = cookies[3].get();
   CPPUNIT_ASSERT_EQUAL(std::string("TAX"), c->getName());
   CPPUNIT_ASSERT_EQUAL(std::string("1000"), c->getValue());
-  CPPUNIT_ASSERT((time_t)INT32_MAX <= c->getExpiryTime());
+  CPPUNIT_ASSERT_EQUAL((time_t)1463304912, c->getExpiryTime());
   CPPUNIT_ASSERT(c->getPersistent());
-  CPPUNIT_ASSERT_EQUAL(std::string("overflow"), c->getDomain());
+  CPPUNIT_ASSERT_EQUAL(std::string("something"), c->getDomain());
   CPPUNIT_ASSERT(c->getHostOnly());
   CPPUNIT_ASSERT_EQUAL(std::string("/"), c->getPath());
   CPPUNIT_ASSERT(!c->getSecure());

+ 19 - 0
test/UtilTest2.cc

@@ -66,6 +66,7 @@ class UtilTest2 : public CppUnit::TestFixture {
   CPPUNIT_TEST(testInPrivateAddress);
   CPPUNIT_TEST(testSecfmt);
   CPPUNIT_TEST(testTlsHostnameMatch);
+  CPPUNIT_TEST(testParseDoubleNoThrow);
   CPPUNIT_TEST_SUITE_END();
 
 private:
@@ -115,6 +116,7 @@ public:
   void testInPrivateAddress();
   void testSecfmt();
   void testTlsHostnameMatch();
+  void testParseDoubleNoThrow();
 };
 
 CPPUNIT_TEST_SUITE_REGISTRATION(UtilTest2);
@@ -978,4 +980,21 @@ void UtilTest2::testTlsHostnameMatch()
   CPPUNIT_ASSERT(!util::tlsHostnameMatch("xn--*.a.b", "xn--c.a.b"));
 }
 
+void UtilTest2::testParseDoubleNoThrow()
+{
+  double n;
+
+  CPPUNIT_ASSERT(util::parseDoubleNoThrow(n, " 123 "));
+  CPPUNIT_ASSERT_EQUAL(123., n);
+
+  CPPUNIT_ASSERT(util::parseDoubleNoThrow(n, "3.14"));
+  CPPUNIT_ASSERT_EQUAL(3.14, n);
+
+  CPPUNIT_ASSERT(util::parseDoubleNoThrow(n, "-3.14"));
+  CPPUNIT_ASSERT_EQUAL(-3.14, n);
+
+  CPPUNIT_ASSERT(!util::parseDoubleNoThrow(n, ""));
+  CPPUNIT_ASSERT(!util::parseDoubleNoThrow(n, "123x"));
+}
+
 } // namespace aria2

BIN
test/chromium_cookies.sqlite


+ 2 - 2
test/nscookietest.txt

@@ -5,5 +5,5 @@ expired	FALSE	/	FALSE	1000	user	me
 
 192.168.0.1	TRUE	/cgi-bin	FALSE	0	passwd	secret
 badformat
-overflow	FALSE	/	FALSE	9223372036854775807	TAX	1000
-.example.org	TRUE	/	FALSE	2147483647	novalue	
+something	FALSE	/	FALSE	1463304912.5	TAX	1000
+.example.org	TRUE	/	FALSE	2147483647.123	novalue	

Some files were not shown because too many files changed in this diff