]> git.saurik.com Git - apt.git/blobdiff - methods/ftp.cc
Correct neuros problem with no source directory
[apt.git] / methods / ftp.cc
index efc50c205bf813f1ed4b1713532d4e2cb2259fbb..bbc4a3f22704f5b9945ca9de544eaaa2c9eba8f4 100644 (file)
@@ -1,9 +1,9 @@
 // -*- mode: cpp; mode: fold -*-
 // Description                                                         /*{{{*/
-// $Id: ftp.cc,v 1.12 1999/05/28 07:04:45 jgg Exp $
+// $Id: ftp.cc,v 1.28 2001/05/22 04:02:00 jgg Exp $
 /* ######################################################################
 
-   HTTP Aquire Method - This is the FTP aquire method for APT.
+   FTP Aquire Method - This is the FTP aquire method for APT.
 
    This is a very simple implementation that does not try to optimize
    at all. Commands are sent syncronously with the FTP server (as the
@@ -18,7 +18,7 @@
 #include <apt-pkg/fileutl.h>
 #include <apt-pkg/acquire-method.h>
 #include <apt-pkg/error.h>
-#include <apt-pkg/md5.h>
+#include <apt-pkg/hashes.h>
 
 #include <sys/stat.h>
 #include <sys/time.h>
@@ -28,6 +28,7 @@
 #include <stdio.h>
 #include <errno.h>
 #include <stdarg.h>
+#include <iostream>
 
 // Internet stuff
 #include <netinet/in.h>
 #include <netdb.h>
 
 #include "rfc2553emu.h"
+#include "connect.h"
 #include "ftp.h"
                                                                        /*}}}*/
 
+using namespace std;
+
+/* This table is for the EPRT and EPSV commands, it maps the OS address
+   family to the IETF address families */
+struct AFMap
+{
+   unsigned long Family;
+   unsigned long IETFFamily;
+};
+
+#ifndef AF_INET6
+struct AFMap AFMap[] = {{AF_INET,1},{}};
+#else
+struct AFMap AFMap[] = {{AF_INET,1},{AF_INET6,2},{}};
+#endif
+
 unsigned long TimeOut = 120;
 URI Proxy;
 string FtpMethod::FailFile;
@@ -52,7 +70,7 @@ FTPConn::FTPConn(URI Srv) : Len(0), ServerFd(-1), DataFd(-1),
                             DataListenFd(-1), ServerName(Srv)
 {
    Debug = _config->FindB("Debug::Acquire::Ftp",false);
-   memset(&PasvAddr,0,sizeof(PasvAddr));
+   PasvAddr = 0;
 }
                                                                        /*}}}*/
 // FTPConn::~FTPConn - Destructor                                      /*{{{*/
@@ -74,16 +92,16 @@ void FTPConn::Close()
    DataFd = -1;
    close(DataListenFd);
    DataListenFd = -1;
-   memset(&PasvAddr,0,sizeof(PasvAddr));
+   
+   if (PasvAddr != 0)
+      freeaddrinfo(PasvAddr);
+   PasvAddr = 0;
 }
                                                                        /*}}}*/
 // FTPConn::Open - Open a new connection                               /*{{{*/
 // ---------------------------------------------------------------------
 /* Connect to the server using a non-blocking connection and perform a 
    login. */
