Tatsuhiro Tsujikawa 19 years ago
parent
commit
e0aa767f94

+ 207 - 0
src/FtpConnection.cc

@@ -0,0 +1,207 @@
+/* <!-- copyright */
+/*
+ * aria2 - a simple utility for downloading files faster
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* copyright --> */
+#include "FtpConnection.h"
+#include "Util.h"
+#include "DlAbortEx.h"
+#include "message.h"
+
+FtpConnection::FtpConnection(int cuid, const Socket* socket, const Request* req, const Option* op, const Logger* logger):cuid(cuid), socket(socket), req(req), option(op), logger(logger) {}
+
+FtpConnection::~FtpConnection() {}
+
+void FtpConnection::sendUser() const {
+  string request = "USER "+option->get("ftp_user")+"\r\n";
+  logger->info(MSG_SENDING_FTP_REQUEST, cuid, request.c_str());
+  socket->writeData(request);
+}
+
+void FtpConnection::sendPass() const {
+  string request = "PASS "+option->get("ftp_passwd")+"\r\n";
+  logger->info(MSG_SENDING_FTP_REQUEST, cuid, "PASS ********");
+  socket->writeData(request);
+}
+
+void FtpConnection::sendType() const {
+  string request = "TYPE "+option->get("ftp_type")+"\r\n";
+  logger->info(MSG_SENDING_FTP_REQUEST, cuid, request.c_str());
+  socket->writeData(request);
+}
+
+void FtpConnection::sendCwd() const {
+  string request = "CWD "+req->getDir()+"\r\n";
+  logger->info(MSG_SENDING_FTP_REQUEST, cuid, request.c_str());
+  socket->writeData(request);
+}
+
+void FtpConnection::sendSize() const {
+  string request = "SIZE "+req->getFile()+"\r\n";
+  logger->info(MSG_SENDING_FTP_REQUEST, cuid, request.c_str());
+  socket->writeData(request);
+}
+
+void FtpConnection::sendPasv() const {
+  string request = "PASV\r\n";
+  logger->info(MSG_SENDING_FTP_REQUEST, cuid, request.c_str());
+  socket->writeData(request);
+}
+
+Socket* FtpConnection::sendPort() const {
+  Socket* serverSocket = new Socket();
+  try {
+    serverSocket->beginListen();
+
+    pair<string, int> addrinfo;
+    socket->getAddrInfo(addrinfo);
+    int ipaddr[4]; 
+    sscanf(addrinfo.first.c_str(), "%d.%d.%d.%d",
+	   &ipaddr[0], &ipaddr[1], &ipaddr[2], &ipaddr[3]);
+    serverSocket->getAddrInfo(addrinfo);
+    string request = "PORT "+
+      Util::itos(ipaddr[0])+","+Util::itos(ipaddr[1])+","+
+      Util::itos(ipaddr[2])+","+Util::itos(ipaddr[3])+","+
+      Util::itos(addrinfo.second/256)+","+Util::itos(addrinfo.second%256)+"\r\n";
+    logger->info(MSG_SENDING_FTP_REQUEST, cuid, request.c_str());
+    socket->writeData(request);
+  } catch (Exception* ex) {
+    delete serverSocket;
+    throw;
+  }
+  return serverSocket;
+}
+
+void FtpConnection::sendRest(const Segment& segment) const {
+  string request = "REST "+Util::llitos(segment.sp+segment.ds)+"\r\n";
+  logger->info(MSG_SENDING_FTP_REQUEST, cuid, request.c_str());
+  socket->writeData(request);
+}
+
+void FtpConnection::sendRetr() const {
+  string request = "RETR "+req->getFile()+"\r\n";
+  logger->info(MSG_SENDING_FTP_REQUEST, cuid, request.c_str());
+  socket->writeData(request);
+}
+
+int FtpConnection::getStatus(string response) const {
+  int status;
+  // TODO we must handle when the response is not like "%d %*s"
+  // In this case, we return 0
+  if(sscanf(response.c_str(), "%d %*s", &status) == 1) {
+    return status;
+  } else {
+    return 0;
+  }
+}
+
+bool FtpConnection::isEndOfResponse(int status, string response) const {
+  if(response.size() <= 4) {
+    return false;
+  }
+  // if forth character of buf is '-', then multi line response is expected.
+  if(response.at(3) == '-') {
+    // multi line response
+    string::size_type p;
+    p = response.find("\r\n"+Util::itos(status)+" ");
+    if(p == string::npos) {
+      return false;
+    }
+  }
+  if(Util::endsWith(response, "\r\n")) {
+    return true;
+  } else {
+    return false;
+  }
+}
+
+bool FtpConnection::bulkReceiveResponse(pair<int, string>& response) {
+  char buf[1024];  
+  while(socket->isReadable(0)) {
+    int size = sizeof(buf)-1;
+    socket->readData(buf, size);
+    buf[size] = '\0';
+    strbuf += buf;
+  }
+  int status;
+  if(strbuf.size() >= 4) {
+    status = getStatus(strbuf);
+    if(status == 0) {
+      throw new DlAbortEx(EX_INVALID_RESPONSE);
+    }
+  } else {
+    return false;
+  }
+  if(isEndOfResponse(status, strbuf)) {
+    logger->info(MSG_RECEIVE_RESPONSE, cuid, strbuf.c_str());
+    response.first = status;
+    response.second = strbuf;
+    strbuf.erase();
+    return true;
+  } else {
+    // didn't receive response fully.
+    return false;
+  }
+}
+
+int FtpConnection::receiveResponse() {
+  pair<int, string> response;
+  if(bulkReceiveResponse(response)) {
+    return response.first;
+  } else {
+    return 0;
+  }
+}
+
+int FtpConnection::receiveSizeResponse(long long int& size) {
+  pair<int, string> response;
+  if(bulkReceiveResponse(response)) {
+    if(response.first == 213) {
+      sscanf(response.second.c_str(), "%*d %Ld", &size);
+    }
+    return response.first;
+  } else {
+    return 0;
+  }
+}
+
+int FtpConnection::receivePasvResponse(pair<string, int>& dest) {
+  pair<int, string> response;
+  if(bulkReceiveResponse(response)) {
+    if(response.first == 227) {
+      // we assume the format of response is "227 Entering Passive Mode (h1,h2,h3,h4,p1,p2)."
+      int h1, h2, h3, h4, p1, p2;
+      string::size_type p = response.second.find("(");
+      if(p >= 4) {
+	sscanf(response.second.substr(response.second.find("(")).c_str(),
+	       "(%d,%d,%d,%d,%d,%d).",
+	       &h1, &h2, &h3, &h4, &p1, &p2);
+	// ip address
+	dest.first = Util::itos(h1)+"."+Util::itos(h2)+"."+Util::itos(h3)+"."+Util::itos(h4);
+	// port number
+	dest.second = 256*p1+p2;
+      } else {
+	throw new DlAbortEx(EX_INVALID_RESPONSE);
+      }
+    }
+    return response.first;
+  } else {
+    return 0;
+  }
+}

