]> git.saurik.com Git - apt.git/blobdiff - methods/ftp.cc
bump to 0.3.18
[apt.git] / methods / ftp.cc
index 1068a8fdef2edc6b73b8ce07047649e78e3b6aa1..ac12d09131d20a1f61ffc7f0b978f2fa7e67e777 100644 (file)
@@ -1,6 +1,6 @@
 // -*- mode: cpp; mode: fold -*-
 // Description                                                         /*{{{*/
 // -*- mode: cpp; mode: fold -*-
 // Description                                                         /*{{{*/
-// $Id: ftp.cc,v 1.1 1999/03/15 06:00:59 jgg Exp $
+// $Id: ftp.cc,v 1.19 2000/01/10 03:44:54 jgg Exp $
 /* ######################################################################
 
    HTTP Aquire Method - This is the FTP aquire method for APT.
 /* ######################################################################
 
    HTTP Aquire Method - This is the FTP aquire method for APT.
@@ -9,7 +9,9 @@
    at all. Commands are sent syncronously with the FTP server (as the
    rfc recommends, but it is not really necessary..) and no tricks are
    done to speed things along.
    at all. Commands are sent syncronously with the FTP server (as the
    rfc recommends, but it is not really necessary..) and no tricks are
    done to speed things along.
-                                                              
+                       
+   RFC 2428 describes the IPv6 FTP behavior
+   
    ##################################################################### */
                                                                        /*}}}*/
 // Include Files                                                       /*{{{*/
    ##################################################################### */
                                                                        /*}}}*/
 // Include Files                                                       /*{{{*/
@@ -17,7 +19,6 @@
 #include <apt-pkg/acquire-method.h>
 #include <apt-pkg/error.h>
 #include <apt-pkg/md5.h>
 #include <apt-pkg/acquire-method.h>
 #include <apt-pkg/error.h>
 #include <apt-pkg/md5.h>
-#include "ftp.h"
 
 #include <sys/stat.h>
 #include <sys/time.h>
 
 #include <sys/stat.h>
 #include <sys/time.h>
 #include <arpa/inet.h>
 #include <netdb.h>
 
 #include <arpa/inet.h>
 #include <netdb.h>
 
+#include "rfc2553emu.h"
+#include "connect.h"
+#include "ftp.h"
                                                                        /*}}}*/
 
 unsigned long TimeOut = 120;
 URI Proxy;
                                                                        /*}}}*/
 
 unsigned long TimeOut = 120;
 URI Proxy;
-bool Debug;
+string FtpMethod::FailFile;
+int FtpMethod::FailFd = -1;
+time_t FtpMethod::FailTime = 0;
 
 // FTPConn::FTPConn - Constructor                                      /*{{{*/
 // ---------------------------------------------------------------------
 
 // FTPConn::FTPConn - Constructor                                      /*{{{*/
 // ---------------------------------------------------------------------
@@ -46,7 +52,7 @@ bool Debug;
 FTPConn::FTPConn(URI Srv) : Len(0), ServerFd(-1), DataFd(-1), 
                             DataListenFd(-1), ServerName(Srv)
 {
 FTPConn::FTPConn(URI Srv) : Len(0), ServerFd(-1), DataFd(-1), 
                             DataListenFd(-1), ServerName(Srv)
 {
-   Debug = true;
+   Debug = _config->FindB("Debug::Acquire::Ftp",false);
    memset(&PasvAddr,0,sizeof(PasvAddr));
 }
                                                                        /*}}}*/
    memset(&PasvAddr,0,sizeof(PasvAddr));
 }
                                                                        /*}}}*/
@@ -76,9 +82,7 @@ void FTPConn::Close()
 // ---------------------------------------------------------------------
 /* Connect to the server using a non-blocking connection and perform a 
    login. */
 // ---------------------------------------------------------------------
 /* Connect to the server using a non-blocking connection and perform a 
    login. */