-string LastHost;
-int LastPort = 0;
-struct addrinfo *LastHostAddr = 0;
 bool FTPConn::Open(pkgAcqMethod *Owner)
 {
    // Use the already open connection if possible.
@@ -91,7 +109,7 @@ bool FTPConn::Open(pkgAcqMethod *Owner)
       return true;
    
    Close();
-
+   
    // Determine the proxy setting
    if (getenv("ftp_proxy") == 0)
    {
@@ -110,6 +128,13 @@ bool FTPConn::Open(pkgAcqMethod *Owner)
    else
       Proxy = getenv("ftp_proxy");
    
+   // Parse no_proxy, a , separated list of domains
+   if (getenv("no_proxy") != 0)
+   {
+      if (CheckDomainList(ServerName.Host,getenv("no_proxy")) == true)
+        Proxy = "";
+   }
+   
    // Determine what host and port to use based on the proxy settings
    int Port = 0;
    string Host;   
@@ -125,72 +150,28 @@ bool FTPConn::Open(pkgAcqMethod *Owner)
         Port = Proxy.Port;
       Host = Proxy.Host;
    }
-   
-   /* We used a cached address record.. Yes this is against the spec but
-      the way we have setup our rotating dns suggests that this is more
-      sensible */
-   if (LastHost != Host || LastPort != Port)
-   {
-      Owner->Status("Connecting to %s",Host.c_str());
-
-      // Lookup the host
-      char S[30] = "ftp";
-      if (Port != 0)
-        snprintf(S,sizeof(S),"%u",Port);
 
-      // Free the old address structure
-      if (LastHostAddr != 0)
-      {
-        freeaddrinfo(LastHostAddr);
-        LastHostAddr = 0;
-      }
-      
-      // We only understand SOCK_STREAM sockets.
-      struct addrinfo Hints;
-      memset(&Hints,0,sizeof(Hints));
-      Hints.ai_socktype = SOCK_STREAM;
-      
-      // Resolve both the host and service simultaneously
-      if (getaddrinfo(Host.c_str(),S,&Hints,&LastHostAddr) != 0 ||
-         LastHostAddr == 0)
-        return _error->Error("Could not resolve '%s'",Host.c_str());
-
-      LastHost = Host;
-      LastPort = Port;
-   }
-
-   // Get the printable IP address
-   char Name[NI_MAXHOST];
-   Name[0] = 0;
-   getnameinfo(LastHostAddr->ai_addr,LastHostAddr->ai_addrlen,
-              Name,sizeof(Name),0,0,NI_NUMERICHOST);
-   Owner->Status("Connecting to %s (%s)",Host.c_str(),Name);
-   
-   // Get a socket
-   if ((ServerFd = socket(LastHostAddr->ai_family,LastHostAddr->ai_socktype,
-                         LastHostAddr->ai_protocol)) < 0)
-      return _error->Errno("socket","Could not create a socket");
-   SetNonBlock(ServerFd,true);
-   if (connect(ServerFd,LastHostAddr->ai_addr,LastHostAddr->ai_addrlen) < 0 &&
-       errno != EINPROGRESS)
-      return _error->Errno("connect","Cannot initiate the connection "
-                          "to %s (%s).",Host.c_str(),Name);
-   Peer = *((struct sockaddr_in *)LastHostAddr->ai_addr);
-      
-   /* This implements a timeout for connect by opening the connection
-      nonblocking */
-   if (WaitFd(ServerFd,true,TimeOut) == false)
-      return _error->Error("Could not connect to %s (%s), "
-                          "connection timed out",Host.c_str(),Name);
-   unsigned int Err;
-   unsigned int Len = sizeof(Err);
-   if (getsockopt(ServerFd,SOL_SOCKET,SO_ERROR,&Err,&Len) != 0)
-      return _error->Errno("getsockopt","Failed");
-   if (Err != 0)
-      return _error->Error("Could not connect to %s (%s).",Host.c_str(),Name);
+   /* Connect to the remote server. Since FTP is connection oriented we
+      want to make sure we get a new server every time we reconnect */
+   RotateDNS();
+   if (Connect(Host,Port,"ftp",21,ServerFd,TimeOut,Owner) == false)
+      return false;
 
+   // Login must be before getpeername otherwise dante won't work.
    Owner->Status("Logging in");
-   return Login();
+   bool Res = Login();
+   
+   // Get the remote server's address
+   PeerAddrLen = sizeof(PeerAddr);
+   if (getpeername(ServerFd,(sockaddr *)&PeerAddr,&PeerAddrLen) != 0)
+      return _error->Errno("getpeername","Unable to determine the peer name");
+   
+   // Get the local machine's address
+   ServerAddrLen = sizeof(ServerAddr);
+   if (getsockname(ServerFd,(sockaddr *)&ServerAddr,&ServerAddrLen) != 0)
+      return _error->Errno("getsockname","Unable to determine the local name");
+   
+   return Res;
 }
                                                                        /*}}}*/
 // FTPConn::Login - Login to the remote server                         /*{{{*/
@@ -204,7 +185,7 @@ bool FTPConn::Login()
    
    // Setup the variables needed for authentication
    string User = "anonymous";
-   string Pass = "apt_get_ftp_2.0@debian.linux.user";
+   string Pass = "apt_get_ftp_2.1@debian.linux.user";
 
    // Fill in the user/pass
    if (ServerName.User.empty() == false)