+ 65 - 0
src/FtpConnection.h

@@ -0,0 +1,65 @@
+/* <!-- copyright */
+/*
+ * aria2 - a simple utility for downloading files faster
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* copyright --> */
+#ifndef _D_FTP_CONNECTION_H_
+#define _D_FTP_CONNECTION_H_
+
+#include "Socket.h"
+#include "Option.h"
+#include "Logger.h"
+#include "Segment.h"
+#include "Request.h"
+#include <utility>
+
+using namespace std;
+
+class FtpConnection {
+private:
+  int cuid;
+  const Socket* socket;
+  const Request* req;
+  const Option* option;
+  const Logger* logger;
+
+  string strbuf;
+
+  int getStatus(string response) const;
+  bool isEndOfResponse(int status, string response) const;
+  bool bulkReceiveResponse(pair<int, string>& response);
+public:
+  FtpConnection(int cuid, const Socket* socket, const Request* req, const Option* op, const Logger* logger);
+  ~FtpConnection();
+  void sendUser() const;
+  void sendPass() const;
+  void sendType() const;
+  void sendCwd() const;
+  void sendSize() const;
+  void sendPasv() const;
+  Socket* sendPort() const;
+  void sendRest(const Segment& segment) const;
+  void sendRetr() const;
+
+  int receiveResponse();
+  int receiveSizeResponse(long long int& size);
+  int receivePasvResponse(pair<string, int>& dest);
+};
+
+#endif // _D_FTP_CONNECTION_H_