-string LastHost;
-in_addr LastHostA;
-bool FTPConn::Open()
+bool FTPConn::Open(pkgAcqMethod *Owner)
 {
    // Use the already open connection if possible.
    if (ServerFd != -1)
 {
    // Use the already open connection if possible.
    if (ServerFd != -1)
@@ -105,14 +109,12 @@ bool FTPConn::Open()
       Proxy = getenv("ftp_proxy");
    
    // Determine what host and port to use based on the proxy settings
       Proxy = getenv("ftp_proxy");
    
    // Determine what host and port to use based on the proxy settings
-   int Port = 21;
+   int Port = 0;
    string Host;   
    if (Proxy.empty() == true)
    {
       if (ServerName.Port != 0)
         Port = ServerName.Port;
    string Host;   
    if (Proxy.empty() == true)
    {
       if (ServerName.Port != 0)
         Port = ServerName.Port;
-      else
-        ServerName.Port = Port;
       Host = ServerName.Host;
    }
    else
       Host = ServerName.Host;
    }
    else
@@ -121,50 +123,15 @@ bool FTPConn::Open()
         Port = Proxy.Port;
       Host = Proxy.Host;
    }
         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)
-   {
-//      Owner->Status("Connecting to %s",Host.c_str());
-
-      // Lookup the host
-      hostent *Addr = gethostbyname(Host.c_str());
-      if (Addr == 0 || Addr->h_addr_list[0] == 0)
-        return _error->Error("Could not resolve '%s'",Host.c_str());
-      LastHost = Host;
-      LastHostA = *(in_addr *)(Addr->h_addr_list[0]);
-   }
-   
-//   Owner->Status("Connecting to %s (%s)",Host.c_str(),inet_ntoa(LastHostA));
-   
-   // Get a socket
-   if ((ServerFd = socket(AF_INET,SOCK_STREAM,0)) < 0)
-      return _error->Errno("socket","Could not create a socket");
-   
-   // Connect to the server
-   struct sockaddr_in server;
-   server.sin_family = AF_INET;
-   server.sin_port = htons(Port);
-   server.sin_addr = LastHostA;
-   SetNonBlock(ServerFd,true);
-   if (connect(ServerFd,(sockaddr *)&server,sizeof(server)) < 0 &&
-       errno != EINPROGRESS)
-      return _error->Errno("socket","Could not create a socket");
-   Peer = server;
-   
-   /* This implements a timeout for connect by opening the connection
-      nonblocking */
-   if (WaitFd(ServerFd,true,TimeOut) == false)
-      return _error->Error("Could not connect, connection timed out");
-   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.");
 
 
+   // Connect to the remote server
+   if (Connect(Host,Port,"ftp",21,ServerFd,TimeOut,Owner) == false)
+      return false;
+   socklen_t Len = sizeof(Peer);
+   if (getpeername(ServerFd,(sockaddr *)&Peer,&Len) != 0)
+      return _error->Errno("getpeername","Unable to determine the peer name");
+   
+   Owner->Status("Logging in");
    return Login();
 }
                                                                        /*}}}*/
    return Login();
 }
                                                                        /*}}}*/
@@ -209,17 +176,10 @@ bool FTPConn::Login()
         return _error->Error("PASS failed, server said: %s",Msg.c_str());
       
       // Enter passive mode
         return _error->Error("PASS failed, server said: %s",Msg.c_str());
       
       // Enter passive mode
-      TryPassive = false;
-      if (_config->Exists("Acquire::FTP::Passive::" + ServerName.Host) == true &&
-         _config->FindB("Acquire::FTP::Passive::" + ServerName.Host,true) == true)
-      {
-        TryPassive = true;
-      }      
+      if (_config->Exists("Acquire::FTP::Passive::" + ServerName.Host) == true)
+        TryPassive = _config->FindB("Acquire::FTP::Passive::" + ServerName.Host,true);
       else
       else