@@ -237,7 +218,7 @@ bool FTPConn::Login()
       if (_config->Exists("Acquire::FTP::Passive::" + ServerName.Host) == true)
         TryPassive = _config->FindB("Acquire::FTP::Passive::" + ServerName.Host,true);
       else
-        TryPassive = _config->FindB("Acquire::FTP::Passive",true);
+        TryPassive = _config->FindB("Acquire::FTP::Passive",true);      
    }
    else
    {      
@@ -294,6 +275,12 @@ bool FTPConn::Login()
       }            
    }
 
+   // Force the use of extended commands
+   if (_config->Exists("Acquire::FTP::ForceExtended::" + ServerName.Host) == true)
+      ForceExtended = _config->FindB("Acquire::FTP::ForceExtended::" + ServerName.Host,true);
+   else
+      ForceExtended = _config->FindB("Acquire::FTP::ForceExtended",false);
+   
    // Binary mode
    if (WriteMsg(Tag,Msg,"TYPE I") == false)
       return false;
@@ -341,10 +328,13 @@ bool FTPConn::ReadLine(string &Text)
       
       // Suck it back
       int Res = read(ServerFd,Buffer + Len,sizeof(Buffer) - Len);
+      if (Res == 0)
+        _error->Error("Server closed the connection");
       if (Res <= 0)
       {
+        _error->Errno("read","Read error");
         Close();
-        return _error->Errno("read","Read error");
+        return false;
       }      
       Len += Res;
    }
@@ -450,8 +440,9 @@ bool FTPConn::WriteMsg(unsigned int &Ret,string &Text,const char *Fmt,...)
       int Res = write(ServerFd,S + Start,Len);
       if (Res <= 0)
       {
+        _error->Errno("write","Write Error");
         Close();
-        return _error->Errno("write","Write Error");
+        return false;
       }
       
       Len -= Res;