+ 38 - 0
src/FtpDownloadCommand.cc

@@ -0,0 +1,38 @@
+/* <!-- copyright */
+/*
+ * aria2 - a simple utility for downloading files faster
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* copyright --> */
+#include "FtpDownloadCommand.h"
+
+FtpDownloadCommand::FtpDownloadCommand(int cuid, Request* req, DownloadEngine* e, Socket* dataSocket, Socket* ctrlSocket):
+  DownloadCommand(cuid, req, e, dataSocket)
+{
+  this->ctrlSocket = new Socket(*ctrlSocket);
+}
+
+FtpDownloadCommand::~FtpDownloadCommand() {
+  if(ctrlSocket != NULL) {
+    delete ctrlSocket;
+  }
+}
+
+TransferEncoding* FtpDownloadCommand::getTransferEncoding(string name) {
+  return NULL;
+}

+ 37 - 0
src/FtpDownloadCommand.h

@@ -0,0 +1,37 @@
+/* <!-- copyright */
+/*
+ * aria2 - a simple utility for downloading files faster
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* copyright --> */
+#ifndef _D_FTP_DOWNLOAD_COMMAND_H_
+#define _D_FTP_DOWNLOAD_COMMAND_H_
+
+#include "DownloadCommand.h"
+
+class FtpDownloadCommand : public DownloadCommand {
+private:
+  Socket* ctrlSocket;
+public:
+  FtpDownloadCommand(int cuid, Request* req, DownloadEngine* e, Socket* dataSocket, Socket* ctrlSocket);
+  ~FtpDownloadCommand();
+
+  TransferEncoding* getTransferEncoding(string name);
+};
+
+#endif // _D_FTP_DOWNLOAD_COMMAND_H_

+ 84 - 0
src/FtpInitiateConnectionCommand.cc

@@ -0,0 +1,84 @@
+/* <!-- copyright */
+/*
+ * aria2 - a simple utility for downloading files faster
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* copyright --> */
+#include "FtpInitiateConnectionCommand.h"
+#include "FtpNegotiationCommand.h"
+#include "HttpRequestCommand.h"
+#include "FtpTunnelRequestCommand.h"
+#include "DlAbortEx.h"
+#include "message.h"
+
+FtpInitiateConnectionCommand::FtpInitiateConnectionCommand(int cuid, Request* req, DownloadEngine* e):AbstractCommand(cuid, req, e) {}
+
+FtpInitiateConnectionCommand::~FtpInitiateConnectionCommand() {}
+
+bool FtpInitiateConnectionCommand::executeInternal(Segment segment) {
+  if(!e->segmentMan->downloadStarted) {
+    e->segmentMan->filename = req->getFile();
+    bool segFileExists = e->segmentMan->segmentFileExists();
+    if(segFileExists) {
+      e->segmentMan->load();
+      e->diskWriter->openExistingFile(e->segmentMan->getFilePath());
+      e->segmentMan->downloadStarted = true;
+    } else {
+      e->diskWriter->initAndOpenFile(e->segmentMan->getFilePath());
+    }
+  }
+
+  socket = new Socket();
+  Command* command;
+  if(useHttpProxy()) {
+    e->logger->info(MSG_CONNECTING_TO_SERVER, cuid,
+		    e->option->get("http_proxy_host").c_str(),
+		    e->option->getAsInt("http_proxy_port"));
+    socket->establishConnection(e->option->get("http_proxy_host"),
+				e->option->getAsInt("http_proxy_port"));
+    
+    if(useHttpProxyGet()) {
+      command = new HttpRequestCommand(cuid, req, e, socket);
+    } else if(useHttpProxyConnect()) {
+      command = new FtpTunnelRequestCommand(cuid, req, e, socket);
+    } else {
+      // TODO
+      throw new DlAbortEx("ERROR");
+    }
+  } else {
+    e->logger->info(MSG_CONNECTING_TO_SERVER, cuid, req->getHost().c_str(),
+		    req->getPort());
+    socket->establishConnection(req->getHost(), req->getPort());
+    command = new FtpNegotiationCommand(cuid, req, e, socket);
+  }
+  e->commands.push(command);
+  return true;
+}
+
+bool FtpInitiateConnectionCommand::useHttpProxy() const {
+  return e->option->defined("http_proxy_enabled") &&
+    e->option->get("http_proxy_enabled") == "true";
+}
+
+bool FtpInitiateConnectionCommand::useHttpProxyGet() const {
+  return useHttpProxy() && e->option->get("ftp_via_http_proxy") == "get";
+}
+
+bool FtpInitiateConnectionCommand::useHttpProxyConnect() const {
+  return useHttpProxy() && e->option->get("ftp_via_http_proxy") == "tunnel";
+}