-      {
-        if (_config->FindB("Acquire::FTP::Passive",true) == true)
-           TryPassive = true;
-      }      
+        TryPassive = _config->FindB("Acquire::FTP::Passive",true);
    }
    else
    {      
    }
    else
    {      
@@ -244,7 +204,10 @@ bool FTPConn::Login()
         
         // Substitute the variables into the command
         char SitePort[20];
         
         // Substitute the variables into the command
         char SitePort[20];
-        sprintf(SitePort,"%u",ServerName.Port);
+        if (ServerName.Port != 0)
+           sprintf(SitePort,"%u",ServerName.Port);
+        else
+           strcpy(SitePort,"21");
         string Tmp = Opts->Value;
         Tmp = SubstVar(Tmp,"$(PROXY_USER)",Proxy.User);
         Tmp = SubstVar(Tmp,"$(PROXY_PASS)",Proxy.Password);
         string Tmp = Opts->Value;
         Tmp = SubstVar(Tmp,"$(PROXY_USER)",Proxy.User);
         Tmp = SubstVar(Tmp,"$(PROXY_PASS)",Proxy.Password);
@@ -262,19 +225,14 @@ bool FTPConn::Login()
       
       // Enter passive mode
       TryPassive = false;
       
       // Enter passive mode
       TryPassive = false;
-      if (_config->Exists("Acquire::FTP::Passive::" + ServerName.Host) == true &&
-         _config->FindB("Acquire::FTP::Passive::" + ServerName.Host,true) == true)
-      {
-        TryPassive = true;
-      }      
+      if (_config->Exists("Acquire::FTP::Passive::" + ServerName.Host) == true)
+        TryPassive = _config->FindB("Acquire::FTP::Passive::" + ServerName.Host,true);
       else
       {
       else
       {
-        if (_config->Exists("Acquire::FTP::Proxy::Passive") == true &&
-            _config->FindB("Acquire::FTP::Proxy::Passive",true) == true)
-           TryPassive = true;
+        if (_config->Exists("Acquire::FTP::Proxy::Passive") == true)
+           TryPassive = _config->FindB("Acquire::FTP::Proxy::Passive",true);
         else
         else
-           if (_config->FindB("Acquire::FTP::Passive",true) == true)
-              TryPassive = true;              
+           TryPassive = _config->FindB("Acquire::FTP::Passive",true);
       }            
    }
 
       }            
    }
 
@@ -292,6 +250,9 @@ bool FTPConn::Login()
 /* This performs a very simple buffered read. */
 bool FTPConn::ReadLine(string &Text)
 {
 /* This performs a very simple buffered read. */
 bool FTPConn::ReadLine(string &Text)
 {
+   if (ServerFd == -1)
+      return false;
+   
    // Suck in a line
    while (Len < sizeof(Buffer))
    {
    // Suck in a line
    while (Len < sizeof(Buffer))
    {
@@ -315,12 +276,19 @@ bool FTPConn::ReadLine(string &Text)
 
       // Wait for some data..
       if (WaitFd(ServerFd,false,TimeOut) == false)
 
       // Wait for some data..
       if (WaitFd(ServerFd,false,TimeOut) == false)
+      {
+        Close();
         return _error->Error("Connection timeout");
         return _error->Error("Connection timeout");
+      }
       
       // Suck it back
       
       // Suck it back
-      int Res = read(ServerFd,Buffer,sizeof(Buffer) - Len);
+      int Res = read(ServerFd,Buffer + Len,sizeof(Buffer) - Len);
       if (Res <= 0)
       if (Res <= 0)
-        return _error->Errno("read","Read error");
+      {
+        _error->Errno("read","Read error");
+        Close();
+        return false;
+      }      
       Len += Res;
    }
 
       Len += Res;
    }
 
@@ -348,7 +316,7 @@ bool FTPConn::ReadResp(unsigned int &Ret,string &Text)
    if (*End == ' ')
    {
       if (Debug == true)
    if (*End == ' ')
    {
       if (Debug == true)
-        cout << "<- '" << QuoteString(Text,"") << "'" << endl;
+        cerr << "<- '" << QuoteString(Text,"") << "'" << endl;
       return true;
    }
    
       return true;
    }
    
@@ -390,7 +358,7 @@ bool FTPConn::ReadResp(unsigned int &Ret,string &Text)
    }      
 
    if (Debug == true && _error->PendingError() == false)
    }      
 
    if (Debug == true && _error->PendingError() == false)