@@ -465,10 +456,19 @@ bool FTPConn::WriteMsg(unsigned int &Ret,string &Text,const char *Fmt,...)
 // ---------------------------------------------------------------------
 /* Try to enter passive mode, the return code does not indicate if passive
    mode could or could not be established, only if there was a fatal error. 
-   Borrowed mostly from lftp. We have to enter passive mode every time 
- we make a data connection :| */
+   We have to enter passive mode every time we make a data connection :| */
 bool FTPConn::GoPasv()
 {
+   /* The PASV command only works on IPv4 sockets, even though it could
+      in theory suppory IPv6 via an all zeros reply */
+   if (((struct sockaddr *)&PeerAddr)->sa_family != AF_INET || 
+       ForceExtended == true)
+      return ExtGoPasv();
+   
+   if (PasvAddr != 0)
+      freeaddrinfo(PasvAddr);
+   PasvAddr = 0;
+   
    // Try to enable pasv mode
    unsigned int Tag;
    string Msg;
@@ -478,41 +478,139 @@ bool FTPConn::GoPasv()
    // Unsupported function
    string::size_type Pos = Msg.find('(');
    if (Tag >= 400 || Pos == string::npos)
-   {
-      memset(&PasvAddr,0,sizeof(PasvAddr));
       return true;
-   }
 
    // Scan it
    unsigned a0,a1,a2,a3,p0,p1;
    if (sscanf(Msg.c_str() + Pos,"(%u,%u,%u,%u,%u,%u)",&a0,&a1,&a2,&a3,&p0,&p1) != 6)
+      return true;
+   
+   /* Some evil servers return 0 to mean their addr. We can actually speak
+      to these servers natively using IPv6 */
+   if (a0 == 0 && a1 == 0 && a2 == 0 && a3 == 0)
    {
-      memset(&PasvAddr,0,sizeof(PasvAddr));
+      // Get the IP in text form
+      char Name[NI_MAXHOST];
+      char Service[NI_MAXSERV];
+      getnameinfo((struct sockaddr *)&PeerAddr,PeerAddrLen,
+                 Name,sizeof(Name),Service,sizeof(Service),
+                 NI_NUMERICHOST|NI_NUMERICSERV);
+      
+      struct addrinfo Hints;
+      memset(&Hints,0,sizeof(Hints));
+      Hints.ai_socktype = SOCK_STREAM;
+      Hints.ai_family = ((struct sockaddr *)&PeerAddr)->sa_family;
+      Hints.ai_flags |= AI_NUMERICHOST;
+      
+      // Get a new passive address.
+      char Port[100];
+      snprintf(Port,sizeof(Port),"%u",(p0 << 8) + p1);
+      if (getaddrinfo(Name,Port,&Hints,&PasvAddr) != 0)
+        return true;
       return true;
    }
    
-   // lftp used this horrid byte order manipulation.. Ik.
-   PasvAddr.sin_family = AF_INET;
-   unsigned char *a;
-   unsigned char *p;
-   a = (unsigned char *)&PasvAddr.sin_addr;
-   p = (unsigned char *)&PasvAddr.sin_port;
+   struct addrinfo Hints;
+   memset(&Hints,0,sizeof(Hints));
+   Hints.ai_socktype = SOCK_STREAM;
+   Hints.ai_family = AF_INET;
+   Hints.ai_flags |= AI_NUMERICHOST;
    
-   // Some evil servers return 0 to mean their addr
-   if (a0 == 0 && a1 == 0 && a2 == 0 && a3 == 0)
+   // Get a new passive address.
+   char Port[100];
+   snprintf(Port,sizeof(Port),"%u",(p0 << 8) + p1);
+   char Name[100];
+   snprintf(Name,sizeof(Name),"%u.%u.%u.%u",a0,a1,a2,a3);
+   if (getaddrinfo(Name,Port,&Hints,&PasvAddr) != 0)
+      return true;
+   return true;
+}
+                                                                       /*}}}*/
+// FTPConn::ExtGoPasv - Enter Extended Passive mode                    /*{{{*/
+// ---------------------------------------------------------------------
+/* Try to enter extended passive mode. See GoPasv above and RFC 2428 */
+bool FTPConn::ExtGoPasv()
+{
+   if (PasvAddr != 0)
+      freeaddrinfo(PasvAddr);
+   PasvAddr = 0;
+   
+   // Try to enable pasv mode
+   unsigned int Tag;
+   string Msg;
+   if (WriteMsg(Tag,Msg,"EPSV") == false)
+      return false;
+   
+   // Unsupported function
+   string::size_type Pos = Msg.find('(');
+   if (Tag >= 400 || Pos == string::npos)
+      return true;
+
+   // Scan it   
+   string::const_iterator List[4];
+   unsigned Count = 0;
+   Pos++;
+   for (string::const_iterator I = Msg.begin() + Pos; I < Msg.end(); I++)
    {
-      PasvAddr.sin_addr = Peer.sin_addr;
+      if (*I != Msg[Pos])
+        continue;
+      if (Count >= 4)
+        return true;
+      List[Count++] = I;
+   }
+   if (Count != 4)
+      return true;
+   
+   // Break it up ..
+   unsigned long Proto = 0;
+   unsigned long Port = 0;
+   string IP;
+   IP = string(List[1]+1,List[2]);
+   Port = atoi(string(List[2]+1,List[3]).c_str());
+   if (IP.empty() == false)
+      Proto = atoi(string(List[0]+1,List[1]).c_str());
+   
+   if (Port == 0)
+      return false;
+
+   // String version of the port
+   char PStr[100];
+   snprintf(PStr,sizeof(PStr),"%lu",Port);
+
+   // Get the IP in text form
+   struct addrinfo Hints;
+   memset(&Hints,0,sizeof(Hints));
+   Hints.ai_socktype = SOCK_STREAM;
+   Hints.ai_flags |= AI_NUMERICHOST;
+   
+   /* The RFC defined case, connect to the old IP/protocol using the
+      new port. */
+   if (IP.empty() == true)
+   {
+      // Get the IP in text form
+      char Name[NI_MAXHOST];
+      char Service[NI_MAXSERV];
+      getnameinfo((struct sockaddr *)&PeerAddr,PeerAddrLen,
+                 Name,sizeof(Name),Service,sizeof(Service),
+                 NI_NUMERICHOST|NI_NUMERICSERV);
+      IP = Name;
+      Hints.ai_family = ((struct sockaddr *)&PeerAddr)->sa_family;
    }
    else
    {
-      a[0] = a0; 
-      a[1] = a1; 
-      a[2] = a2; 
-      a[3] = a3;
+      // Get the family..
+      Hints.ai_family = 0;
+      for (unsigned J = 0; AFMap[J].Family != 0; J++)
+        if (AFMap[J].IETFFamily == Proto)
+           Hints.ai_family = AFMap[J].Family;
+      if (Hints.ai_family == 0)
+        return true;
    }
    
-   p[0] = p0;
-   p[1] = p1;
+   // Get a new passive address.
+   int Res;
+   if ((Res = getaddrinfo(IP.c_str(),PStr,&Hints,&PasvAddr)) != 0)
+      return true;
    
    return true;
 }