+ 39 - 0
src/FtpInitiateConnectionCommand.h

@@ -0,0 +1,39 @@
+/* <!-- copyright */
+/*
+ * aria2 - a simple utility for downloading files faster
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* copyright --> */
+#ifndef _D_FTP_INITIATE_CONNECTION_COMMAND_H_
+#define _D_FTP_INITIATE_CONNECTION_COMMAND_H_
+
+#include "AbstractCommand.h"
+
+class FtpInitiateConnectionCommand : public AbstractCommand {
+private:
+  bool useHttpProxy() const;
+  bool useHttpProxyGet() const;
+  bool useHttpProxyConnect() const;
+protected:
+  bool executeInternal(Segment segment);
+public:
+  FtpInitiateConnectionCommand(int cuid, Request* req, DownloadEngine* e);
+  ~FtpInitiateConnectionCommand();
+};
+
+#endif // _D_FTP_INITIATE_CONNECTION_COMMAND_H_

+ 328 - 0
src/FtpNegotiationCommand.cc

@@ -0,0 +1,328 @@
+/* <!-- copyright */
+/*
+ * aria2 - a simple utility for downloading files faster
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* copyright --> */
+#include "FtpNegotiationCommand.h"
+#include "FtpDownloadCommand.h"
+#include "DlAbortEx.h"
+#include "message.h"
+
+FtpNegotiationCommand::FtpNegotiationCommand(int cuid, Request* req, DownloadEngine* e, Socket* s):
+  AbstractCommand(cuid, req, e, s),
+  dataSocket(NULL), serverSocket(NULL), sequence(SEQ_RECV_GREETING)
+{
+  ftp = new FtpConnection(cuid, socket, req, e->option, e->logger);
+  setReadCheckSocket(NULL);
+  setWriteCheckSocket(socket);
+}
+
+FtpNegotiationCommand::~FtpNegotiationCommand() {
+  if(dataSocket != NULL) {
+    delete dataSocket;
+  }
+  if(serverSocket != NULL) {
+    delete serverSocket;
+  }
+  delete ftp;
+}
+
+bool FtpNegotiationCommand::executeInternal(Segment segment) {
+  while(processSequence(segment));
+  if(sequence == SEQ_RETRY) {
+    return prepareForRetry(0);
+  } else if(sequence == SEQ_NEGOTIATION_COMPLETED) {
+    FtpDownloadCommand* command = new FtpDownloadCommand(cuid, req, e, dataSocket, socket);
+    e->commands.push(command);
+    return true;
+  } else {
+    e->commands.push(this);
+    return false;
+  }
+}
+
+bool FtpNegotiationCommand::recvGreeting() {
+  socket->setBlockingMode();
+  int status = ftp->receiveResponse();
+  if(status == 0) {
+    return false;
+  }
+  if(status != 220) {
+    throw new DlAbortEx(EX_CONNECTION_FAILED);
+  }
+  sequence = SEQ_SEND_USER;
+
+  setReadCheckSocket(socket);
+  setWriteCheckSocket(NULL);
+
+  return true;
+}
+
+bool FtpNegotiationCommand::sendUser() {
+  ftp->sendUser();
+  sequence = SEQ_RECV_USER;
+  return false;
+}
+
+bool FtpNegotiationCommand::recvUser() {
+  int status = ftp->receiveResponse();
+  switch(status) {
+  case 0:
+    return false;
+  case 230:
+    sequence = SEQ_SEND_TYPE;
+    break;
+  case 331:
+    sequence = SEQ_SEND_PASS;
+    break;
+  default:
+    throw new DlAbortEx(EX_BAD_STATUS, status);
+  }
+  return true;
+}
+
+bool FtpNegotiationCommand::sendPass() {
+  ftp->sendPass();
+  sequence = SEQ_RECV_PASS;
+  return false;
+}
+
+bool FtpNegotiationCommand::recvPass() {
+  int status = ftp->receiveResponse();
+  if(status == 0) {
+    return false;
+  }
+  if(status != 230) {
+    throw new DlAbortEx(EX_BAD_STATUS, status);
+  }
+  sequence = SEQ_SEND_TYPE;
+  return true;
+}
+
+bool FtpNegotiationCommand::sendType() {
+  ftp->sendType();
+  sequence = SEQ_RECV_TYPE;
+  return false;
+}
+
+bool FtpNegotiationCommand::recvType() {
+  int status = ftp->receiveResponse();
+  if(status == 0) {
+    return false;
+  }
+  if(status != 200) {
+    throw new DlAbortEx(EX_BAD_STATUS, status);
+  }
+  sequence = SEQ_SEND_CWD;
+  return true;
+}
+
+bool FtpNegotiationCommand::sendCwd() {
+  ftp->sendCwd();
+  sequence = SEQ_RECV_CWD;
+  return false;
+}
+
+bool FtpNegotiationCommand::recvCwd() {
+  int status = ftp->receiveResponse();
+  if(status == 0) {
+    return false;
+  }
+  if(status != 250) {
+    throw new DlAbortEx(EX_BAD_STATUS, status);
+  }
+  sequence = SEQ_SEND_SIZE;
+  return true;
+}
+
+bool FtpNegotiationCommand::sendSize() {
+  ftp->sendSize();
+  sequence = SEQ_RECV_SIZE;
+  return false;
+}
+
+bool FtpNegotiationCommand::recvSize() {
+  long long int size = 0;
+  int status = ftp->receiveSizeResponse(size);
+  if(status == 0) {
+    return false;
+  }
+  if(status != 213) {
+    throw new DlAbortEx(EX_BAD_STATUS, status);
+  }
+  // TODO check the value of size
+  if(!e->segmentMan->downloadStarted) {
+    e->segmentMan->downloadStarted = true;
+    e->segmentMan->totalSize = size;
+  } else if(e->segmentMan->totalSize != size) {
+    throw new DlAbortEx(EX_SIZE_MISMATCH, e->segmentMan->totalSize, size);
+  }
+  if(e->option->get("ftp_pasv_enabled") == "true") {
+    sequence = SEQ_SEND_PASV;
+  } else {
+    sequence = SEQ_SEND_PORT;
+  }
+  return true;
+}
+
+bool FtpNegotiationCommand::sendPort() {
+  serverSocket = ftp->sendPort();
+  sequence = SEQ_RECV_PORT;
+  return false;
+}
+
+bool FtpNegotiationCommand::recvPort() {
+  int status = ftp->receiveResponse();
+  if(status == 0) {
+    return false;
+  }
+  if(status != 200) {
+    throw new DlAbortEx(EX_BAD_STATUS, status);
+  }
+  sequence = SEQ_SEND_REST;
+  return true;
+}
+
+bool FtpNegotiationCommand::sendPasv() {
+  ftp->sendPasv();
+  sequence = SEQ_RECV_PASV;
+  return false;
+}
+
+bool FtpNegotiationCommand::recvPasv() {
+  pair<string, int> dest;
+  int status = ftp->receivePasvResponse(dest);
+  if(status == 0) {
+    return false;
+  }
+  if(status != 227) {
+    throw new DlAbortEx(EX_BAD_STATUS, status);
+  }
+  // make a data connection to the server.
+  dataSocket = new Socket();
+
+  e->logger->info(MSG_CONNECTING_TO_SERVER, cuid,
+		  dest.first.c_str(),
+		  dest.second);
+  dataSocket->establishConnection(dest.first, dest.second);
+
+  setReadCheckSocket(NULL);
+  setWriteCheckSocket(dataSocket);
+
+  sequence = SEQ_SEND_REST_PASV;
+  return false;
+}
+
+bool FtpNegotiationCommand::sendRestPasv(const Segment& segment) {
+  dataSocket->setBlockingMode();
+  setReadCheckSocket(socket);
+  setWriteCheckSocket(NULL);
+  return sendRest(segment);
+}
+
+bool FtpNegotiationCommand::sendRest(const Segment& segment) {
+  ftp->sendRest(segment);
+  sequence = SEQ_RECV_REST;
+  return false;
+}
+
+bool FtpNegotiationCommand::recvRest() {
+  int status = ftp->receiveResponse();
+  if(status == 0) {
+    return false;
+  }
+  // TODO if we recieve negative response, then we set e->segmentMan->splittable = false, and continue.
+  if(status != 350) {
+    throw new DlAbortEx(EX_BAD_STATUS, status);
+  }
+  sequence = SEQ_SEND_RETR;
+  return true;
+}
+
+bool FtpNegotiationCommand::sendRetr() {
+  ftp->sendRetr();
+  sequence = SEQ_RECV_RETR;
+  return false;
+}
+
+bool FtpNegotiationCommand::recvRetr() {
+  int status = ftp->receiveResponse();
+  if(status == 0) {
+    return false;
+  }
+  if(status != 150) {
+    throw new DlAbortEx(EX_BAD_STATUS, status);
+  }
+  if(e->option->get("ftp_pasv_enabled") != "true") {
+    assert(serverSocket);
+    dataSocket = serverSocket->acceptConnection();
+  }
+  sequence = SEQ_NEGOTIATION_COMPLETED;
+
+  return false;
+}
+
+bool FtpNegotiationCommand::processSequence(const Segment& segment) {
+  bool doNextSequence = true;
+  switch(sequence) {
+  case SEQ_RECV_GREETING:
+    return recvGreeting();
+  case SEQ_SEND_USER:
+    return sendUser();
+  case SEQ_RECV_USER:
+    return recvUser();
+  case SEQ_SEND_PASS:
+    return sendPass();
+  case SEQ_RECV_PASS:
+    return recvPass();
+  case SEQ_SEND_TYPE:
+    return sendType();
+  case SEQ_RECV_TYPE:
+    return recvType();
+  case SEQ_SEND_CWD:
+    return sendCwd();
+  case SEQ_RECV_CWD:
+    return recvCwd();
+  case SEQ_SEND_SIZE:
+    return sendSize();
+  case SEQ_RECV_SIZE:
+    return recvSize();
+  case SEQ_SEND_PORT:
+    return sendPort();
+  case SEQ_RECV_PORT:
+    return recvPort();
+  case SEQ_SEND_PASV:
+    return sendPasv();
+  case SEQ_RECV_PASV:
+    return recvPasv();
+  case SEQ_SEND_REST_PASV:
+    return sendRestPasv(segment);
+  case SEQ_SEND_REST:
+    return sendRest(segment);
+  case SEQ_RECV_REST:
+    return recvRest();
+  case SEQ_SEND_RETR:
+    return sendRetr();
+  case SEQ_RECV_RETR:
+    return recvRetr();
+  default:
+    abort();
+  }
+  return doNextSequence;
+}