-      cout << "<- '" << QuoteString(Text,"") << "'" << endl;
+      cerr << "<- '" << QuoteString(Text,"") << "'" << endl;
       
    return !_error->PendingError();
 }
       
    return !_error->PendingError();
 }
@@ -409,7 +377,7 @@ bool FTPConn::WriteMsg(unsigned int &Ret,string &Text,const char *Fmt,...)
    strcat(S,"\r\n");
  
    if (Debug == true)
    strcat(S,"\r\n");
  
    if (Debug == true)
-      cout << "-> '" << QuoteString(S,"") << "'" << endl;
+      cerr << "-> '" << QuoteString(S,"") << "'" << endl;
 
    // Send it off
    unsigned long Len = strlen(S);
 
    // Send it off
    unsigned long Len = strlen(S);
@@ -417,11 +385,19 @@ bool FTPConn::WriteMsg(unsigned int &Ret,string &Text,const char *Fmt,...)
    while (Len != 0)
    {
       if (WaitFd(ServerFd,true,TimeOut) == false)
    while (Len != 0)
    {
       if (WaitFd(ServerFd,true,TimeOut) == false)
+      {
+        Close();
         return _error->Error("Connection timeout");
         return _error->Error("Connection timeout");
+      }
       
       int Res = write(ServerFd,S + Start,Len);
       if (Res <= 0)
       
       int Res = write(ServerFd,S + Start,Len);
       if (Res <= 0)
-        return _error->Errno("write","Write Error");
+      {
+        _error->Errno("write","Write Error");
+        Close();
+        return false;
+      }
+      
       Len -= Res;
       Start += Res;
    }
       Len -= Res;
       Start += Res;
    }
@@ -488,19 +464,20 @@ bool FTPConn::GoPasv()
 // FTPConn::Size - Return the size of a file                           /*{{{*/
 // ---------------------------------------------------------------------
 /* Grab the file size from the server, 0 means no size or empty file */
 // FTPConn::Size - Return the size of a file                           /*{{{*/
 // ---------------------------------------------------------------------
 /* Grab the file size from the server, 0 means no size or empty file */
-unsigned long FTPConn::Size(const char *Path)
+bool FTPConn::Size(const char *Path,unsigned long &Size)
 {
    // Query the size
    unsigned int Tag;
    string Msg;
 {
    // Query the size
    unsigned int Tag;
    string Msg;
+   Size = 0;
    if (WriteMsg(Tag,Msg,"SIZE %s",Path) == false)
       return false;
    
    char *End;
    if (WriteMsg(Tag,Msg,"SIZE %s",Path) == false)
       return false;
    
    char *End;
-   unsigned long Size = strtol(Msg.c_str(),&End,10);
+   Size = strtol(Msg.c_str(),&End,10);
    if (Tag >= 400 || End == Msg.c_str())
    if (Tag >= 400 || End == Msg.c_str())
-      return 0;
-   return Size;
+      Size = 0;
+   return true;
 }
                                                                        /*}}}*/
 // FTPConn::ModTime - Return the modification time of the file         /*{{{*/
 }
                                                                        /*}}}*/
 // FTPConn::ModTime - Return the modification time of the file         /*{{{*/
@@ -521,18 +498,7 @@ bool FTPConn::ModTime(const char *Path, time_t &Time)
       return true;
    
    // Parse it
       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;
 }
                                                                        /*}}}*/
    return true;
 }
                                                                        /*}}}*/