@@ -554,18 +652,7 @@ bool FTPConn::ModTime(const char *Path, time_t &Time)
       return true;
    
    // Parse it
-   struct tm tm;
-   memset(&tm,0,sizeof(tm));   
-   if (sscanf(Msg.c_str(),"%4d%2d%2d%2d%2d%2d",&tm.tm_year,&tm.tm_mon,
-             &tm.tm_mday,&tm.tm_hour,&tm.tm_min,&tm.tm_sec) != 6)
-      return true;
-   
-   tm.tm_year -= 1900;
-   tm.tm_mon--;
-   
-   /* We use timegm from the GNU C library, libapt-pkg will provide this
-      symbol if it does not exist */
-   Time = timegm(&tm);
+   StrToTime(Msg,Time);
    return true;
 }
                                                                        /*}}}*/
@@ -584,34 +671,35 @@ bool FTPConn::CreateDataFd()
         return false;
       
       // Oops, didn't work out, don't bother trying again.
-      if (PasvAddr.sin_port == 0)
+      if (PasvAddr == 0)
         TryPassive = false;
    }
    
    // Passive mode?
-   if (PasvAddr.sin_port != 0)
+   if (PasvAddr != 0)
    {
       // Get a socket
-      if ((DataFd = socket(AF_INET,SOCK_STREAM,0)) < 0)
+      if ((DataFd = socket(PasvAddr->ai_family,PasvAddr->ai_socktype,
+                          PasvAddr->ai_protocol)) < 0)
         return _error->Errno("socket","Could not create a socket");
       
       // Connect to the server
       SetNonBlock(DataFd,true);
-      if (connect(DataFd,(sockaddr *)&PasvAddr,sizeof(PasvAddr)) < 0 &&
+      if (connect(DataFd,PasvAddr->ai_addr,PasvAddr->ai_addrlen) < 0 &&
          errno != EINPROGRESS)
         return _error->Errno("socket","Could not create a socket");
    
       /* This implements a timeout for connect by opening the connection
          nonblocking */
-      if (WaitFd(ServerFd,true,TimeOut) == false)
+      if (WaitFd(DataFd,true,TimeOut) == false)
         return _error->Error("Could not connect data socket, connection timed out");
       unsigned int Err;
       unsigned int Len = sizeof(Err);
-      if (getsockopt(ServerFd,SOL_SOCKET,SO_ERROR,&Err,&Len) != 0)
+      if (getsockopt(DataFd,SOL_SOCKET,SO_ERROR,&Err,&Len) != 0)
         return _error->Errno("getsockopt","Failed");
       if (Err != 0)
-        return _error->Error("Could not connect.");
-      
+        return _error->Error("Could not connect passive socket.");
+
       return true;
    }
    
@@ -619,43 +707,91 @@ bool FTPConn::CreateDataFd()
    close(DataListenFd);
    DataListenFd = -1;
 
-   // Get a socket
-   if ((DataListenFd = socket(AF_INET,SOCK_STREAM,0)) < 0)
+   // Get the information for a listening socket.
+   struct addrinfo *BindAddr = 0;
+   struct addrinfo Hints;
+   memset(&Hints,0,sizeof(Hints));
+   Hints.ai_socktype = SOCK_STREAM;
+   Hints.ai_flags |= AI_PASSIVE;
+   Hints.ai_family = ((struct sockaddr *)&ServerAddr)->sa_family;
+   int Res;
+   if ((Res = getaddrinfo(0,"0",&Hints,&BindAddr)) != 0)
+      return _error->Error("getaddrinfo was unable to get a listening socket");
+   
+   // Construct the socket
+   if ((DataListenFd = socket(BindAddr->ai_family,BindAddr->ai_socktype,
+                             BindAddr->ai_protocol)) < 0)
+   {
+      freeaddrinfo(BindAddr);
       return _error->Errno("socket","Could not create a socket");
+   }
    
    // Bind and listen
-   sockaddr_in Addr;
-   memset(&Addr,0,sizeof(Addr));
-   if (bind(DataListenFd,(sockaddr *)&Addr,sizeof(Addr)) < 0)
+   if (bind(DataListenFd,BindAddr->ai_addr,BindAddr->ai_addrlen) < 0)
+   {
+      freeaddrinfo(BindAddr);
       return _error->Errno("bind","Could not bind a socket");
+   }
+   freeaddrinfo(BindAddr);   
    if (listen(DataListenFd,1) < 0)
       return _error->Errno("listen","Could not listen on the socket");
    SetNonBlock(DataListenFd,true);
    
    // Determine the name to send to the remote
-   sockaddr_in Addr2;
-   socklen_t Jnk = sizeof(Addr);
-   if (getsockname(DataListenFd,(sockaddr *)&Addr,&Jnk) < 0)
-      return _error->Errno("getsockname","Could not determine the socket's name");
-   Jnk = sizeof(Addr2);
-   if (getsockname(ServerFd,(sockaddr *)&Addr2,&Jnk) < 0)
+   struct sockaddr_storage Addr;
+   socklen_t AddrLen = sizeof(Addr);
+   if (getsockname(DataListenFd,(sockaddr *)&Addr,&AddrLen) < 0)
       return _error->Errno("getsockname","Could not determine the socket's name");
-   
-   // This bit ripped from qftp
-   unsigned long badr = ntohl(*(unsigned long *)&Addr2.sin_addr);
-   unsigned long bp = ntohs(Addr.sin_port);
 
-   // Send the port command
+   // Reverse the address. We need the server address and the data port.
+   char Name[NI_MAXHOST];
+   char Service[NI_MAXSERV];
+   char Service2[NI_MAXSERV];
+   getnameinfo((struct sockaddr *)&Addr,AddrLen,
+              Name,sizeof(Name),Service,sizeof(Service),
+              NI_NUMERICHOST|NI_NUMERICSERV);
+   getnameinfo((struct sockaddr *)&ServerAddr,ServerAddrLen,
+              Name,sizeof(Name),Service2,sizeof(Service2),
+              NI_NUMERICHOST|NI_NUMERICSERV);
+
+   // Send off an IPv4 address in the old port format
+   if (((struct sockaddr *)&Addr)->sa_family == AF_INET && 
+       ForceExtended == false)
+   {
+      // Convert the dots in the quad into commas
+      for (char *I = Name; *I != 0; I++)
+        if (*I == '.')
+           *I = ',';
+      unsigned long Port = atoi(Service);
+      
+      // Send the port command
+      unsigned int Tag;
+      string Msg;
+      if (WriteMsg(Tag,Msg,"PORT %s,%d,%d",
+                  Name,
+                  (int)(Port >> 8) & 0xff, (int)(Port & 0xff)) == false)
+        return false;
+      if (Tag >= 400)
+        return _error->Error("Unable to send PORT command");
+      return true;
+   }
+
+   // Construct an EPRT command
+   unsigned Proto = 0;
+   for (unsigned J = 0; AFMap[J].Family != 0; J++)
+      if (AFMap[J].Family == ((struct sockaddr *)&Addr)->sa_family)
+        Proto = AFMap[J].IETFFamily;
+   if (Proto == 0)
+      return _error->Error("Unkonwn address family %u (AF_*)",
+                          ((struct sockaddr *)&Addr)->sa_family);
+   
+   // Send the EPRT command
    unsigned int Tag;
    string Msg;
-   if (WriteMsg(Tag,Msg,"PORT %d,%d,%d,%d,%d,%d", 
-               (int) (badr >> 24) & 0xff, (int) (badr >> 16) & 0xff, 
-               (int) (badr >> 8) & 0xff,  (int) badr & 0xff, 
-               (int) (bp >> 8) & 0xff, (int) bp & 0xff) == false)
+   if (WriteMsg(Tag,Msg,"EPRT |%u|%s|%s|",Proto,Name,Service) == false)
       return false;
    if (Tag >= 400)
-      return _error->Error("Unable to send port command");
-
+      return _error->Error("EPRT failed, server said: %s",Msg.c_str());
    return true;
 }
                                                                        /*}}}*/