+ 87 - 0
src/FtpNegotiationCommand.h

@@ -0,0 +1,87 @@
+/* <!-- copyright */
+/*
+ * aria2 - a simple utility for downloading files faster
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* copyright --> */
+#ifndef _D_FTP_NEGOTIATION_COMMAND_H_
+#define _D_FTP_NEGOTIATION_COMMAND_H_
+
+#include "AbstractCommand.h"
+#include "FtpConnection.h"
+
+class FtpNegotiationCommand : public AbstractCommand {
+private:
+  enum Seq {
+    SEQ_RECV_GREETING,
+    SEQ_SEND_USER,
+    SEQ_RECV_USER,
+    SEQ_SEND_PASS,
+    SEQ_RECV_PASS,
+    SEQ_SEND_TYPE,
+    SEQ_RECV_TYPE,
+    SEQ_SEND_CWD,
+    SEQ_RECV_CWD,
+    SEQ_SEND_SIZE,
+    SEQ_RECV_SIZE,
+    SEQ_SEND_PORT,
+    SEQ_RECV_PORT,
+    SEQ_SEND_PASV,
+    SEQ_RECV_PASV,
+    SEQ_SEND_REST_PASV,
+    SEQ_SEND_REST,
+    SEQ_RECV_REST,
+    SEQ_SEND_RETR,
+    SEQ_RECV_RETR,
+    SEQ_NEGOTIATION_COMPLETED,
+    SEQ_RETRY
+  };
+  bool recvGreeting();
+  bool sendUser();
+  bool recvUser();
+  bool sendPass();
+  bool recvPass();
+  bool sendType();
+  bool recvType();
+  bool sendCwd();
+  bool recvCwd();
+  bool sendSize();
+  bool recvSize();
+  bool sendPort();
+  bool recvPort();
+  bool sendPasv();
+  bool recvPasv();
+  bool sendRest(const Segment& segment);
+  bool sendRestPasv(const Segment& segment);
+  bool recvRest();
+  bool sendRetr();
+  bool recvRetr();
+  bool processSequence(const Segment& segment);
+
+  Socket* dataSocket;
+  Socket* serverSocket;
+  int sequence;
+  FtpConnection* ftp;
+protected:
+  bool executeInternal(Segment segment);
+public:
+  FtpNegotiationCommand(int cuid, Request* req, DownloadEngine* e, Socket* s);
+  ~FtpNegotiationCommand();
+};
+
+#endif // _D_FTP_NEGOTIATION_COMMAND_H_