@@ -569,7 +535,7 @@ bool FTPConn::CreateDataFd()
         return _error->Errno("socket","Could not create a socket");
    
       /* This implements a timeout for connect by opening the connection
         return _error->Errno("socket","Could not create a socket");
    
       /* This implements a timeout for connect by opening the connection
-       nonblocking */
+         nonblocking */
       if (WaitFd(ServerFd,true,TimeOut) == false)
         return _error->Error("Could not connect data socket, connection timed out");
       unsigned int Err;
       if (WaitFd(ServerFd,true,TimeOut) == false)
         return _error->Error("Could not connect data socket, connection timed out");
       unsigned int Err;
@@ -583,24 +549,23 @@ bool FTPConn::CreateDataFd()
    }
    
    // Port mode :<
    }
    
    // Port mode :<
-   if (DataListenFd == -1)
-   {
-      // Get a socket
-      if ((DataListenFd = socket(AF_INET,SOCK_STREAM,0)) < 0)
-        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)
-        return _error->Errno("bind","Could not bind a socket");
-      if (listen(DataListenFd,1) < 0)
-        return _error->Errno("listen","Could not listen on the socket");
-      SetNonBlock(DataListenFd,true);
-   }
+   close(DataListenFd);
+   DataListenFd = -1;
+
+   // Get a socket
+   if ((DataListenFd = socket(AF_INET,SOCK_STREAM,0)) < 0)
+      return _error->Errno("socket","Could not create a socket");
    
    
-   // Determine the name to send to the remote
+   // Bind and listen
    sockaddr_in Addr;
    sockaddr_in Addr;
+   memset(&Addr,0,sizeof(Addr));
+   if (bind(DataListenFd,(sockaddr *)&Addr,sizeof(Addr)) < 0)
+      return _error->Errno("bind","Could not bind a socket");
+   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)
    sockaddr_in Addr2;
    socklen_t Jnk = sizeof(Addr);
    if (getsockname(DataListenFd,(sockaddr *)&Addr,&Jnk) < 0)
@@ -652,6 +617,9 @@ bool FTPConn::Finalize()
    if (DataFd < 0)
       return _error->Errno("accept","Unable to accept connection");
 
    if (DataFd < 0)
       return _error->Errno("accept","Unable to accept connection");
 
+   close(DataListenFd);
+   DataListenFd = -1;
+   
    return true;
 }
                                                                        /*}}}*/
    return true;
 }
                                                                        /*}}}*/