@@ -666,7 +802,7 @@ bool FTPConn::CreateDataFd()
 bool FTPConn::Finalize()
 {
    // Passive mode? Do nothing
-   if (PasvAddr.sin_port != 0)
+   if (PasvAddr != 0)
       return true;
    
    // Close any old socket..
@@ -695,7 +831,7 @@ bool FTPConn::Finalize()
 /* This opens a data connection, sends REST and RETR and then
    transfers the file over. */
 bool FTPConn::Get(const char *Path,FileFd &To,unsigned long Resume,
-                 MD5Summation &MD5,bool &Missing)
+                 Hashes &Hash,bool &Missing)
 {
    Missing = false;
    if (CreateDataFd() == false)
@@ -719,7 +855,7 @@ bool FTPConn::Get(const char *Path,FileFd &To,unsigned long Resume,
    
    if (Resume != 0)
    {
-      if (MD5.AddFD(To.Fd(),Resume) == false)
+      if (Hash.AddFD(To.Fd(),Resume) == false)
       {
         _error->Errno("read","Problem hashing file");
         return false;
@@ -763,7 +899,7 @@ bool FTPConn::Get(const char *Path,FileFd &To,unsigned long Resume,
         break;
       }
    
-      MD5.Add(Buffer,Res);
+      Hash.Add(Buffer,Res);
       if (To.Write(Buffer,Res) == false)
       {
         Close();
@@ -803,7 +939,7 @@ FtpMethod::FtpMethod() : pkgAcqMethod("1.0",SendConfig)
 void FtpMethod::SigTerm(int)
 {
    if (FailFd == -1)
-      exit(100);
+      _exit(100);
    close(FailFd);
    
    // Timestamp
@@ -812,7 +948,7 @@ void FtpMethod::SigTerm(int)
    UBuf.modtime = FailTime;
    utime(FailFile.c_str(),&UBuf);
    
-   exit(100);
+   _exit(100);
 }
                                                                        /*}}}*/
 // FtpMethod::Configuration - Handle a configuration message           /*{{{*/
@@ -881,6 +1017,7 @@ bool FtpMethod::Fetch(FetchItem *Itm)
       {
         Res.Size = Buf.st_size;
         Res.LastModified = Buf.st_mtime;
+        Res.ResumePoint = Buf.st_size;
         URIDone(Res);
         return true;
       }
@@ -891,7 +1028,7 @@ bool FtpMethod::Fetch(FetchItem *Itm)
    }
    
    // Open the file
-   MD5Summation MD5;
+   Hashes Hash;
    {
       FileFd Fd(Itm->DestFile,FileFd::WriteAny);
       if (_error->PendingError() == true)
@@ -904,8 +1041,16 @@ bool FtpMethod::Fetch(FetchItem *Itm)
       FailFd = Fd.Fd();
       
       bool Missing;
-      if (Server->Get(File,Fd,Res.ResumePoint,MD5,Missing) == false)
+      if (Server->Get(File,Fd,Res.ResumePoint,Hash,Missing) == false)
       {
+        Fd.Close();
+        
+        // Timestamp
+        struct utimbuf UBuf;
+        UBuf.actime = FailTime;
+        UBuf.modtime = FailTime;
+        utime(FailFile.c_str(),&UBuf);
+        
         // If the file is missing we hard fail otherwise transient fail
         if (Missing == true)
            return false;
@@ -917,11 +1062,10 @@ bool FtpMethod::Fetch(FetchItem *Itm)
    }
    
    Res.LastModified = FailTime;
-   Res.MD5Sum = MD5.Result();
+   Res.TakeHashes(Hash);
    
    // Timestamp
    struct utimbuf UBuf;
-   time(&UBuf.actime);
    UBuf.actime = FailTime;
    UBuf.modtime = FailTime;
    utime(Queue->DestFile.c_str(),&UBuf);
@@ -940,12 +1084,15 @@ int main(int argc,const char *argv[])
    if (getenv("ftp_proxy") != 0)
    {
       URI Proxy = string(getenv("ftp_proxy"));
+      
+      // Run the HTTP method
       if (Proxy.Access == "http")
       {
         // Copy over the environment setting
         char S[300];
         snprintf(S,sizeof(S),"http_proxy=%s",getenv("ftp_proxy"));
         putenv(S);
+        putenv("no_proxy=");
         
         // Run the http method
         string Path = flNotFile(argv[0]) + "/http";