+ 41 - 0
src/FtpTunnelRequestCommand.cc

@@ -0,0 +1,41 @@
+/* <!-- copyright */
+/*
+ * aria2 - a simple utility for downloading files faster
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* copyright --> */
+#include "FtpTunnelRequestCommand.h"
+#include "FtpTunnelResponseCommand.h"
+#include "HttpConnection.h"
+
+FtpTunnelRequestCommand::FtpTunnelRequestCommand(int cuid, Request* req, DownloadEngine* e, Socket* s):AbstractCommand(cuid, req, e, s) {
+  setReadCheckSocket(NULL);
+  setWriteCheckSocket(NULL);
+}
+
+FtpTunnelRequestCommand::~FtpTunnelRequestCommand() {}
+
+bool FtpTunnelRequestCommand::executeInternal(Segment segment) {
+  socket->setBlockingMode();
+  HttpConnection httpConnection(cuid, socket, req, e->option, e->logger);
+  httpConnection.sendProxyRequest();
+
+  FtpTunnelResponseCommand* command = new FtpTunnelResponseCommand(cuid, req, e, socket);
+  e->commands.push(command);
+  return true;
+}

+ 35 - 0
src/FtpTunnelRequestCommand.h

@@ -0,0 +1,35 @@
+/* <!-- copyright */
+/*
+ * aria2 - a simple utility for downloading files faster
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* copyright --> */
+#ifndef _D_FTP_TUNNEL_REQUEST_COMMAND_H_
+#define _D_FTP_TUNNEL_REQUEST_COMMAND_H_
+
+#include "AbstractCommand.h"
+
+class FtpTunnelRequestCommand : public AbstractCommand {
+protected:
+  bool executeInternal(Segment segment);
+public:
+  FtpTunnelRequestCommand(int cuid, Request* req, DownloadEngine* e, Socket* s);
+  ~FtpTunnelRequestCommand();
+};
+
+#endif // _D_FTP_TUNNEL_REQUEST_COMMAND_H_