@@ -659,13 +627,15 @@ bool FTPConn::Finalize()
 // ---------------------------------------------------------------------
 /* This opens a data connection, sends REST and RETR and then
    transfers the file over. */
 // ---------------------------------------------------------------------
 /* 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)
+bool FTPConn::Get(const char *Path,FileFd &To,unsigned long Resume,
+                 MD5Summation &MD5,bool &Missing)
 {
 {
+   Missing = false;
    if (CreateDataFd() == false)
       return false;
 
    unsigned int Tag;
    if (CreateDataFd() == false)
       return false;
 
    unsigned int Tag;
-   string Msg;
+   string Msg;   
    if (Resume != 0)
    {      
       if (WriteMsg(Tag,Msg,"REST %u",Resume) == false)
    if (Resume != 0)
    {      
       if (WriteMsg(Tag,Msg,"REST %u",Resume) == false)
@@ -676,13 +646,29 @@ bool FTPConn::Get(const char *Path,FileFd &To,unsigned long Resume)
    
    if (To.Truncate(Resume) == false)
       return false;
    
    if (To.Truncate(Resume) == false)
       return false;
+
+   if (To.Seek(0) == false)
+      return false;
+   
+   if (Resume != 0)
+   {
+      if (MD5.AddFD(To.Fd(),Resume) == false)
+      {
+        _error->Errno("read","Problem hashing file");
+        return false;
+      }
+   }
    
    // Send the get command
    if (WriteMsg(Tag,Msg,"RETR %s",Path) == false)
       return false;
    
    if (Tag >= 400)
    
    // Send the get command
    if (WriteMsg(Tag,Msg,"RETR %s",Path) == false)
       return false;
    
    if (Tag >= 400)
+   {
+      if (Tag == 550)
+        Missing = true;
       return _error->Error("Unable to fetch file, server said '%s'",Msg.c_str());
       return _error->Error("Unable to fetch file, server said '%s'",Msg.c_str());
+   }
    
    // Finish off the data connection
    if (Finalize() == false)
    
    // Finish off the data connection
    if (Finalize() == false)
@@ -694,8 +680,11 @@ bool FTPConn::Get(const char *Path,FileFd &To,unsigned long Resume)
    {
       // Wait for some data..
       if (WaitFd(DataFd,false,TimeOut) == false)
    {
       // Wait for some data..
       if (WaitFd(DataFd,false,TimeOut) == false)
-        return _error->Error("Data socket connect timed out");
-    
+      {
+        Close();
+        return _error->Error("Data socket timed out");
+      }
+      
       // Read the data..
       int Res = read(DataFd,Buffer,sizeof(Buffer));
       if (Res == 0)
       // Read the data..
       int Res = read(DataFd,Buffer,sizeof(Buffer));
       if (Res == 0)
@@ -706,9 +695,13 @@ bool FTPConn::Get(const char *Path,FileFd &To,unsigned long Resume)
            continue;
         break;
       }
            continue;
         break;
       }
-      
+   
+      MD5.Add(Buffer,Res);
       if (To.Write(Buffer,Res) == false)
       if (To.Write(Buffer,Res) == false)
+      {
+        Close();
         return false;
         return false;
+      }      
    }
 
    // All done
    }
 
    // All done
@@ -724,37 +717,186 @@ bool FTPConn::Get(const char *Path,FileFd &To,unsigned long Resume)
 }
                                                                        /*}}}*/
 
 }
                                                                        /*}}}*/
 
