Tatsuhiro Tsujikawa 19 년 전
부모
커밋
5a8710676d

+ 6 - 0
ChangeLog

@@ -1,3 +1,9 @@
+2006-02-19  Tatsuhiro Tsujikawa  <tujikawa at rednoah dot com>
+
+	* Fixed timeout bug in AbstractCommand
+	* Added totalSize entry to .aria2 file. No compatibility for
+	version 0.1.0's.
+	
 2006-02-18  Tatsuhiro Tsujikawa  <tujikawa at rednoah dot com>
 
 	* Added --enable-ssl option to configure script.

+ 56 - 12
src/AbstractCommand.cc

@@ -28,13 +28,14 @@
 #include "message.h"
 #include "SleepCommand.h"
 
-#define TIMEOUT_SEC 5
+#define TIMEOUT_SEC 60
 
 AbstractCommand::AbstractCommand(int cuid, Request* req, DownloadEngine* e, Socket* s):
   Command(cuid), req(req), e(e), checkSocketIsReadable(false), checkSocketIsWritable(false) {
+
   if(s != NULL) {
     socket = new Socket(*s);
-    e->addSocketForReadCheck(socket);
+    setReadCheckSocket(socket);
   } else {
     socket = NULL;
   }
@@ -43,8 +44,8 @@ AbstractCommand::AbstractCommand(int cuid, Request* req, DownloadEngine* e, Sock
 }
 
 AbstractCommand::~AbstractCommand() {
-  e->deleteSocketForReadCheck(socket);
-  e->deleteSocketForWriteCheck(socket);
+  setReadCheckSocket(NULL);
+  setWriteCheckSocket(NULL);
   if(socket != NULL) {
     delete(socket);
   }
@@ -58,6 +59,7 @@ bool AbstractCommand::isTimeoutDetected() {
   struct timeval now;
   gettimeofday(&now, NULL);
   if(checkPoint.tv_sec == 0 && checkPoint.tv_usec == 0) {
+    checkPoint = now;
     return false;
   } else {
     long long int elapsed = Util::difftv(now, checkPoint);
@@ -71,12 +73,11 @@ bool AbstractCommand::isTimeoutDetected() {
 
 bool AbstractCommand::execute() {
   try {
-    if(checkSocketIsReadable && !socket->isReadable(0)
-       || checkSocketIsWritable && !socket->isWritable(0)) {
+    if(checkSocketIsReadable && !readCheckTarget->isReadable(0)
+       || checkSocketIsWritable && !writeCheckTarget->isWritable(0)) {
       if(isTimeoutDetected()) {
 	throw new DlRetryEx(EX_TIME_OUT);
       }
-      updateCheckPoint();
       e->commands.push(this);
       return false;
     }
@@ -93,17 +94,16 @@ bool AbstractCommand::execute() {
     return executeInternal(seg);
   } catch(DlAbortEx* err) {
     e->logger->error(MSG_DOWNLOAD_ABORTED, err, cuid);
-    onError(err);
+    onAbort(err);
     delete(err);
     req->resetUrl();
     return true;
   } catch(DlRetryEx* err) {
     e->logger->error(MSG_RESTARTING_DOWNLOAD, err, cuid);
-    onError(err);
     delete(err);
     //req->resetUrl();
-    req->addRetryCount();
-    if(req->noMoreRetry()) {
+    req->addTryCount();
+    if(req->noMoreTry()) {
       e->logger->error(MSG_MAX_RETRY, cuid);
       return true;
     } else {
@@ -123,5 +123,49 @@ bool AbstractCommand::prepareForRetry(int wait) {
   return true;
 }
 
-void AbstractCommand::onError(Exception* e) {
+void AbstractCommand::onAbort(Exception* e) {
+}
+
+void AbstractCommand::setReadCheckSocket(Socket* socket) {
+  if(socket == NULL) {
+    if(checkSocketIsReadable) {
+      e->deleteSocketForReadCheck(readCheckTarget);
+      checkSocketIsReadable = false;
+      readCheckTarget = NULL;
+    }
+  } else {
+    if(checkSocketIsReadable) {
+      if(readCheckTarget != socket) {
+	e->deleteSocketForReadCheck(readCheckTarget);
+	e->addSocketForReadCheck(socket);
+	readCheckTarget = socket;
+      }
+    } else {
+      e->addSocketForReadCheck(socket);
+      checkSocketIsReadable = true;
+      readCheckTarget = socket;
+    }
+  }
+}
+
+void AbstractCommand::setWriteCheckSocket(Socket* socket) {
+  if(socket == NULL) {
+    if(checkSocketIsWritable) {
+      e->deleteSocketForWriteCheck(writeCheckTarget);
+      checkSocketIsWritable = false;
+      writeCheckTarget = NULL;
+    }
+  } else {
+    if(checkSocketIsWritable) {
+      if(writeCheckTarget != socket) {
+	e->deleteSocketForWriteCheck(writeCheckTarget);
+	e->addSocketForWriteCheck(socket);
+	writeCheckTarget = socket;
+      }
+    } else {
+      e->addSocketForWriteCheck(socket);
+      checkSocketIsWritable = true;
+      writeCheckTarget = socket;
+    }
+  }
 }

+ 10 - 3
src/AbstractCommand.h

@@ -37,11 +37,18 @@ protected:
   Request* req;
   DownloadEngine* e;
   Socket* socket;
-  bool checkSocketIsReadable;
-  bool checkSocketIsWritable;
+
   virtual bool prepareForRetry(int wait);
-  virtual void onError(Exception* e);
+  virtual void onAbort(Exception* e);
   virtual bool executeInternal(Segment segment) = 0;
+
+  void setReadCheckSocket(Socket* socket);
+  void setWriteCheckSocket(Socket* socket);
+private:
+  bool checkSocketIsReadable;
+  bool checkSocketIsWritable;
+  Socket* readCheckTarget;
+  Socket* writeCheckTarget;
 public:
   AbstractCommand(int cuid, Request* req, DownloadEngine* e, Socket* s= NULL);
   virtual ~AbstractCommand();

+ 6 - 3
src/DownloadCommand.cc

@@ -27,9 +27,7 @@
 #include "InitiateConnectionCommandFactory.h"
 #include "message.h"
 
-DownloadCommand::DownloadCommand(int cuid, Request* req, DownloadEngine* e, Socket* s):AbstractCommand(cuid, req, e, s) {
-  AbstractCommand::checkSocketIsReadable = true;
-}
+DownloadCommand::DownloadCommand(int cuid, Request* req, DownloadEngine* e, Socket* s):AbstractCommand(cuid, req, e, s) {}
 
 DownloadCommand::~DownloadCommand() {}
 
@@ -84,3 +82,8 @@ bool DownloadCommand::prepareForNextSegment() {
     return true;
   }
 }
+
+void DownloadCommand::onAbort(Exception* ex) {
+  e->logger->debug(MSG_UNREGISTER_CUID, cuid);
+  e->segmentMan->unregisterId(cuid);
+}

+ 1 - 1
src/DownloadCommand.h

@@ -34,7 +34,7 @@ protected:
 
   bool prepareForRetry(int wait);
   bool prepareForNextSegment();
-
+  void onAbort(Exception* ex);
 public:
   DownloadCommand(int cuid, Request* req, DownloadEngine* e, Socket* s);
   virtual ~DownloadCommand();

+ 23 - 21
src/DownloadEngine.cc

@@ -51,30 +51,31 @@ void DownloadEngine::run() {
     }
     if(!noWait && !commands.empty()) {
       waitData();
-      long long int dlSize = segmentMan->getDownloadedSize();
+    }
+    noWait = false;
 
-      struct timeval now;
-      gettimeofday(&now, NULL);
-      if(cp.tv_sec == 0 && cp.tv_usec == 0) {
+    long long int dlSize = segmentMan->getDownloadedSize();
+    struct timeval now;
+    gettimeofday(&now, NULL);
+    if(cp.tv_sec == 0 && cp.tv_usec == 0) {
+      cp = now;
+      psize = dlSize;
+    } else {
+      long long int elapsed = Util::difftv(now, cp);
+      if(elapsed >= 500000) {
+	int nspeed = (int)((dlSize-psize)/(elapsed/1000000.0));
+	speed = (nspeed+speed)/2;
 	cp = now;
 	psize = dlSize;
-      } else {
-	long long int elapsed = Util::difftv(now, cp);
-	if(elapsed >= 500000) {
-	  int nspeed = (int)((dlSize-psize)/(elapsed/1000000.0));
-	  speed = (nspeed+speed)/2;
-	  cp = now;
-	  psize = dlSize;
-	  cout << "\r                                                                            ";
-	  cout << "\rProgress " << Util::llitos(dlSize, true) << " Bytes/" <<
-	    Util::llitos(segmentMan->totalSize, true) << " Bytes " <<
-	    (segmentMan->totalSize == 0 ? 0 : (dlSize*100)/segmentMan->totalSize) << "% " <<
-	    speed/1000.0 << "KB/s " <<
-	    "(" << commands.size() << " connections)" << flush;
-	}
+	cout << "\r                                                                            ";
+	cout << "\rProgress " << Util::llitos(dlSize, true) << " Bytes/" <<
+	  Util::llitos(segmentMan->totalSize, true) << " Bytes " <<
+	  (segmentMan->totalSize == 0 ? 0 : (dlSize*100)/segmentMan->totalSize) << "% " <<
+	  speed/1000.0 << "KB/s " <<
+	  "(" << commands.size() << " connections)" << flush;
       }
     }
-    noWait = false;
+
   }
   segmentMan->removeIfFinished();
   diskWriter->closeFile();
@@ -99,7 +100,7 @@ void DownloadEngine::waitData() {
   fd_set wfds;
   struct timeval tv;
   int retval;
-  
+
   FD_ZERO(&rfds);
   FD_ZERO(&wfds);
   int max = 0;
@@ -110,6 +111,7 @@ void DownloadEngine::waitData() {
     }
   }
   for(vector<Socket*>::iterator itr = wsockets.begin(); itr != wsockets.end(); itr++) {
+
     FD_SET((*itr)->getSockfd(), &wfds);
     if(max < (*itr)->getSockfd()) {
       max = (*itr)->getSockfd();
@@ -118,7 +120,7 @@ void DownloadEngine::waitData() {
   tv.tv_sec = 1;
   tv.tv_usec = 0;
 
-  retval = select(max+1, &rfds, &wfds, NULL, &tv);
+  retval = select(max+1, &rfds, /*&wfds*/NULL, NULL, &tv);
 }
 
 bool DownloadEngine::addSocket(vector<Socket*>& sockets, Socket* socket) {

+ 35 - 53
src/HttpConnection.cc

@@ -26,15 +26,16 @@
 #include "Base64.h"
 #include "message.h"
 
-HttpConnection::HttpConnection(int cuid, Socket* socket, const Option* op, Logger* logger):cuid(cuid), socket(socket),option(op),logger(logger) {}
+HttpConnection::HttpConnection(int cuid, const Socket* socket, const Request* req, const Option* op, const Logger* logger):
+  cuid(cuid), socket(socket), req(req), option(op), logger(logger) {}
 
-void HttpConnection::sendRequest(const Request* req, const Segment& segment) {
-  string request = createRequest(req, segment);
+void HttpConnection::sendRequest(const Segment& segment) const {
+  string request = createRequest(segment);
   logger->info(MSG_SENDING_HTTP_REQUEST, cuid, request.c_str());
   socket->writeData(request.c_str(), request.size());
 }
 
-void HttpConnection::sendProxyRequest(const Request* req) {
+void HttpConnection::sendProxyRequest() const {
   string request = string("CONNECT ")+req->getHost()+":"+Util::llitos(req->getPort())+
     string(" HTTP/1.1\r\n")+
     "Host: "+getHost(req->getHost(), req->getPort())+"\r\n";
@@ -47,19 +48,19 @@ void HttpConnection::sendProxyRequest(const Request* req) {
   socket->writeData(request.c_str(), request.size());
 }
 
-string HttpConnection::getHost(const string& host, int port) {
+string HttpConnection::getHost(const string& host, int port) const {
   return host+(port == 80 || port == 443 ? "" : ":"+Util::llitos(port));
 }
 
-string HttpConnection::createRequest(const Request* req, const Segment& segment) {
+string HttpConnection::createRequest(const Segment& segment) const {
   string request = string("GET ")+
-    // some servers cannot permit absolute URI as requet URI.
-    //req->getCurrentUrl()+
-    (req->getDir() == "/" ? "/" : req->getDir()+"/")+req->getFile()+
+    (req->getProtocol() == "ftp" ?
+     req->getCurrentUrl() :
+     ((req->getDir() == "/" ? "/" : req->getDir()+"/")+req->getFile()))+
     string(" HTTP/1.1\r\n")+
     "User-Agent: aria2\r\n"+
     "Connection: close\r\n"+
-    "Accept: */*\r\n"+
+    "Accept: */*\r\n"+        /* */
     "Host: "+getHost(req->getHost(), req->getPort())+"\r\n"+
     "Pragma: no-cache\r\n"+
     "Cache-Control: no-cache\r\n";
@@ -87,48 +88,31 @@ string HttpConnection::createRequest(const Request* req, const Segment& segment)
 }
 
 int HttpConnection::receiveResponse(HttpHeader& headers) {
-  string header;
-  char* buf = NULL;
-  try {
-    // read a line of the header      
-    int bufSize = 256;
-    // TODO limit iteration count
-    while(1) {
-      bufSize += 256;
-      if(bufSize > 2048) {
-	throw new DlAbortEx(EX_INVALID_HEADER);
-      }
-      buf = new char[bufSize];
-      int tbufSize = bufSize-1;
-      socket->peekData(buf, tbufSize);
-      if(tbufSize > 0) {
-	buf[tbufSize] = '\0';
-      }
-      header = buf;
-      char* p;
-      if((p = strstr(buf, "\r\n")) == buf) {
-	throw new DlAbortEx(EX_NO_HEADER);
-      }
-      if((p = strstr(buf, "\r\n\r\n")) != NULL) {
-	*(p+4) = '\0';
-	header = buf;
-	tbufSize = header.size();
-	socket->readData(buf, tbufSize);
-	delete [] buf;
-	buf = NULL;
-	break;
+  char buf[512];
+  while(socket->isReadable(0)) {
+    int size = sizeof(buf)-1;
+    socket->peekData(buf, size);
+    buf[size] = '\0';
+    int hlenTemp = header.size();
+    header += buf;
+    string::size_type p;
+    if((p = header.find("\r\n\r\n")) == string::npos) {
+      socket->readData(buf, size);
+    } else {
+      if(Util::endsWith(header, "\r\n\r\n")) {
+	socket->readData(buf, size);
       } else {
-	delete [] buf;
-	buf = NULL;
+	header.erase(p+4);
+	size = p+4-hlenTemp;
+	socket->readData(buf, size);
       }
+      break;
     }
-  } catch(Exception* e) {
-    if(buf != NULL) {
-      delete [] buf;
-    }
-    throw;
   }
-  // OK, i got all headers.
+  if(!Util::endsWith(header, "\r\n\r\n")) {
+    return 0;
+  }
+  // OK, we got all headers.
   logger->info(MSG_RECEIVE_RESPONSE, cuid, header.c_str());
   string::size_type p, np;
   p = np = 0;
@@ -145,19 +129,17 @@ int HttpConnection::receiveResponse(HttpHeader& headers) {
     p = np+2;
     pair<string, string> hp;
     Util::split(hp, line, ':');
-    HttpHeader::value_type nh(hp.first, hp.second);
-    headers.insert(nh);
+    headers.put(hp.first, hp.second);
   }
-  // TODO rewrite this using strtoul
   return (int)strtol(status.c_str(), NULL, 10);
 }
 
-bool HttpConnection::useProxy() {
+bool HttpConnection::useProxy() const {
   return option->defined("http_proxy_enabled") &&
     option->get("http_proxy_enabled") == "true";
 }
 
-bool HttpConnection::useProxyAuth() {
+bool HttpConnection::useProxyAuth() const {
   return option->defined("http_proxy_auth_enabled") &&
     option->get("http_proxy_auth_enabled") == "true";
 }

+ 13 - 12
src/HttpConnection.h

@@ -27,30 +27,31 @@
 #include "Request.h"
 #include "Option.h"
 #include "Logger.h"
+#include "HttpHeader.h"
 #include <map>
 #include <string>
 
 using namespace std;
 
-typedef multimap<string, string> HttpHeader;
+//typedef multimap<string, string> HttpHeader;
 
 class HttpConnection {
 private:
-  string getHost(const string& host, int port);
-  string createRequest(const Request* req, const Segment& segment);
-  bool useProxy();
-  bool useProxyAuth();
-  bool useBasicAuth();
+  string getHost(const string& host, int port) const;
+  string createRequest(const Segment& segment) const;
+  bool useProxy() const;
+  bool useProxyAuth() const;
   int cuid;
-  Socket* socket;
+  const Socket* socket;
+  const Request* req;
   const Option* option;
-  Logger* logger;
+  const Logger* logger;
+  string header;
 public:
+  HttpConnection(int cuid, const Socket* socket, const Request* req, const Option* op, const Logger* logger);
 
-  HttpConnection(int cuid, Socket* socket, const Option* op, Logger* logger);
-
-  void sendRequest(const Request* req, const Segment& segment);
-  void sendProxyRequest(const Request* req);
+  void sendRequest(const Segment& segment) const;
+  void sendProxyRequest() const;
   int receiveResponse(HttpHeader& headers);
 };
 

+ 0 - 3
src/HttpDownloadCommand.h

@@ -37,13 +37,10 @@ class HttpDownloadCommand:public DownloadCommand {
 private:
   map<string, TransferEncoding*> transferEncodings;
 public:
-  int cuid;
-
   HttpDownloadCommand(int cuid, Request* req, DownloadEngine* e, Socket* s);
   ~HttpDownloadCommand();
 
   TransferEncoding* getTransferEncoding(string transferEncoding);
-
 };
 
 #endif // _D_HTTP_DOWNLOAD_COMMAND_H_

+ 5 - 6
src/HttpProxyRequestCommand.cc

@@ -24,17 +24,16 @@
 #include "HttpProxyResponseCommand.h"
 
 HttpProxyRequestCommand::HttpProxyRequestCommand(int cuid, Request* req, DownloadEngine* e, Socket* s):AbstractCommand(cuid, req, e, s) {
-  AbstractCommand::checkSocketIsWritable = true;
-  e->deleteSocketForReadCheck(socket);
-  e->addSocketForWriteCheck(socket);
+  setReadCheckSocket(NULL);
+  setWriteCheckSocket(socket);
 }
 
 HttpProxyRequestCommand::~HttpProxyRequestCommand() {}
 
 bool HttpProxyRequestCommand::executeInternal(Segment segment) {
-  socket->setNonBlockingMode();
-  HttpConnection httpConnection(cuid, socket, e->option, e->logger);
-  httpConnection.sendProxyRequest(req);
+  socket->setBlockingMode();
+  HttpConnection httpConnection(cuid, socket, req, e->option, e->logger);
+  httpConnection.sendProxyRequest();
 
   HttpProxyResponseCommand* command = new HttpProxyResponseCommand(cuid, req, e, socket);
   e->commands.push(command);

+ 10 - 5
src/HttpProxyResponseCommand.cc

@@ -22,19 +22,24 @@
 #include "HttpProxyResponseCommand.h"
 #include "HttpRequestCommand.h"
 #include "DlRetryEx.h"
-#include "HttpConnection.h"
 #include "message.h"
 
 HttpProxyResponseCommand::HttpProxyResponseCommand(int cuid, Request* req, DownloadEngine* e, Socket* s):AbstractCommand(cuid, req, e, s) {
-  AbstractCommand::checkSocketIsReadable = true;
+  http = new HttpConnection(cuid, socket, req, e->option, e->logger);
 }
 
-HttpProxyResponseCommand::~HttpProxyResponseCommand() {}
+HttpProxyResponseCommand::~HttpProxyResponseCommand() {
+  delete http;
+}
 
 bool HttpProxyResponseCommand::executeInternal(Segment segment) {
   HttpHeader headers;
-  HttpConnection httpConnection(cuid, socket, e->option, e->logger);
-  int status = httpConnection.receiveResponse(headers);
+  int status = http->receiveResponse(headers);
+  if(status == 0) {
+    // we didn't receive all of headers yet.
+    e->commands.push(this);
+    return false;
+  }
   if(status != 200) {
     throw new DlRetryEx(EX_PROXY_CONNECTION_FAILED);
   }

+ 3 - 0
src/HttpProxyResponseCommand.h

@@ -23,8 +23,11 @@
 #define _D_HTTP_PROXY_RESPONSE_COMMAND_H_
 
 #include "AbstractCommand.h"
+#include "HttpConnection.h"
 
 class HttpProxyResponseCommand : public AbstractCommand {
+private:
+  HttpConnection* http;
 protected:
   bool executeInternal(Segment segment);
 public:

+ 5 - 6
src/HttpRequestCommand.cc

@@ -27,22 +27,21 @@
 #include "HttpConnection.h"
 
 HttpRequestCommand::HttpRequestCommand(int cuid, Request* req, DownloadEngine* e, Socket* s):AbstractCommand(cuid, req, e, s) {
-  AbstractCommand::checkSocketIsWritable = true;
-  e->deleteSocketForReadCheck(socket);
-  e->addSocketForWriteCheck(socket);
+  setReadCheckSocket(NULL);
+  setWriteCheckSocket(socket);
 }
 
 HttpRequestCommand::~HttpRequestCommand() {}
 
 bool HttpRequestCommand::executeInternal(Segment seg) {
-  socket->setNonBlockingMode();
+  socket->setBlockingMode();
   if(req->getProtocol() == "https") {
     socket->initiateSecureConnection();
   }
-  HttpConnection httpConnection(cuid, socket, e->option, e->logger);
+  HttpConnection http(cuid, socket, req, e->option, e->logger);
   // set seg to request in order to remember the request range
   req->seg = seg;
-  httpConnection.sendRequest(req, seg);
+  http.sendRequest(seg);
 
   HttpResponseCommand* command = new HttpResponseCommand(cuid, req, e, socket);
   e->commands.push(command);

+ 27 - 23
src/HttpResponseCommand.cc

@@ -28,10 +28,12 @@
 
 HttpResponseCommand::HttpResponseCommand(int cuid, Request* req, DownloadEngine* e, Socket* s):
   AbstractCommand(cuid, req, e, s) {
-  AbstractCommand::checkSocketIsReadable = true;
+  http = new HttpConnection(cuid, socket, req, e->option, e->logger);
 }
 
-HttpResponseCommand::~HttpResponseCommand() {}
+HttpResponseCommand::~HttpResponseCommand() {
+  delete http;
+}
 
 bool HttpResponseCommand::executeInternal(Segment seg) {
   if(SEGMENT_EQUAL(req->seg, seg) == false) {
@@ -39,26 +41,30 @@ bool HttpResponseCommand::executeInternal(Segment seg) {
     return prepareForRetry(0);
   }
   HttpHeader headers;
-  HttpConnection httpConnection(cuid, socket, e->option, e->logger);
-  int status = httpConnection.receiveResponse(headers);
+  int status = http->receiveResponse(headers);
+  if(status == 0) {
+    // didn't receive header fully
+    e->commands.push(this);
+    return false;
+  }
   // check HTTP status number
   checkResponse(status, seg);
   retrieveCookie(headers);
   // check whether Location header exists. If it does, update request object
   // with redirected URL.
   // then establish a connection to the new host and port
-  if(headers.count("Location")) {
-    return handleRedirect((*headers.find("Location")).second, headers);
+  if(headers.defined("Location")) {
+    return handleRedirect(headers.getFirst("Location"), headers);
   }
   if(!e->segmentMan->downloadStarted) {
     string transferEncoding;
-    headers.find("Transfer-Encoding");
-    if(headers.count("Transfer-Encoding")) {
-      return handleOtherEncoding((*headers.find("Transfer-Encoding")).second, headers);
+    if(headers.defined("Transfer-Encoding")) {
+      return handleOtherEncoding(headers.getFirst("Transfer-Encoding"), headers);
     } else {
       return handleDefaultEncoding(headers);
     }
   } else {
+    // TODO we must check headers["size"] == e->segmentMan->totalSize here
     if(req->getFile() != e->segmentMan->filename) {
       throw new DlAbortEx(EX_FILENAME_MISMATCH, req->getFile().c_str(), e->segmentMan->filename.c_str());
     }
@@ -83,28 +89,25 @@ bool HttpResponseCommand::handleRedirect(string url, const HttpHeader& headers)
 }
 
 bool HttpResponseCommand::handleDefaultEncoding(const HttpHeader& headers) {
-  long long int size;
-  if(headers.count("Content-Length") == 0) {
-    size = 0;
-  } else {
-    size = STRTOLL((*headers.find("Content-Length")).second.c_str());
-    if(size == LONG_LONG_MAX || size == LONG_LONG_MIN) {
-      throw new DlAbortEx(EX_TOO_LARGE_FILE, size);
-    }
+  long long int size = headers.getFirstAsLLInt("Content-Length");
+  if(size == LONG_LONG_MAX || size == LONG_LONG_MIN || size < 0) {
+    throw new DlAbortEx(EX_TOO_LARGE_FILE, size);
   }
   e->segmentMan->isSplittable = !(size == 0);
   e->segmentMan->filename = req->getFile();
-  e->segmentMan->totalSize = size;
   bool segFileExists = e->segmentMan->segmentFileExists();
   e->segmentMan->downloadStarted = true;
   if(segFileExists) {
-    e->segmentMan->load();
+
     e->diskWriter->openExistingFile(e->segmentMan->getFilePath());
+    // we must check headers["size"] == e->segmentMan->totalSize here
+    if(e->segmentMan->totalSize != size) {
+      return new DlAbortEx(EX_SIZE_MISMATCH, e->segmentMan->totalSize, size);
+    }
     // send request again to the server with Range header
     return prepareForRetry(0);
   } else {
-    Segment seg;
-    e->segmentMan->getSegment(seg, cuid);	
+    e->segmentMan->totalSize = size;
     e->diskWriter->initAndOpenFile(e->segmentMan->getFilePath());
     createHttpDownloadCommand();
     return true;
@@ -140,9 +143,10 @@ void HttpResponseCommand::createHttpDownloadCommand(string transferEncoding) {
 }
 
 void HttpResponseCommand::retrieveCookie(const HttpHeader& headers) {
-  for(HttpHeader::const_iterator itr = headers.find("Set-Cookie"); itr != headers.end(); itr++) {
+  vector<string> v = headers.get("Set-Cookie");
+  for(vector<string>::const_iterator itr = v.begin(); itr != v.end(); itr++) {
     Cookie c;
-    req->cookieBox->parse(c, (*itr).second);
+    req->cookieBox->parse(c, *itr);
     req->cookieBox->add(c);
   }
 }

+ 1 - 0
src/HttpResponseCommand.h

@@ -33,6 +33,7 @@ private:
   bool handleOtherEncoding(string transferEncoding, const HttpHeader& headers);
   void createHttpDownloadCommand(string transferEncoding = "");
   void retrieveCookie(const HttpHeader& headers);
+  HttpConnection* http;
 protected:
   bool executeInternal(Segment segment);
 public:

+ 3 - 0
src/InitiateConnectionCommandFactory.cc

@@ -21,6 +21,7 @@
 /* copyright --> */
 #include "InitiateConnectionCommandFactory.h"
 #include "HttpInitiateConnectionCommand.h"
+#include "FtpInitiateConnectionCommand.h"
 
 Command* InitiateConnectionCommandFactory::createInitiateConnectionCommand(int cuid, Request* req, DownloadEngine* e) {
   if(req->getProtocol() == "http"
@@ -30,6 +31,8 @@ Command* InitiateConnectionCommandFactory::createInitiateConnectionCommand(int c
 #endif // HAVE_LIBSSL
      ) {
     return new HttpInitiateConnectionCommand(cuid, req, e);
+  } else if(req->getProtocol() == "ftp") {
+    return new FtpInitiateConnectionCommand(cuid, req, e);
   } else {
     // these protocols are not supported yet
     return NULL;

+ 6 - 6
src/Logger.h

@@ -29,12 +29,12 @@ using namespace std;
 class Logger {
 public:
   virtual ~Logger() {}
-  virtual void debug(string msg, ...) = 0;
-  virtual void debug(string msg, Exception* ex, ...) = 0;
-  virtual void info(string msg, ...) = 0;
-  virtual void info(string msg, Exception* ex, ...) = 0;
-  virtual void error(string msg, ...) = 0;
-  virtual void error(string msg, Exception* ex, ...) = 0;
+  virtual void debug(string msg, ...) const = 0;
+  virtual void debug(string msg, Exception* ex, ...) const = 0;
+  virtual void info(string msg, ...) const = 0;
+  virtual void info(string msg, Exception* ex, ...) const = 0;
+  virtual void error(string msg, ...) const = 0;
+  virtual void error(string msg, Exception* ex, ...) const = 0;
 };
 
 #endif // _D_LOGGER_H_

+ 7 - 0
src/Makefile.am

@@ -12,7 +12,14 @@ SRCS =  Socket.cc Socket.h\
 	HttpProxyRequestCommand.cc HttpProxyRequestCommand.h\
 	HttpProxyResponseCommand.cc HttpProxyResponseCommand.h\
 	HttpDownloadCommand.cc HttpDownloadCommand.h\
+	HttpHeader.cc HttpHeader.h\
 	HttpConnection.cc HttpConnection.h\
+	FtpConnection.cc FtpConnection.h\
+	FtpInitiateConnectionCommand.cc FtpInitiateConnectionCommand.h\
+	FtpNegotiationCommand.cc FtpNegotiationCommand.h\
+	FtpDownloadCommand.cc FtpDownloadCommand.h\
+	FtpTunnelRequestCommand.cc FtpTunnelRequestCommand.h\
+	FtpTunnelResponseCommand.cc FtpTunnelResponseCommand.h\
 	SleepCommand.cc SleepCommand.h\
 	DownloadEngine.cc DownloadEngine.h\
 	Segment.h\

+ 119 - 0
src/Makefile.in

@@ -61,7 +61,14 @@ am__objects_1 = libaria2c_a-Socket.$(OBJEXT) \
 	libaria2c_a-HttpProxyRequestCommand.$(OBJEXT) \
 	libaria2c_a-HttpProxyResponseCommand.$(OBJEXT) \
 	libaria2c_a-HttpDownloadCommand.$(OBJEXT) \
+	libaria2c_a-HttpHeader.$(OBJEXT) \
 	libaria2c_a-HttpConnection.$(OBJEXT) \
+	libaria2c_a-FtpConnection.$(OBJEXT) \
+	libaria2c_a-FtpInitiateConnectionCommand.$(OBJEXT) \
+	libaria2c_a-FtpNegotiationCommand.$(OBJEXT) \
+	libaria2c_a-FtpDownloadCommand.$(OBJEXT) \
+	libaria2c_a-FtpTunnelRequestCommand.$(OBJEXT) \
+	libaria2c_a-FtpTunnelResponseCommand.$(OBJEXT) \
 	libaria2c_a-SleepCommand.$(OBJEXT) \
 	libaria2c_a-DownloadEngine.$(OBJEXT) \
 	libaria2c_a-SegmentMan.$(OBJEXT) libaria2c_a-Util.$(OBJEXT) \
@@ -192,7 +199,14 @@ SRCS = Socket.cc Socket.h\
 	HttpProxyRequestCommand.cc HttpProxyRequestCommand.h\
 	HttpProxyResponseCommand.cc HttpProxyResponseCommand.h\
 	HttpDownloadCommand.cc HttpDownloadCommand.h\
+	HttpHeader.cc HttpHeader.h\
 	HttpConnection.cc HttpConnection.h\
+	FtpConnection.cc FtpConnection.h\
+	FtpInitiateConnectionCommand.cc FtpInitiateConnectionCommand.h\
+	FtpNegotiationCommand.cc FtpNegotiationCommand.h\
+	FtpDownloadCommand.cc FtpDownloadCommand.h\
+	FtpTunnelRequestCommand.cc FtpTunnelRequestCommand.h\
+	FtpTunnelResponseCommand.cc FtpTunnelResponseCommand.h\
 	SleepCommand.cc SleepCommand.h\
 	DownloadEngine.cc DownloadEngine.h\
 	Segment.h\
@@ -305,8 +319,15 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libaria2c_a-DownloadCommand.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libaria2c_a-DownloadEngine.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libaria2c_a-File.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libaria2c_a-FtpConnection.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libaria2c_a-FtpDownloadCommand.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libaria2c_a-FtpInitiateConnectionCommand.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libaria2c_a-FtpNegotiationCommand.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libaria2c_a-FtpTunnelRequestCommand.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libaria2c_a-FtpTunnelResponseCommand.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libaria2c_a-HttpConnection.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libaria2c_a-HttpDownloadCommand.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libaria2c_a-HttpHeader.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libaria2c_a-HttpInitiateConnectionCommand.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libaria2c_a-HttpProxyRequestCommand.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libaria2c_a-HttpProxyResponseCommand.Po@am__quote@
@@ -490,6 +511,20 @@ libaria2c_a-HttpDownloadCommand.obj: HttpDownloadCommand.cc
 @AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCXX_FALSE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libaria2c_a_CXXFLAGS) $(CXXFLAGS) -c -o libaria2c_a-HttpDownloadCommand.obj `if test -f 'HttpDownloadCommand.cc'; then $(CYGPATH_W) 'HttpDownloadCommand.cc'; else $(CYGPATH_W) '$(srcdir)/HttpDownloadCommand.cc'; fi`
 
+libaria2c_a-HttpHeader.o: HttpHeader.cc
+@am__fastdepCXX_TRUE@	if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libaria2c_a_CXXFLAGS) $(CXXFLAGS) -MT libaria2c_a-HttpHeader.o -MD -MP -MF "$(DEPDIR)/libaria2c_a-HttpHeader.Tpo" -c -o libaria2c_a-HttpHeader.o `test -f 'HttpHeader.cc' || echo '$(srcdir)/'`HttpHeader.cc; \
+@am__fastdepCXX_TRUE@	then mv -f "$(DEPDIR)/libaria2c_a-HttpHeader.Tpo" "$(DEPDIR)/libaria2c_a-HttpHeader.Po"; else rm -f "$(DEPDIR)/libaria2c_a-HttpHeader.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	source='HttpHeader.cc' object='libaria2c_a-HttpHeader.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libaria2c_a_CXXFLAGS) $(CXXFLAGS) -c -o libaria2c_a-HttpHeader.o `test -f 'HttpHeader.cc' || echo '$(srcdir)/'`HttpHeader.cc
+
+libaria2c_a-HttpHeader.obj: HttpHeader.cc
+@am__fastdepCXX_TRUE@	if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libaria2c_a_CXXFLAGS) $(CXXFLAGS) -MT libaria2c_a-HttpHeader.obj -MD -MP -MF "$(DEPDIR)/libaria2c_a-HttpHeader.Tpo" -c -o libaria2c_a-HttpHeader.obj `if test -f 'HttpHeader.cc'; then $(CYGPATH_W) 'HttpHeader.cc'; else $(CYGPATH_W) '$(srcdir)/HttpHeader.cc'; fi`; \
+@am__fastdepCXX_TRUE@	then mv -f "$(DEPDIR)/libaria2c_a-HttpHeader.Tpo" "$(DEPDIR)/libaria2c_a-HttpHeader.Po"; else rm -f "$(DEPDIR)/libaria2c_a-HttpHeader.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	source='HttpHeader.cc' object='libaria2c_a-HttpHeader.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libaria2c_a_CXXFLAGS) $(CXXFLAGS) -c -o libaria2c_a-HttpHeader.obj `if test -f 'HttpHeader.cc'; then $(CYGPATH_W) 'HttpHeader.cc'; else $(CYGPATH_W) '$(srcdir)/HttpHeader.cc'; fi`
+
 libaria2c_a-HttpConnection.o: HttpConnection.cc
 @am__fastdepCXX_TRUE@	if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libaria2c_a_CXXFLAGS) $(CXXFLAGS) -MT libaria2c_a-HttpConnection.o -MD -MP -MF "$(DEPDIR)/libaria2c_a-HttpConnection.Tpo" -c -o libaria2c_a-HttpConnection.o `test -f 'HttpConnection.cc' || echo '$(srcdir)/'`HttpConnection.cc; \
 @am__fastdepCXX_TRUE@	then mv -f "$(DEPDIR)/libaria2c_a-HttpConnection.Tpo" "$(DEPDIR)/libaria2c_a-HttpConnection.Po"; else rm -f "$(DEPDIR)/libaria2c_a-HttpConnection.Tpo"; exit 1; fi
@@ -504,6 +539,90 @@ libaria2c_a-HttpConnection.obj: HttpConnection.cc
 @AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCXX_FALSE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libaria2c_a_CXXFLAGS) $(CXXFLAGS) -c -o libaria2c_a-HttpConnection.obj `if test -f 'HttpConnection.cc'; then $(CYGPATH_W) 'HttpConnection.cc'; else $(CYGPATH_W) '$(srcdir)/HttpConnection.cc'; fi`
 
+libaria2c_a-FtpConnection.o: FtpConnection.cc
+@am__fastdepCXX_TRUE@	if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libaria2c_a_CXXFLAGS) $(CXXFLAGS) -MT libaria2c_a-FtpConnection.o -MD -MP -MF "$(DEPDIR)/libaria2c_a-FtpConnection.Tpo" -c -o libaria2c_a-FtpConnection.o `test -f 'FtpConnection.cc' || echo '$(srcdir)/'`FtpConnection.cc; \
+@am__fastdepCXX_TRUE@	then mv -f "$(DEPDIR)/libaria2c_a-FtpConnection.Tpo" "$(DEPDIR)/libaria2c_a-FtpConnection.Po"; else rm -f "$(DEPDIR)/libaria2c_a-FtpConnection.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	source='FtpConnection.cc' object='libaria2c_a-FtpConnection.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libaria2c_a_CXXFLAGS) $(CXXFLAGS) -c -o libaria2c_a-FtpConnection.o `test -f 'FtpConnection.cc' || echo '$(srcdir)/'`FtpConnection.cc
+
+libaria2c_a-FtpConnection.obj: FtpConnection.cc
+@am__fastdepCXX_TRUE@	if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libaria2c_a_CXXFLAGS) $(CXXFLAGS) -MT libaria2c_a-FtpConnection.obj -MD -MP -MF "$(DEPDIR)/libaria2c_a-FtpConnection.Tpo" -c -o libaria2c_a-FtpConnection.obj `if test -f 'FtpConnection.cc'; then $(CYGPATH_W) 'FtpConnection.cc'; else $(CYGPATH_W) '$(srcdir)/FtpConnection.cc'; fi`; \
+@am__fastdepCXX_TRUE@	then mv -f "$(DEPDIR)/libaria2c_a-FtpConnection.Tpo" "$(DEPDIR)/libaria2c_a-FtpConnection.Po"; else rm -f "$(DEPDIR)/libaria2c_a-FtpConnection.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	source='FtpConnection.cc' object='libaria2c_a-FtpConnection.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libaria2c_a_CXXFLAGS) $(CXXFLAGS) -c -o libaria2c_a-FtpConnection.obj `if test -f 'FtpConnection.cc'; then $(CYGPATH_W) 'FtpConnection.cc'; else $(CYGPATH_W) '$(srcdir)/FtpConnection.cc'; fi`
+
+libaria2c_a-FtpInitiateConnectionCommand.o: FtpInitiateConnectionCommand.cc
+@am__fastdepCXX_TRUE@	if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libaria2c_a_CXXFLAGS) $(CXXFLAGS) -MT libaria2c_a-FtpInitiateConnectionCommand.o -MD -MP -MF "$(DEPDIR)/libaria2c_a-FtpInitiateConnectionCommand.Tpo" -c -o libaria2c_a-FtpInitiateConnectionCommand.o `test -f 'FtpInitiateConnectionCommand.cc' || echo '$(srcdir)/'`FtpInitiateConnectionCommand.cc; \
+@am__fastdepCXX_TRUE@	then mv -f "$(DEPDIR)/libaria2c_a-FtpInitiateConnectionCommand.Tpo" "$(DEPDIR)/libaria2c_a-FtpInitiateConnectionCommand.Po"; else rm -f "$(DEPDIR)/libaria2c_a-FtpInitiateConnectionCommand.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	source='FtpInitiateConnectionCommand.cc' object='libaria2c_a-FtpInitiateConnectionCommand.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libaria2c_a_CXXFLAGS) $(CXXFLAGS) -c -o libaria2c_a-FtpInitiateConnectionCommand.o `test -f 'FtpInitiateConnectionCommand.cc' || echo '$(srcdir)/'`FtpInitiateConnectionCommand.cc
+
+libaria2c_a-FtpInitiateConnectionCommand.obj: FtpInitiateConnectionCommand.cc
+@am__fastdepCXX_TRUE@	if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libaria2c_a_CXXFLAGS) $(CXXFLAGS) -MT libaria2c_a-FtpInitiateConnectionCommand.obj -MD -MP -MF "$(DEPDIR)/libaria2c_a-FtpInitiateConnectionCommand.Tpo" -c -o libaria2c_a-FtpInitiateConnectionCommand.obj `if test -f 'FtpInitiateConnectionCommand.cc'; then $(CYGPATH_W) 'FtpInitiateConnectionCommand.cc'; else $(CYGPATH_W) '$(srcdir)/FtpInitiateConnectionCommand.cc'; fi`; \
+@am__fastdepCXX_TRUE@	then mv -f "$(DEPDIR)/libaria2c_a-FtpInitiateConnectionCommand.Tpo" "$(DEPDIR)/libaria2c_a-FtpInitiateConnectionCommand.Po"; else rm -f "$(DEPDIR)/libaria2c_a-FtpInitiateConnectionCommand.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	source='FtpInitiateConnectionCommand.cc' object='libaria2c_a-FtpInitiateConnectionCommand.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libaria2c_a_CXXFLAGS) $(CXXFLAGS) -c -o libaria2c_a-FtpInitiateConnectionCommand.obj `if test -f 'FtpInitiateConnectionCommand.cc'; then $(CYGPATH_W) 'FtpInitiateConnectionCommand.cc'; else $(CYGPATH_W) '$(srcdir)/FtpInitiateConnectionCommand.cc'; fi`
+
+libaria2c_a-FtpNegotiationCommand.o: FtpNegotiationCommand.cc
+@am__fastdepCXX_TRUE@	if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libaria2c_a_CXXFLAGS) $(CXXFLAGS) -MT libaria2c_a-FtpNegotiationCommand.o -MD -MP -MF "$(DEPDIR)/libaria2c_a-FtpNegotiationCommand.Tpo" -c -o libaria2c_a-FtpNegotiationCommand.o `test -f 'FtpNegotiationCommand.cc' || echo '$(srcdir)/'`FtpNegotiationCommand.cc; \
+@am__fastdepCXX_TRUE@	then mv -f "$(DEPDIR)/libaria2c_a-FtpNegotiationCommand.Tpo" "$(DEPDIR)/libaria2c_a-FtpNegotiationCommand.Po"; else rm -f "$(DEPDIR)/libaria2c_a-FtpNegotiationCommand.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	source='FtpNegotiationCommand.cc' object='libaria2c_a-FtpNegotiationCommand.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libaria2c_a_CXXFLAGS) $(CXXFLAGS) -c -o libaria2c_a-FtpNegotiationCommand.o `test -f 'FtpNegotiationCommand.cc' || echo '$(srcdir)/'`FtpNegotiationCommand.cc
+
+libaria2c_a-FtpNegotiationCommand.obj: FtpNegotiationCommand.cc
+@am__fastdepCXX_TRUE@	if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libaria2c_a_CXXFLAGS) $(CXXFLAGS) -MT libaria2c_a-FtpNegotiationCommand.obj -MD -MP -MF "$(DEPDIR)/libaria2c_a-FtpNegotiationCommand.Tpo" -c -o libaria2c_a-FtpNegotiationCommand.obj `if test -f 'FtpNegotiationCommand.cc'; then $(CYGPATH_W) 'FtpNegotiationCommand.cc'; else $(CYGPATH_W) '$(srcdir)/FtpNegotiationCommand.cc'; fi`; \
+@am__fastdepCXX_TRUE@	then mv -f "$(DEPDIR)/libaria2c_a-FtpNegotiationCommand.Tpo" "$(DEPDIR)/libaria2c_a-FtpNegotiationCommand.Po"; else rm -f "$(DEPDIR)/libaria2c_a-FtpNegotiationCommand.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	source='FtpNegotiationCommand.cc' object='libaria2c_a-FtpNegotiationCommand.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libaria2c_a_CXXFLAGS) $(CXXFLAGS) -c -o libaria2c_a-FtpNegotiationCommand.obj `if test -f 'FtpNegotiationCommand.cc'; then $(CYGPATH_W) 'FtpNegotiationCommand.cc'; else $(CYGPATH_W) '$(srcdir)/FtpNegotiationCommand.cc'; fi`
+
+libaria2c_a-FtpDownloadCommand.o: FtpDownloadCommand.cc
+@am__fastdepCXX_TRUE@	if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libaria2c_a_CXXFLAGS) $(CXXFLAGS) -MT libaria2c_a-FtpDownloadCommand.o -MD -MP -MF "$(DEPDIR)/libaria2c_a-FtpDownloadCommand.Tpo" -c -o libaria2c_a-FtpDownloadCommand.o `test -f 'FtpDownloadCommand.cc' || echo '$(srcdir)/'`FtpDownloadCommand.cc; \
+@am__fastdepCXX_TRUE@	then mv -f "$(DEPDIR)/libaria2c_a-FtpDownloadCommand.Tpo" "$(DEPDIR)/libaria2c_a-FtpDownloadCommand.Po"; else rm -f "$(DEPDIR)/libaria2c_a-FtpDownloadCommand.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	source='FtpDownloadCommand.cc' object='libaria2c_a-FtpDownloadCommand.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libaria2c_a_CXXFLAGS) $(CXXFLAGS) -c -o libaria2c_a-FtpDownloadCommand.o `test -f 'FtpDownloadCommand.cc' || echo '$(srcdir)/'`FtpDownloadCommand.cc
+
+libaria2c_a-FtpDownloadCommand.obj: FtpDownloadCommand.cc
+@am__fastdepCXX_TRUE@	if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libaria2c_a_CXXFLAGS) $(CXXFLAGS) -MT libaria2c_a-FtpDownloadCommand.obj -MD -MP -MF "$(DEPDIR)/libaria2c_a-FtpDownloadCommand.Tpo" -c -o libaria2c_a-FtpDownloadCommand.obj `if test -f 'FtpDownloadCommand.cc'; then $(CYGPATH_W) 'FtpDownloadCommand.cc'; else $(CYGPATH_W) '$(srcdir)/FtpDownloadCommand.cc'; fi`; \
+@am__fastdepCXX_TRUE@	then mv -f "$(DEPDIR)/libaria2c_a-FtpDownloadCommand.Tpo" "$(DEPDIR)/libaria2c_a-FtpDownloadCommand.Po"; else rm -f "$(DEPDIR)/libaria2c_a-FtpDownloadCommand.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	source='FtpDownloadCommand.cc' object='libaria2c_a-FtpDownloadCommand.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libaria2c_a_CXXFLAGS) $(CXXFLAGS) -c -o libaria2c_a-FtpDownloadCommand.obj `if test -f 'FtpDownloadCommand.cc'; then $(CYGPATH_W) 'FtpDownloadCommand.cc'; else $(CYGPATH_W) '$(srcdir)/FtpDownloadCommand.cc'; fi`
+
+libaria2c_a-FtpTunnelRequestCommand.o: FtpTunnelRequestCommand.cc
+@am__fastdepCXX_TRUE@	if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libaria2c_a_CXXFLAGS) $(CXXFLAGS) -MT libaria2c_a-FtpTunnelRequestCommand.o -MD -MP -MF "$(DEPDIR)/libaria2c_a-FtpTunnelRequestCommand.Tpo" -c -o libaria2c_a-FtpTunnelRequestCommand.o `test -f 'FtpTunnelRequestCommand.cc' || echo '$(srcdir)/'`FtpTunnelRequestCommand.cc; \
+@am__fastdepCXX_TRUE@	then mv -f "$(DEPDIR)/libaria2c_a-FtpTunnelRequestCommand.Tpo" "$(DEPDIR)/libaria2c_a-FtpTunnelRequestCommand.Po"; else rm -f "$(DEPDIR)/libaria2c_a-FtpTunnelRequestCommand.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	source='FtpTunnelRequestCommand.cc' object='libaria2c_a-FtpTunnelRequestCommand.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libaria2c_a_CXXFLAGS) $(CXXFLAGS) -c -o libaria2c_a-FtpTunnelRequestCommand.o `test -f 'FtpTunnelRequestCommand.cc' || echo '$(srcdir)/'`FtpTunnelRequestCommand.cc
+
+libaria2c_a-FtpTunnelRequestCommand.obj: FtpTunnelRequestCommand.cc
+@am__fastdepCXX_TRUE@	if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libaria2c_a_CXXFLAGS) $(CXXFLAGS) -MT libaria2c_a-FtpTunnelRequestCommand.obj -MD -MP -MF "$(DEPDIR)/libaria2c_a-FtpTunnelRequestCommand.Tpo" -c -o libaria2c_a-FtpTunnelRequestCommand.obj `if test -f 'FtpTunnelRequestCommand.cc'; then $(CYGPATH_W) 'FtpTunnelRequestCommand.cc'; else $(CYGPATH_W) '$(srcdir)/FtpTunnelRequestCommand.cc'; fi`; \
+@am__fastdepCXX_TRUE@	then mv -f "$(DEPDIR)/libaria2c_a-FtpTunnelRequestCommand.Tpo" "$(DEPDIR)/libaria2c_a-FtpTunnelRequestCommand.Po"; else rm -f "$(DEPDIR)/libaria2c_a-FtpTunnelRequestCommand.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	source='FtpTunnelRequestCommand.cc' object='libaria2c_a-FtpTunnelRequestCommand.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libaria2c_a_CXXFLAGS) $(CXXFLAGS) -c -o libaria2c_a-FtpTunnelRequestCommand.obj `if test -f 'FtpTunnelRequestCommand.cc'; then $(CYGPATH_W) 'FtpTunnelRequestCommand.cc'; else $(CYGPATH_W) '$(srcdir)/FtpTunnelRequestCommand.cc'; fi`
+
+libaria2c_a-FtpTunnelResponseCommand.o: FtpTunnelResponseCommand.cc
+@am__fastdepCXX_TRUE@	if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libaria2c_a_CXXFLAGS) $(CXXFLAGS) -MT libaria2c_a-FtpTunnelResponseCommand.o -MD -MP -MF "$(DEPDIR)/libaria2c_a-FtpTunnelResponseCommand.Tpo" -c -o libaria2c_a-FtpTunnelResponseCommand.o `test -f 'FtpTunnelResponseCommand.cc' || echo '$(srcdir)/'`FtpTunnelResponseCommand.cc; \
+@am__fastdepCXX_TRUE@	then mv -f "$(DEPDIR)/libaria2c_a-FtpTunnelResponseCommand.Tpo" "$(DEPDIR)/libaria2c_a-FtpTunnelResponseCommand.Po"; else rm -f "$(DEPDIR)/libaria2c_a-FtpTunnelResponseCommand.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	source='FtpTunnelResponseCommand.cc' object='libaria2c_a-FtpTunnelResponseCommand.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libaria2c_a_CXXFLAGS) $(CXXFLAGS) -c -o libaria2c_a-FtpTunnelResponseCommand.o `test -f 'FtpTunnelResponseCommand.cc' || echo '$(srcdir)/'`FtpTunnelResponseCommand.cc
+
+libaria2c_a-FtpTunnelResponseCommand.obj: FtpTunnelResponseCommand.cc
+@am__fastdepCXX_TRUE@	if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libaria2c_a_CXXFLAGS) $(CXXFLAGS) -MT libaria2c_a-FtpTunnelResponseCommand.obj -MD -MP -MF "$(DEPDIR)/libaria2c_a-FtpTunnelResponseCommand.Tpo" -c -o libaria2c_a-FtpTunnelResponseCommand.obj `if test -f 'FtpTunnelResponseCommand.cc'; then $(CYGPATH_W) 'FtpTunnelResponseCommand.cc'; else $(CYGPATH_W) '$(srcdir)/FtpTunnelResponseCommand.cc'; fi`; \
+@am__fastdepCXX_TRUE@	then mv -f "$(DEPDIR)/libaria2c_a-FtpTunnelResponseCommand.Tpo" "$(DEPDIR)/libaria2c_a-FtpTunnelResponseCommand.Po"; else rm -f "$(DEPDIR)/libaria2c_a-FtpTunnelResponseCommand.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	source='FtpTunnelResponseCommand.cc' object='libaria2c_a-FtpTunnelResponseCommand.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libaria2c_a_CXXFLAGS) $(CXXFLAGS) -c -o libaria2c_a-FtpTunnelResponseCommand.obj `if test -f 'FtpTunnelResponseCommand.cc'; then $(CYGPATH_W) 'FtpTunnelResponseCommand.cc'; else $(CYGPATH_W) '$(srcdir)/FtpTunnelResponseCommand.cc'; fi`
+
 libaria2c_a-SleepCommand.o: SleepCommand.cc
 @am__fastdepCXX_TRUE@	if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libaria2c_a_CXXFLAGS) $(CXXFLAGS) -MT libaria2c_a-SleepCommand.o -MD -MP -MF "$(DEPDIR)/libaria2c_a-SleepCommand.Tpo" -c -o libaria2c_a-SleepCommand.o `test -f 'SleepCommand.cc' || echo '$(srcdir)/'`SleepCommand.cc; \
 @am__fastdepCXX_TRUE@	then mv -f "$(DEPDIR)/libaria2c_a-SleepCommand.Tpo" "$(DEPDIR)/libaria2c_a-SleepCommand.Po"; else rm -f "$(DEPDIR)/libaria2c_a-SleepCommand.Tpo"; exit 1; fi

+ 2 - 15
src/Request.cc

@@ -22,14 +22,13 @@
 #include "Request.h"
 #include "Util.h"
 
-#define MAX_RETRY_COUNT 5
-
-Request::Request():port(0), retryCount(0) {
+Request::Request():port(0), tryCount(0) {
   defaultPorts["http"] = 80;
 #ifdef HAVE_LIBSSL
   // for SSL
   defaultPorts["https"] = 443;
 #endif // HAVE_LIBSSL
+  defaultPorts["ftp"] = 21;
   seg.sp = 0;
   seg.ep = 0;
   seg.ds = 0;
@@ -102,15 +101,3 @@ bool Request::parseUrl(string url) {
   }
   return true;
 }
-
-void Request::resetRetryCount() {
-  this->retryCount = 0;
-}
-
-void Request::addRetryCount() {
-  retryCount++;
-}
-
-bool Request::noMoreRetry() {
-  return retryCount >= MAX_RETRY_COUNT;
-}

+ 7 - 6
src/Request.h

@@ -29,6 +29,8 @@
 
 using namespace std;
 
+#define MAX_TRY_COUNT 5
+
 #define SAFE_CHARS "abcdefghijklmnopqrstuvwxyz"\
 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"\
 "0123456789"\
@@ -55,7 +57,7 @@ private:
   string dir;
   string file;
   map<string, int> defaultPorts;
-  int retryCount;
+  int tryCount;
   bool parseUrl(string url);
 public:
   Segment seg;
@@ -72,15 +74,14 @@ public:
   // Returns true if parsing goes successful, otherwise returns false.
   bool redirectUrl(string url);
   bool resetUrl();
-  void resetRetryCount();
-  void addRetryCount();
-  int getRetryCount();
-  bool noMoreRetry();
+  void resetTryCount() { tryCount = 0; }
+  void addTryCount() { tryCount++; }
+  int getTryCount() const { return tryCount; }
+  bool noMoreTry() const { return tryCount >= MAX_TRY_COUNT; }
 
   string getUrl() const { return url; }
   string getCurrentUrl() const { return currentUrl; }
   string getPreviousUrl() const { return previousUrl; }
-  void setPreviousUrl(string url) { previousUrl = url; }
   string getReferer() const { return referer; }
   void setReferer(string url) { referer = previousUrl = url; }
   string getProtocol() const { return protocol; }

+ 17 - 11
src/SegmentMan.cc

@@ -123,7 +123,7 @@ void SegmentMan::updateSegment(const Segment& segment) {
 }
 
 
-bool SegmentMan::segmentFileExists() {
+bool SegmentMan::segmentFileExists() const {
   if(!isSplittable) {
     return false;
   }
@@ -154,14 +154,17 @@ void SegmentMan::load() {
   }
 }
 
-void SegmentMan::save() {
-  if(!isSplittable) {
+void SegmentMan::save() const {
+  if(!isSplittable || totalSize == 0) {
     return;
   }
   string segFilename = getSegmentFilePath();
   logger->info(MSG_SAVING_SEGMENT_FILE, segFilename.c_str());
   FILE* segFile = openSegFile(segFilename, "w");
-  for(vector<Segment>::iterator itr = segments.begin(); itr != segments.end(); itr++) {
+  if(fwrite(&totalSize, sizeof(totalSize), 1, segFile) < 1) {
+    throw new DlAbortEx(strerror(errno));
+  }
+  for(vector<Segment>::const_iterator itr = segments.begin(); itr != segments.end(); itr++) {
     if(fwrite(&*itr, sizeof(Segment), 1, segFile) < 1) {
       throw new DlAbortEx(strerror(errno));
     }
@@ -170,7 +173,7 @@ void SegmentMan::save() {
   logger->info(MSG_SAVED_SEGMENT_FILE);
 }
 
-FILE* SegmentMan::openSegFile(string segFilename, string mode) {
+FILE* SegmentMan::openSegFile(string segFilename, string mode) const {
   FILE* segFile = fopen(segFilename.c_str(), mode.c_str());
   if(segFile == NULL) {
     throw new DlAbortEx(strerror(errno));
@@ -180,6 +183,9 @@ FILE* SegmentMan::openSegFile(string segFilename, string mode) {
 
 void SegmentMan::read(FILE* file) {
   assert(file != NULL);
+  if(fread(&totalSize, sizeof(totalSize), 1, file) < 1) {
+    throw new DlAbortEx(strerror(errno));
+  }
   while(1) {
     Segment seg;
     if(fread(&seg, sizeof(Segment), 1, file) < 1) {
@@ -193,7 +199,7 @@ void SegmentMan::read(FILE* file) {
   }
 }
 
-void SegmentMan::remove() {
+void SegmentMan::remove() const {
   if(!isSplittable) {
     return;
   }
@@ -203,11 +209,11 @@ void SegmentMan::remove() {
   }
 }
 
-bool SegmentMan::finished() {
+bool SegmentMan::finished() const {
   if(!downloadStarted || segments.size() == 0) {
     return false;
   }
-  for(vector<Segment>::iterator itr = segments.begin(); itr != segments.end(); itr++) {
+  for(vector<Segment>::const_iterator itr = segments.begin(); itr != segments.end(); itr++) {
     if(!(*itr).finish) {
       return false;
     }
@@ -215,15 +221,15 @@ bool SegmentMan::finished() {
   return true;
 }
 
-void SegmentMan::removeIfFinished() {
+void SegmentMan::removeIfFinished() const {
   if(finished()) {
     remove();
   }
 }
 
-long long int SegmentMan::getDownloadedSize() {
+long long int SegmentMan::getDownloadedSize() const {
   long long int size = 0;
-  for(vector<Segment>::iterator itr = segments.begin(); itr != segments.end(); itr++) {
+  for(vector<Segment>::const_iterator itr = segments.begin(); itr != segments.end(); itr++) {
     size += (*itr).ds;
   }
   return size;

+ 10 - 10
src/SegmentMan.h

@@ -37,7 +37,7 @@ using namespace std;
 class SegmentMan {
 private:
   void read(FILE* file);
-  FILE* openSegFile(string segFilename, string mode);
+  FILE* openSegFile(string segFilename, string mode) const;
 public:
   /**
    * The total number of bytes to download.
@@ -80,7 +80,7 @@ public:
    */
   string ufilename;
 
-  Logger* logger;
+  const Logger* logger;
 
   SegmentMan();
   ~SegmentMan();
@@ -89,13 +89,13 @@ public:
    * Returns dir+"/"+filename.
    * If filename is empty, then returns dir+"/"+"inex.html";
    */
-  string getFilePath() {
+  string getFilePath() const {
     return (dir == "" ? "." : dir)+"/"+
       (ufilename == "" ? 
        (filename == "" ? "index.html" : filename) : ufilename);
   }
 
-  string getSegmentFilePath() {
+  string getSegmentFilePath() const {
     return getFilePath()+SEGMENT_FILE_EXTENSION;
   }
 
@@ -129,7 +129,7 @@ public:
    * The file name of the segment data is filename appended by ".aria2".
    * If isSplittable is false, then returns simply false without any operation.
    */
-  bool segmentFileExists();
+  bool segmentFileExists() const;
   /**
    * Loads the segment data file.
    * If isSplittable is false, then returns without any operation.
@@ -139,26 +139,26 @@ public:
    * Saves the segment data file.
    * If isSplittable is false, then returns without any operation.
    */
-  void save();
+  void save() const;
   /**
    * Removes the segment data file.
    * If isSplittable is false, then returns without any operation.
    */
-  void remove();
+  void remove() const;
   /**
    * Returs true when the download has finished.
    * If downloadStarted is false or the number of the segments of this object
    * holds is 0, then returns false.
    */
-  bool finished();
+  bool finished() const;
   /**
    * if finished() is true, then call remove()
    */
-  void removeIfFinished();
+  void removeIfFinished() const;
   /**
    * Returns the total number of bytes to be downloaded.
    */
-  long long int getDownloadedSize();
+  long long int getDownloadedSize() const;
 };
 
 #endif // _D_SEGMENT_MAN_H_

+ 8 - 7
src/SimpleLogger.cc

@@ -36,7 +36,8 @@ SimpleLogger::~SimpleLogger() {
   }
 }
 
-void SimpleLogger::writeLog(int level, string msg, va_list ap, Exception* e) {
+void SimpleLogger::writeLog(int level, string msg, va_list ap, Exception* e) const
+{
   string levelStr;
   switch(level) {
   case DEBUG:
@@ -56,14 +57,14 @@ void SimpleLogger::writeLog(int level, string msg, va_list ap, Exception* e) {
   fflush(stdout);
 }
 
-void SimpleLogger::debug(string msg, ...) {
+void SimpleLogger::debug(string msg, ...) const {
   va_list ap;
   va_start(ap, msg);
   writeLog(DEBUG, msg, ap);
   va_end(ap);
 }
 
-void SimpleLogger::debug(string msg, Exception* e, ...) {
+void SimpleLogger::debug(string msg, Exception* e, ...) const {
   va_list ap;
   va_start(ap, e);
   writeLog(DEBUG, msg, ap, e);
@@ -71,14 +72,14 @@ void SimpleLogger::debug(string msg, Exception* e, ...) {
 }
 
   
-void SimpleLogger::info(string msg, ...) {
+void SimpleLogger::info(string msg, ...) const {
   va_list ap;
   va_start(ap, msg);
   writeLog(INFO, msg, ap);
   va_end(ap);
 }
 
-void SimpleLogger::info(string msg, Exception* e, ...) {
+void SimpleLogger::info(string msg, Exception* e, ...) const {
   va_list ap;
   va_start(ap, e);
   writeLog(INFO, msg, ap, e);
@@ -86,14 +87,14 @@ void SimpleLogger::info(string msg, Exception* e, ...) {
 }
 
   
-void SimpleLogger::error(string msg, ...) {
+void SimpleLogger::error(string msg, ...) const {
   va_list ap;
   va_start(ap, msg);
   writeLog(ERROR, msg, ap);
   va_end(ap);
 }
 
-void SimpleLogger::error(string msg, Exception* e, ...) {
+void SimpleLogger::error(string msg, Exception* e, ...) const {
   va_list ap;
   va_start(ap, e);
   writeLog(ERROR, msg, ap, e);

+ 7 - 7
src/SimpleLogger.h

@@ -30,19 +30,19 @@ private:
     DEBUG,
     INFO,
     ERROR};
-  void writeLog(int level, string msg, va_list ap, Exception* e = NULL);
+  void writeLog(int level, string msg, va_list ap, Exception* e = NULL) const;
   FILE* file;
 public:
   SimpleLogger(string filename);
   SimpleLogger(FILE* logfile);
   ~SimpleLogger();
 
-  void debug(string msg, ...);
-  void debug(string msg, Exception* ex, ...);
-  void info(string msg, ...);
-  void info(string msg, Exception* ex, ...);
-  void error(string msg, ...);
-  void error(string msg, Exception* ex, ...);
+  void debug(string msg, ...) const;
+  void debug(string msg, Exception* ex, ...) const;
+  void info(string msg, ...) const;
+  void info(string msg, Exception* ex, ...) const;
+  void error(string msg, ...) const;
+  void error(string msg, Exception* ex, ...) const;
 };
 
 #endif // _D_SIMPLE_LOGGER_H_

+ 30 - 10
src/Socket.cc

@@ -30,6 +30,10 @@ Socket::Socket(const Socket& s) {
   core->use++;
 }
 
+Socket::Socket(SocketCore* core) {
+  this->core = core;
+}
+
 Socket::~Socket() {
   core->use--;
   if(core->use == 0) {
@@ -49,38 +53,54 @@ Socket& Socket::operator=(const Socket& s) {
   return *this;
 }
 
-void Socket::establishConnection(string host, int port) {
+void Socket::beginListen() const {
+  core->beginListen();
+}
+
+void Socket::getAddrInfo(pair<string, int>& addrinfo) const {
+  core->getAddrInfo(addrinfo);
+}
+
+Socket* Socket::acceptConnection() const {
+  return new Socket(core->acceptConnection());
+}
+
+void Socket::establishConnection(string host, int port) const {
   core->establishConnection(host, port);
 }
 
-void Socket::setNonBlockingMode() {
-  core->setNonBlockingMode();
+void Socket::setBlockingMode() const {
+  core->setBlockingMode();
 }
 
-void Socket::closeConnection() {
+void Socket::closeConnection() const {
   core->closeConnection();
 }
 
-bool Socket::isWritable(int timeout) {
+bool Socket::isWritable(int timeout) const {
   return core->isWritable(timeout);
 }
 
-bool Socket::isReadable(int timeout) {
+bool Socket::isReadable(int timeout) const {
   return core->isReadable(timeout);
 }
 
-void Socket::writeData(const char* data, int len, int timeout) {
+void Socket::writeData(const char* data, int len, int timeout) const {
   core->writeData(data, len, timeout);
 }
 
-void Socket::readData(char* data, int& len, int timeout) {
+void Socket::writeData(string str, int timeout) const {
+  core->writeData(str.c_str(), str.size(), timeout);
+}
+
+void Socket::readData(char* data, int& len, int timeout) const {
   core->readData(data, len, timeout);
 }
 
-void Socket::peekData(char* data, int& len, int timeout) {
+void Socket::peekData(char* data, int& len, int timeout) const {
   core->peekData(data, len, timeout);
 }
 
-void Socket::initiateSecureConnection() {
+void Socket::initiateSecureConnection() const {
   core->initiateSecureConnection();
 }

+ 16 - 10
src/Socket.h

@@ -32,6 +32,7 @@ class Socket {
 private:
   // socket endpoint descriptor
   SocketCore* core;
+  Socket(SocketCore* core);
 public:
   Socket();
   Socket(const Socket& s);
@@ -41,46 +42,51 @@ public:
 
   int getSockfd() const { return core->sockfd; }
 
+  void beginListen() const;
+  void getAddrInfo(pair<string, int>& addrinfo) const;
+  Socket* acceptConnection() const;
+
   /**
    * Connects to the server named host and the destination port is port.
    * This method make socket non-blocking mode.
-   * To make the socket blocking mode, call setNonBlockingMode() after
+   * To make the socket blocking mode, call setBlockingMode() after
    * the connection is established.
    */
-  void establishConnection(string host, int port);
+  void establishConnection(string host, int port) const;
 
-  void setNonBlockingMode();
+  void setBlockingMode() const;
 
   // Closes the connection which this socket object has
-  void closeConnection();
+  void closeConnection() const;
 
   // examines whether the socket of this Socket object is available for writing.
   // returns true if the socket is available for writing, otherwise returns false.
-  bool isWritable(int timeout);
+  bool isWritable(int timeout) const;
 
   // examines whether the socket of this Socket object is available for reading.
   // returns true if the socket is available for reading, otherwise returns false.
-  bool isReadable(int timeout);
+  bool isReadable(int timeout) const;
 
   // writes characters into the socket. data is a pointer pointing the first
   // byte of the data and len is the length of the data.
-  void writeData(const char* data, int len, int timeout = 5);
+  void writeData(const char* data, int len, int timeout = 5) const;
+  void writeData(string str, int timeout = 5) const;
 
   // Reads up to len bytes from this socket.
   // data is a pointer pointing the first
   // byte of the data, which must be allocated before this method is called.
   // len is the size of the allocated memory. When this method returns
   // successfully, len is replaced by the size of the read data.
-  void readData(char* data, int& len, int timeout = 5);
+  void readData(char* data, int& len, int timeout = 5) const;
   // Reads up to len bytes from this socket, but bytes are not removed from
   // this socket.
-  void peekData(char* data, int& len, int timeout = 5);
+  void peekData(char* data, int& len, int timeout = 5) const;
 
   /**
    * Makes this socket secure.
    * If the system has not OpenSSL, then this method do nothing.
    */
-  void initiateSecureConnection();
+  void initiateSecureConnection() const;
 };
 
 #endif // _D_SOCKET_H_

+ 92 - 21
src/SocketCore.cc

@@ -34,29 +34,92 @@
 #include <errno.h>
 #include "message.h"
 
-SocketCore::SocketCore():sockfd(-1), use(1), secure(false)
+SocketCore::SocketCore():sockfd(-1) {
+  init();
+}
+
+SocketCore::SocketCore(int sockfd):sockfd(sockfd) {
+  init();
+}
+
+void SocketCore::init() {
+  use = 1;
+  secure = false;
 #ifdef HAVE_LIBSSL
   // for SSL
-			 , sslCtx(NULL), ssl(NULL)
+  sslCtx = NULL;
+  ssl = NULL;
 #endif // HAVE_LIBSSL
-{}
+}
 
 SocketCore::~SocketCore() {
   closeConnection();
 }
 
+void SocketCore::beginListen() {
+  closeConnection();
+  //sockfd = socket(AF_UNSPEC, SOCK_STREAM, PF_UNSPEC);
+  sockfd = socket(AF_INET, SOCK_STREAM, 0);
+  if(sockfd == -1) {
+    throw new DlAbortEx(strerror(errno));
+  }
+  socklen_t sockopt = 1;
+  if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(socklen_t)) < 0) {
+    close(sockfd);
+    sockfd = -1;
+    throw new DlAbortEx(strerror(errno));
+  }
+
+  struct sockaddr_in sockaddr;
+  memset((char*)&sockaddr, 0, sizeof(sockaddr));
+  sockaddr.sin_family = AF_INET;
+  sockaddr.sin_addr.s_addr = INADDR_ANY;
+  sockaddr.sin_port = htons(0);
+  
+  if(bind(sockfd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == -1) {
+    throw new DlAbortEx(strerror(errno));
+  }
+
+  if(listen(sockfd, 1) == -1) {
+    throw new DlAbortEx(strerror(errno));
+  }
+}
+
+SocketCore* SocketCore::acceptConnection() const {
+  struct sockaddr_in sockaddr;
+  socklen_t len = sizeof(sockaddr);
+  memset((char*)&sockaddr, 0, sizeof(sockaddr));
+  int fd;
+  if((fd = accept(sockfd, (struct sockaddr*)&sockaddr, &len)) == -1) {
+    throw new DlAbortEx(strerror(errno));
+  }
+  SocketCore* s = new SocketCore(fd);
+  return s;
+}
+
+void SocketCore::getAddrInfo(pair<string, int>& addrinfo) const {
+  struct sockaddr_in listenaddr;
+  memset((char*)&listenaddr, 0, sizeof(listenaddr));
+  socklen_t len = sizeof(listenaddr);
+  if(getsockname(sockfd, (struct sockaddr*)&listenaddr, &len) == -1) {
+    throw new DlAbortEx(strerror(errno));
+  }
+  addrinfo.first = inet_ntoa(listenaddr.sin_addr);
+  addrinfo.second = ntohs(listenaddr.sin_port);
+}
+
 void SocketCore::establishConnection(string host, int port) {
+  closeConnection();
   sockfd = socket(AF_INET, SOCK_STREAM, 0);
-  if(sockfd >= 0) {
-    socklen_t sockopt = 1;
-    if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(int)) < 0) {
-      close(sockfd);
-      sockfd = -1;
-      throw new DlAbortEx(strerror(errno));
-    }
-  } else {
+  if(sockfd == -1) {
       throw new DlAbortEx(strerror(errno));
   }
+  socklen_t sockopt = 1;
+  if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(socklen_t)) < 0) {
+    close(sockfd);
+    sockfd = -1;
+    throw new DlAbortEx(strerror(errno));
+  }
 
   struct sockaddr_in sockaddr;
   memset((char*)&sockaddr, 0, sizeof(sockaddr));
@@ -87,7 +150,7 @@ void SocketCore::establishConnection(string host, int port) {
   }
 }
 
-void SocketCore::setNonBlockingMode() {
+void SocketCore::setBlockingMode() const {
   int flags = fcntl(sockfd, F_GETFL, 0);
   fcntl(sockfd, F_SETFL, flags&~O_NONBLOCK);
 }
@@ -95,7 +158,7 @@ void SocketCore::setNonBlockingMode() {
 void SocketCore::closeConnection() {
 #ifdef HAVE_LIBSSL
   // for SSL
-  if(secure) {
+  if(secure && ssl != NULL) {
     SSL_shutdown(ssl);
   }
 #endif // HAVE_LIBSSL
@@ -105,7 +168,7 @@ void SocketCore::closeConnection() {
   }
 #ifdef HAVE_LIBSSL
   // for SSL
-  if(secure) {
+  if(secure && ssl != NULL) {
     SSL_free(ssl);
     SSL_CTX_free(sslCtx);
     ssl = NULL;
@@ -114,7 +177,7 @@ void SocketCore::closeConnection() {
 #endif // HAVE_LIBSSL
 }
 
-bool SocketCore::isWritable(int timeout) {
+bool SocketCore::isWritable(int timeout) const {
   fd_set fds;
   FD_ZERO(&fds);
   FD_SET(sockfd, &fds);
@@ -130,11 +193,15 @@ bool SocketCore::isWritable(int timeout) {
     // time out
     return false;
   } else {
-    throw new DlRetryEx(strerror(errno));
+    if(errno == EINPROGRESS) {
+      return false;
+    } else {
+      throw new DlRetryEx(strerror(errno));
+    }
   }
 }
 
-bool SocketCore::isReadable(int timeout) {
+bool SocketCore::isReadable(int timeout) const {
   fd_set fds;
   FD_ZERO(&fds);
   FD_SET(sockfd, &fds);
@@ -150,11 +217,15 @@ bool SocketCore::isReadable(int timeout) {
     // time out
     return false;
   } else {
-    throw new DlRetryEx(strerror(errno));
+    if(errno == EINPROGRESS) {
+      return false;
+    } else {
+      throw new DlRetryEx(strerror(errno));
+    }
   }
 }
 
-void SocketCore::writeData(const char* data, int len, int timeout) {
+void SocketCore::writeData(const char* data, int len, int timeout) const {
   if(!isWritable(timeout) ||
      !secure && send(sockfd, data, (size_t)len, 0) != len
 #ifdef HAVE_LIBSSL
@@ -167,7 +238,7 @@ void SocketCore::writeData(const char* data, int len, int timeout) {
   }
 }
 
-void SocketCore::readData(char* data, int& len, int timeout) {
+void SocketCore::readData(char* data, int& len, int timeout) const {
   if(!isReadable(timeout) ||
      !secure && (len = recv(sockfd, data, (size_t)len, 0)) < 0
 #ifdef HAVE_LIBSSL
@@ -180,7 +251,7 @@ void SocketCore::readData(char* data, int& len, int timeout) {
   }
 }
 
-void SocketCore::peekData(char* data, int& len, int timeout) {
+void SocketCore::peekData(char* data, int& len, int timeout) const {
   if(!isReadable(timeout) ||
      !secure && (len = recv(sockfd, data, (size_t)len, MSG_PEEK)) < 0
 #ifdef HAVE_LIBSSL

+ 14 - 8
src/SocketCore.h

@@ -23,6 +23,7 @@
 #define _D_SOCKET_CORE_H_
 
 #include <string>
+#include <utility>
 #include "common.h"
 
 #ifdef HAVE_LIBSSL
@@ -45,50 +46,55 @@ private:
   SSL_CTX* sslCtx;
   SSL* ssl;
 #endif // HAVE_LIBSSL
+  void init();
+  SocketCore(int sockfd);
 public:
   SocketCore();
   ~SocketCore();
 
+  void beginListen();
+  void getAddrInfo(pair<string, int>& addrinfo) const;
+  SocketCore* acceptConnection() const;
   /**
    * Connects to the server named host and the destination port is port.
    * This method make socket non-blocking mode.
-   * To make the socket blocking mode, call setNonBlockingMode() after
+   * To make the socket blocking mode, call setBlockingMode() after
    * the connection is established.
    */
   void establishConnection(string host, int port);
 
-  void setNonBlockingMode();
+  void setBlockingMode() const;
 
   // Closes the connection which this socket object has
   void closeConnection();
 
   // examines whether the socket of this SocketCore object is available for writing.
   // returns true if the socket is available for writing, otherwise returns false.
-  bool isWritable(int timeout);
+  bool isWritable(int timeout) const;
 
   // examines whether the socket of this SocketCore object is available for reading.
   // returns true if the socket is available for reading, otherwise returns false.
-  bool isReadable(int timeout);
+  bool isReadable(int timeout) const;
 
   // writes characters into the socket. data is a pointer pointing the first
   // byte of the data and len is the length of the data.
-  void writeData(const char* data, int len, int timeout = 5);
+  void writeData(const char* data, int len, int timeout = 5) const;
 
   // Reads up to len bytes from this socket.
   // data is a pointer pointing the first
   // byte of the data, which must be allocated before this method is called.
   // len is the size of the allocated memory. When this method returns
   // successfully, len is replaced by the size of the read data.
-  void readData(char* data, int& len, int timeout = 5);
+  void readData(char* data, int& len, int timeout = 5) const;
   // Reads up to len bytes from this socket, but bytes are not removed from
   // this socket.
-  void peekData(char* data, int& len, int timeout = 5);
+  void peekData(char* data, int& len, int timeout = 5) const;
   
   /**
    * Makes this socket secure.
    * If the system has not OpenSSL, then this method do nothing.
    */
-  void initiateSecureConnection();
+  void initiateSecureConnection() ;
 };
 
 #endif // _D_SOCKET_CORE_H_

+ 11 - 0
src/Util.cc

@@ -108,3 +108,14 @@ void Util::slice(vector<string>& result, string src, char delim) {
     result.push_back(trim(term));
   } 
 }
+
+bool Util::endsWith(string target, string part) {
+  if(target.size() < part.size()) {
+    return false;
+  }
+  if(target.compare(target.size()-part.size(), part.size(), part, 0, part.size()) == 0) {
+    return true;
+  } else {
+    return false;
+  }
+}

+ 2 - 0
src/Util.h

@@ -49,6 +49,8 @@ public:
   static void slice(vector<string>& result, string src, char delim);
   
   static string trim(string src);
+
+  static bool endsWith(string target, string part);
 };
 
 #endif // _D_UTIL_H_

+ 9 - 0
src/main.cc

@@ -147,6 +147,15 @@ int main(int argc, char* argv[]) {
   Option* op = new Option();
   op->put("retry_wait", "5");
 
+  // TODO warning! Delete username/password line when commit
+  op->put("ftp_user", "anonymous");
+  op->put("ftp_passwd", "IE60USER@");
+  op->put("ftp_type", "I");
+
+  op->put("ftp_pasv_enabled", "true");
+  op->put("ftp_via_http_proxy", "tunnel");
+  op->put("http_abs_uri_request_enabled", "true");
+
   while(1) {
     int optIndex = 0;
     int lopt;

+ 6 - 0
src/message.h

@@ -28,10 +28,12 @@
 #define MSG_SEGMENT_CHANGED "CUID#%d - The segment changed. We send the request again with new Range header."
 #define MSG_REDIRECT "CUID#%d - Redirecting to %s"
 #define MSG_SENDING_HTTP_REQUEST "CUID#%d - Sending the request:\n%s"
+#define MSG_SENDING_FTP_REQUEST "CUID#%d - Sending the request: %s"
 #define MSG_RECEIVE_RESPONSE "CUID#%d - Response received:\n%s"
 #define MSG_DOWNLOAD_ABORTED "CUID#%d - Download aborted."
 #define MSG_RESTARTING_DOWNLOAD "CUID#%d - Restarting the download."
 #define MSG_MAX_RETRY "CUID#%d - The retry count reached its max value. Download aborted."
+#define MSG_UNREGISTER_CUID "CUID#%d - Unregistering cuid from segmentManager."
 
 #define MSG_SEGMENT_FILE_EXISTS "The segment file %s exists."
 #define MSG_SEGMENT_FILE_DOES_NOT_EXIST "The segment file %s does not exist."
@@ -44,12 +46,16 @@
 #define EX_INVALID_CHUNK_SIZE "Invalid chunk size."
 #define EX_TOO_LARGE_CHUNK "Too large chunk. size = %d"
 #define EX_INVALID_HEADER "Invalid header."
+#define EX_INVALID_RESPONSE "Invalid response."
 #define EX_NO_HEADER "No header found."
 #define EX_NO_STATUS_HEADER "No status header."
 #define EX_PROXY_CONNECTION_FAILED "Proxy connection failed."
+#define EX_CONNECTION_FAILED "Connection failed."
 #define EX_FILENAME_MISMATCH "The requested filename and the previously registered one are not same. %s != %s"
 #define EX_BAD_STATUS "The response status is not successful. status = %d"
 #define EX_TOO_LARGE_FILE "Too large file size. size = %d"
 #define EX_TRANSFER_ENCODING_NOT_SUPPORTED "Transfer encoding %s is not supported."
 #define EX_SSL_INIT_FAILURE "SSL initialization failed."
+#define EX_SIZE_MISMATCH "Size mismatch %d != %d"
+
 #endif // _D_MESSAGE_H_

+ 36 - 0
test/UtilTest.cc

@@ -10,6 +10,7 @@ class UtilTest:public CppUnit::TestFixture {
   CPPUNIT_TEST(testTrim);
   CPPUNIT_TEST(testSplit);
   CPPUNIT_TEST(testSlice);
+  CPPUNIT_TEST(testEndsWith);
   CPPUNIT_TEST_SUITE_END();
 private:
 
@@ -20,6 +21,7 @@ public:
   void testTrim();
   void testSplit();
   void testSlice();
+  void testEndsWith();
 };
 
 
@@ -75,3 +77,37 @@ void UtilTest::testSlice() {
   CPPUNIT_ASSERT_EQUAL(string("name2=value2"), *itr++);
   CPPUNIT_ASSERT_EQUAL(string("name3=value3"), *itr++);
 }
+
+void UtilTest::testEndsWith() {
+  string target = "abcdefg";
+  string part = "fg";
+  CPPUNIT_ASSERT(Util::endsWith(target, part));
+
+  target = "abdefg";
+  part = "g";
+  CPPUNIT_ASSERT(Util::endsWith(target, part));
+
+  target = "abdefg";
+  part = "eg";
+  CPPUNIT_ASSERT(!Util::endsWith(target, part));
+
+  target = "g";
+  part = "eg";
+  CPPUNIT_ASSERT(!Util::endsWith(target, part));
+
+  target = "g";
+  part = "g";
+  CPPUNIT_ASSERT(Util::endsWith(target, part));
+
+  target = "g";
+  part = "";
+  CPPUNIT_ASSERT(Util::endsWith(target, part));
+
+  target = "";
+  part = "";
+  CPPUNIT_ASSERT(Util::endsWith(target, part));
+
+  target = "";
+  part = "g";
+  CPPUNIT_ASSERT(!Util::endsWith(target, part));
+}