+ 49 - 0
src/FtpTunnelResponseCommand.cc

@@ -0,0 +1,49 @@
+/* <!-- copyright */
+/*
+ * aria2 - a simple utility for downloading files faster
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* copyright --> */
+#include "FtpTunnelResponseCommand.h"
+#include "FtpNegotiationCommand.h"
+#include "DlRetryEx.h"
+#include "message.h"
+
+FtpTunnelResponseCommand::FtpTunnelResponseCommand(int cuid, Request* req, DownloadEngine* e, Socket* s):AbstractCommand(cuid, req, e, s) {
+  http = new HttpConnection(cuid, socket, req, e->option, e->logger);
+}
+
+FtpTunnelResponseCommand::~FtpTunnelResponseCommand() {
+  delete http;
+}
+
+bool FtpTunnelResponseCommand::executeInternal(Segment segment) {
+  HttpHeader 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);
+  }
+  FtpNegotiationCommand* command = new FtpNegotiationCommand(cuid, req, e, socket);
+  e->commands.push(command);
+  return true;
+}

+ 39 - 0
src/FtpTunnelResponseCommand.h

@@ -0,0 +1,39 @@
+/* <!-- copyright */
+/*
+ * aria2 - a simple utility for downloading files faster
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* copyright --> */
+#ifndef _D_FTP_TUNNEL_RESPONSE_COMMAND_H_
+#define _D_FTP_TUNNEL_RESPONSE_COMMAND_H_
+
+#include "AbstractCommand.h"
+#include "HttpConnection.h"
+
+class FtpTunnelResponseCommand : public AbstractCommand {
+private:
+  HttpConnection* http;
+protected:
+    bool executeInternal(Segment segment);
+public:
+  FtpTunnelResponseCommand(int cuid, Request* req, DownloadEngine* e, Socket* s);
+  ~FtpTunnelResponseCommand();
+};
+
+#endif // _D_FTP_TUNNEL_RESPONSE_COMMAND_H_
+