-int main()
+// FtpMethod::FtpMethod - Constructor                                  /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+FtpMethod::FtpMethod() : pkgAcqMethod("1.0",SendConfig)
 {
 {
-   FTPConn Con(URI("ftp://va.debian.org/debian/README"));
-   string Msg;
-   _config->Set("Acquire::FTP::Passive","false");
+   signal(SIGTERM,SigTerm);
+   signal(SIGINT,SigTerm);
    
    
-   while (1)
+   Server = 0;
+   FailFd = -1;
+}
+                                                                       /*}}}*/
+// FtpMethod::SigTerm - Handle a fatal signal                          /*{{{*/
+// ---------------------------------------------------------------------
+/* This closes and timestamps the open file. This is neccessary to get 
+   resume behavoir on user abort */
+void FtpMethod::SigTerm(int)
+{
+   if (FailFd == -1)
+      _exit(100);
+   close(FailFd);
+   
+   // Timestamp
+   struct utimbuf UBuf;
+   UBuf.actime = FailTime;
+   UBuf.modtime = FailTime;
+   utime(FailFile.c_str(),&UBuf);
+   
+   _exit(100);
+}
+                                                                       /*}}}*/
+// FtpMethod::Configuration - Handle a configuration message           /*{{{*/
+// ---------------------------------------------------------------------
+/* We stash the desired pipeline depth */
+bool FtpMethod::Configuration(string Message)
+{
+   if (pkgAcqMethod::Configuration(Message) == false)
+      return false;
+   
+   TimeOut = _config->FindI("Acquire::Ftp::Timeout",TimeOut);
+   return true;
+}
+                                                                       /*}}}*/
+// FtpMethod::Fetch - Fetch a file                                     /*{{{*/
+// ---------------------------------------------------------------------
+/* Fetch a single file, called by the base class..  */
+bool FtpMethod::Fetch(FetchItem *Itm)
+{
+   URI Get = Itm->Uri;
+   const char *File = Get.Path.c_str();
+   FetchResult Res;
+   Res.Filename = Itm->DestFile;
+   Res.IMSHit = false;
+   
+   // Connect to the server
+   if (Server == 0 || Server->Comp(Get) == false)
    {
    {
-      if (Con.Open() == false)
-        break;
-      cout << "Size: " << Con.Size("/debian/README") << endl;
-      
-      time_t Time;
-      Con.ModTime("/debian/README",Time);      
-      cout << "Time: " << TimeRFC1123(Time) << endl;
+      delete Server;
+      Server = new FTPConn(Get);
+   }
+  
+   // Could not connect is a transient error..
+   if (Server->Open(this) == false)
+   {
+      Server->Close();
+      Fail(true);
+      return true;
+   }
    
    
+   // Get the files information
+   Status("Query");
+   unsigned long Size;
+   if (Server->Size(File,Size) == false ||
+       Server->ModTime(File,FailTime) == false)
+   {
+      Fail(true);
+      return true;
+   }
+   Res.Size = Size;
+
+   // See if it is an IMS hit
+   if (Itm->LastModified == FailTime)
+   {
+      Res.Size = 0;
+      Res.IMSHit = true;
+      URIDone(Res);
+      return true;
+   }
+   
+   // See if the file exists
+   struct stat Buf;
+   if (stat(Itm->DestFile.c_str(),&Buf) == 0)
+   {
+      if (Size == (unsigned)Buf.st_size && FailTime == Buf.st_mtime)
       {
       {
-        
-      FileFd F("t",FileFd::WriteEmpty);
-      Con.Get("/debian/README",F);
+        Res.Size = Buf.st_size;
+        Res.LastModified = Buf.st_mtime;
+        Res.ResumePoint = Buf.st_size;
+        URIDone(Res);
+        return true;
       }
       
       }
       
+      // Resume?
+      if (FailTime == Buf.st_mtime && Size > (unsigned)Buf.st_size)
+        Res.ResumePoint = Buf.st_size;
+   }
+   
+   // Open the file
+   MD5Summation MD5;
+   {
+      FileFd Fd(Itm->DestFile,FileFd::WriteAny);
+      if (_error->PendingError() == true)
+        return false;
+      
+      URIStart(Res);
+      
+      FailFile = Itm->DestFile;
+      FailFile.c_str();   // Make sure we dont do a malloc in the signal handler
+      FailFd = Fd.Fd();
+      
+      bool Missing;
+      if (Server->Get(File,Fd,Res.ResumePoint,MD5,Missing) == false)
       {
       {
+        Fd.Close();
         
         
-      FileFd F("t3",FileFd::WriteEmpty);
-      Con.Get("/debian/README.pgp",F);
+        // 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;
+        Fail(true);
+        return true;
       }
       }
-      
-      break;
+
+      Res.Size = Fd.Size();
    }
    
    }
    
-   _error->DumpErrors();
-   return 0;
+   Res.LastModified = FailTime;
+   Res.MD5Sum = MD5.Result();
+   
+   // Timestamp
+   struct utimbuf UBuf;
+   UBuf.actime = FailTime;
+   UBuf.modtime = FailTime;
+   utime(Queue->DestFile.c_str(),&UBuf);
+   FailFd = -1;
+
+   URIDone(Res);
+   
+   return true;
+}
+                                                                       /*}}}*/
+
+int main(int argc,const char *argv[])
+{ 
+   /* See if we should be come the http client - we do this for http
+      proxy urls */
+   if (getenv("ftp_proxy") != 0)
+   {
+      URI Proxy = string(getenv("ftp_proxy"));
+      if (Proxy.Access == "http")
+      {
+        // Copy over the environment setting
+        char S[300];
+        snprintf(S,sizeof(S),"http_proxy=%s",getenv("ftp_proxy"));
+        putenv(S);
+        
+        // Run the http method
+        string Path = flNotFile(argv[0]) + "/http";
+        execl(Path.c_str(),Path.c_str(),0);
+        cerr << "Unable to invoke " << Path << endl;
+        exit(100);
+      }      
+   }
+   
+   FtpMethod Mth;
+   
+   return Mth.Run();
 }
 }