+ 66 - 0
src/HttpHeader.cc

@@ -0,0 +1,66 @@
+/* <!-- copyright */
+/*
+ * aria2 - a simple utility for downloading files faster
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* copyright --> */
+#include "HttpHeader.h"
+
+void HttpHeader::put(const string& name, const string& value) {
+  multimap<string, string>::value_type vt(name, value);
+  table.insert(vt);
+}
+
+bool HttpHeader::defined(const string& name) const {
+  return table.count(name) == 1;
+}
+
+string HttpHeader::getFirst(const string& name) const {
+  multimap<string, string>::const_iterator itr = table.find(name);
+  if(itr == table.end()) {
+    return "";
+  } else {
+    return (*itr).second;
+  }
+}
+
+vector<string> HttpHeader::get(const string& name) const {
+  vector<string> v;
+  for(multimap<string, string>::const_iterator itr = table.find(name); itr != table.end(); itr++) {
+    v.push_back((*itr).second);
+  }
+  return v;
+}
+
+int HttpHeader::getFirstAsInt(const string& name) const {
+  multimap<string, string>::const_iterator itr = table.find(name);
+  if(itr == table.end()) {
+    return 0;
+  } else {
+    return (int)strtol((*itr).second.c_str(), NULL, 10);
+  }
+}
+
+long long int HttpHeader::getFirstAsLLInt(const string& name) const {
+  multimap<string, string>::const_iterator itr = table.find(name);
+  if(itr == table.end()) {
+    return 0;
+  } else {
+    return strtoll((*itr).second.c_str(), NULL, 10);
+  }
+}

+ 46 - 0
src/HttpHeader.h

@@ -0,0 +1,46 @@
+/* <!-- copyright */
+/*
+ * aria2 - a simple utility for downloading files faster
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* copyright --> */
+#ifndef _D_HTTP_HEADER_H_
+#define _D_HTTP_HEADER_H_
+
+#include <map>
+#include <vector>
+#include <string>
+
+using namespace std;
+
+class HttpHeader {
+private:
+  multimap<string, string> table;
+public:
+  HttpHeader() {}
+  ~HttpHeader() {}
+
+  void put(const string& name, const string& value);
+  bool defined(const string& name) const;
+  string getFirst(const string& name) const;
+  vector<string> get(const string& name) const;
+  int getFirstAsInt(const string& name) const;
+  long long int getFirstAsLLInt(const string& name) const;
+};
+
+#endif // _D_HTTP_HEADER_H_