]> git.saurik.com Git - apt.git/blobdiff - methods/ftp.cc
fix testcase expecting incorrect remove log from dpkg
[apt.git] / methods / ftp.cc
index 97248f900c8eaff56f46e721a6f4958e11576c20..dbc5f25d190da2e87194877b1a316b36451de2ae 100644 (file)
@@ -3,7 +3,7 @@
 // $Id: ftp.cc,v 1.31.2.1 2004/01/16 18:58:50 mdz Exp $
 /* ######################################################################
 
-   FTP Aquire Method - This is the FTP aquire method for APT.
+   FTP Acquire Method - This is the FTP acquire 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
    ##################################################################### */
                                                                        /*}}}*/
 // Include Files                                                       /*{{{*/
+#include <config.h>
+
 #include <apt-pkg/fileutl.h>
-#include <apt-pkg/acquire-method.h>
 #include <apt-pkg/error.h>
 #include <apt-pkg/hashes.h>
 #include <apt-pkg/netrc.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/strutl.h>
 
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
 #include <sys/stat.h>
 #include <sys/time.h>
-#include <utime.h>
 #include <unistd.h>
 #include <signal.h>
 #include <stdio.h>
 #include <errno.h>
 #include <stdarg.h>
 #include <iostream>
-#include <apti18n.h>
 
 // Internet stuff
 #include <netinet/in.h>
-#include <sys/socket.h>
 #include <arpa/inet.h>
 #include <netdb.h>
 
 #include "rfc2553emu.h"
 #include "connect.h"
 #include "ftp.h"
+
+#include <apti18n.h>
                                                                        /*}}}*/
 
 using namespace std;
@@ -54,9 +59,9 @@ struct AFMap
 };
 
 #ifndef AF_INET6
-struct AFMap AFMap[] = {{AF_INET,1},{}};
+struct AFMap AFMap[] = {{AF_INET,1},{0, 0}};
 #else
-struct AFMap AFMap[] = {{AF_INET,1},{AF_INET6,2},{}};
+struct AFMap AFMap[] = {{AF_INET,1},{AF_INET6,2},{0, 0}};
 #endif
 
 unsigned long TimeOut = 120;
@@ -68,11 +73,14 @@ time_t FtpMethod::FailTime = 0;
 // FTPConn::FTPConn - Constructor                                      /*{{{*/
 // ---------------------------------------------------------------------
 /* */
-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),
+                           ForceExtended(false), TryPassive(true),
+                           PeerAddrLen(0), ServerAddrLen(0)
 {
    Debug = _config->FindB("Debug::Acquire::Ftp",false);
    PasvAddr = 0;
+   Buffer[0] = '\0';
 }
                                                                        /*}}}*/
 // FTPConn::~FTPConn - Destructor                                      /*{{{*/
@@ -249,19 +257,21 @@ bool FTPConn::Login()
       {
         if (Opts->Value.empty() == true)
            continue;
-        
+
         // Substitute the variables into the command
-        char SitePort[20];
-        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);
         Tmp = SubstVar(Tmp,"$(SITE_USER)",User);
         Tmp = SubstVar(Tmp,"$(SITE_PASS)",Pass);
-        Tmp = SubstVar(Tmp,"$(SITE_PORT)",SitePort);
+        if (ServerName.Port != 0)
+        {
+           std::string SitePort;
+           strprintf(SitePort, "%u", ServerName.Port);
+           Tmp = SubstVar(Tmp,"$(SITE_PORT)", SitePort);
+        }
+        else
+           Tmp = SubstVar(Tmp,"$(SITE_PORT)", "21");
         Tmp = SubstVar(Tmp,"$(SITE)",ServerName.Host);
 
         // Send the command
@@ -431,6 +441,7 @@ bool FTPConn::WriteMsg(unsigned int &Ret,string &Text,const char *Fmt,...)
    char S[400];
    vsnprintf(S,sizeof(S) - 4,Fmt,args);
    strcat(S,"\r\n");
+   va_end(args);
  
    if (Debug == true)
       cerr << "-> '" << QuoteString(S,"") << "'" << endl;
@@ -486,12 +497,25 @@ bool FTPConn::GoPasv()
    
    // Unsupported function
    string::size_type Pos = Msg.find('(');
-   if (Tag >= 400 || Pos == string::npos)
+   if (Tag >= 400)
+      return true;
+
+   //wu-2.6.2(1) ftp server, returns
+   //227 Entering Passive Mode 193,219,28,140,150,111
+   //without parentheses, let's try to cope with it.
+   //wget(1) and ftp(1) can.
+   if (Pos == string::npos)
+      Pos = Msg.rfind(' ');
+   else
+      ++Pos;
+
+   // Still unsupported function
+   if (Pos == string::npos)
       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)
+   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
@@ -559,7 +583,7 @@ bool FTPConn::ExtGoPasv()
    string::const_iterator List[4];
    unsigned Count = 0;
    Pos++;
-   for (string::const_iterator I = Msg.begin() + Pos; I < Msg.end(); I++)
+   for (string::const_iterator I = Msg.begin() + Pos; I < Msg.end(); ++I)
    {
       if (*I != Msg[Pos])
         continue;
@@ -617,8 +641,7 @@ bool FTPConn::ExtGoPasv()
    }
    
    // Get a new passive address.
-   int Res;
-   if ((Res = getaddrinfo(IP.c_str(),PStr,&Hints,&PasvAddr)) != 0)
+   if (getaddrinfo(IP.c_str(),PStr,&Hints,&PasvAddr) != 0)
       return true;
    
    return true;
@@ -627,7 +650,7 @@ bool FTPConn::ExtGoPasv()
 // FTPConn::Size - Return the size of a file                           /*{{{*/
 // ---------------------------------------------------------------------
 /* Grab the file size from the server, 0 means no size or empty file */
-bool FTPConn::Size(const char *Path,unsigned long &Size)
+bool FTPConn::Size(const char *Path,unsigned long long &Size)
 {
    // Query the size
    unsigned int Tag;
@@ -637,7 +660,7 @@ bool FTPConn::Size(const char *Path,unsigned long &Size)
       return false;
    
    char *End;
-   Size = strtol(Msg.c_str(),&End,10);
+   Size = strtoull(Msg.c_str(),&End,10);
    if (Tag >= 400 || End == Msg.c_str())
       Size = 0;
    return true;
@@ -716,14 +739,13 @@ bool FTPConn::CreateDataFd()
    DataListenFd = -1;
 
    // Get the information for a listening socket.
-   struct addrinfo *BindAddr = 0;
+   struct addrinfo *BindAddr = NULL;
    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)
+   if (getaddrinfo(0,"0",&Hints,&BindAddr) != 0 || BindAddr == NULL)
       return _error->Error(_("getaddrinfo was unable to get a listening socket"));
    
    // Construct the socket
@@ -735,7 +757,7 @@ bool FTPConn::CreateDataFd()
    }
    
    // Bind and listen
-   if (bind(DataListenFd,BindAddr->ai_addr,BindAddr->ai_addrlen) < 0)
+   if (::bind(DataListenFd,BindAddr->ai_addr,BindAddr->ai_addrlen) < 0)
    {
       freeaddrinfo(BindAddr);
       return _error->Errno("bind",_("Could not bind a socket"));
@@ -839,8 +861,9 @@ 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,
-                 Hashes &Hash,bool &Missing)
+bool FTPConn::Get(const char *Path,FileFd &To,unsigned long long Resume,
+                 Hashes &Hash,bool &Missing, unsigned long long MaximumSize,
+                  pkgAcqMethod *Owner)
 {
    Missing = false;
    if (CreateDataFd() == false)
@@ -864,7 +887,7 @@ bool FTPConn::Get(const char *Path,FileFd &To,unsigned long Resume,
    
    if (Resume != 0)
    {
-      if (Hash.AddFD(To.Fd(),Resume) == false)
+      if (Hash.AddFD(To,Resume) == false)
       {
         _error->Errno("read",_("Problem hashing file"));
         return false;
@@ -913,7 +936,14 @@ bool FTPConn::Get(const char *Path,FileFd &To,unsigned long Resume,
       {
         Close();
         return false;
-      }      
+      }
+
+      if (MaximumSize > 0 && To.Tell() > MaximumSize)
+      {
+         Owner->SetFailReason("MaximumSizeExceeded");
+         return _error->Error("Writing more data than expected (%llu > %llu)",
+                              To.Tell(), MaximumSize);
+      }
    }
 
    // All done
@@ -932,7 +962,7 @@ bool FTPConn::Get(const char *Path,FileFd &To,unsigned long Resume,
 // FtpMethod::FtpMethod - Constructor                                  /*{{{*/
 // ---------------------------------------------------------------------
 /* */
-FtpMethod::FtpMethod() : pkgAcqMethod("1.0",SendConfig)
+FtpMethod::FtpMethod() : aptMethod("ftp","1.0",SendConfig)
 {
    signal(SIGTERM,SigTerm);
    signal(SIGINT,SigTerm);
@@ -943,20 +973,22 @@ FtpMethod::FtpMethod() : pkgAcqMethod("1.0",SendConfig)
                                                                        /*}}}*/
 // FtpMethod::SigTerm - Handle a fatal signal                          /*{{{*/
 // ---------------------------------------------------------------------
-/* This closes and timestamps the open file. This is neccessary to get 
+/* This closes and timestamps the open file. This is necessary 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);
-   
+   struct timeval times[2];
+   times[0].tv_sec = FailTime;
+   times[1].tv_sec = FailTime;
+   times[0].tv_usec = times[1].tv_usec = 0;
+   utimes(FailFile.c_str(), times);
+
+   close(FailFd);
+
    _exit(100);
 }
                                                                        /*}}}*/
@@ -965,10 +997,11 @@ void FtpMethod::SigTerm(int)
 /* We stash the desired pipeline depth */
 bool FtpMethod::Configuration(string Message)
 {
-   if (pkgAcqMethod::Configuration(Message) == false)
+   if (aptMethod::Configuration(Message) == false)
       return false;
-   
+
    TimeOut = _config->FindI("Acquire::Ftp::Timeout",TimeOut);
+
    return true;
 }
                                                                        /*}}}*/
@@ -1002,7 +1035,7 @@ bool FtpMethod::Fetch(FetchItem *Itm)
    
    // Get the files information
    Status(_("Query"));
-   unsigned long Size;
+   unsigned long long Size;
    if (Server->Size(File,Size) == false ||
        Server->ModTime(File,FailTime) == false)
    {
@@ -1024,7 +1057,7 @@ bool FtpMethod::Fetch(FetchItem *Itm)
    struct stat Buf;
    if (stat(Itm->DestFile.c_str(),&Buf) == 0)
    {
-      if (Size == (unsigned)Buf.st_size && FailTime == Buf.st_mtime)
+      if (Size == (unsigned long long)Buf.st_size && FailTime == Buf.st_mtime)
       {
         Res.Size = Buf.st_size;
         Res.LastModified = Buf.st_mtime;
@@ -1034,12 +1067,12 @@ bool FtpMethod::Fetch(FetchItem *Itm)
       }
       
       // Resume?
-      if (FailTime == Buf.st_mtime && Size > (unsigned)Buf.st_size)
+      if (FailTime == Buf.st_mtime && Size > (unsigned long long)Buf.st_size)
         Res.ResumePoint = Buf.st_size;
    }
    
    // Open the file
-   Hashes Hash;
+   Hashes Hash(Itm->ExpectedHashes);
    {
       FileFd Fd(Itm->DestFile,FileFd::WriteAny);
       if (_error->PendingError() == true)
@@ -1048,24 +1081,25 @@ bool FtpMethod::Fetch(FetchItem *Itm)
       URIStart(Res);
       
       FailFile = Itm->DestFile;
-      FailFile.c_str();   // Make sure we dont do a malloc in the signal handler
+      FailFile.c_str();   // Make sure we don't do a malloc in the signal handler
       FailFd = Fd.Fd();
       
       bool Missing;
-      if (Server->Get(File,Fd,Res.ResumePoint,Hash,Missing) == false)
+      if (Server->Get(File,Fd,Res.ResumePoint,Hash,Missing,Itm->MaximumSize,this) == false)
       {
         Fd.Close();
-        
+
         // Timestamp
-        struct utimbuf UBuf;
-        UBuf.actime = FailTime;
-        UBuf.modtime = FailTime;
-        utime(FailFile.c_str(),&UBuf);
-        
+        struct timeval times[2];
+        times[0].tv_sec = FailTime;
+        times[1].tv_sec = FailTime;
+        times[0].tv_usec = times[1].tv_usec = 0;
+        utimes(FailFile.c_str(), times);
+
         // If the file is missing we hard fail and delete the destfile
         // otherwise transient fail
         if (Missing == true) {
-           unlink(FailFile.c_str());
+           RemoveFile("ftp", FailFile);
            return false;
         }
         Fail(true);
@@ -1073,28 +1107,27 @@ bool FtpMethod::Fetch(FetchItem *Itm)
       }
 
       Res.Size = Fd.Size();
+
+      // Timestamp
+      struct timeval times[2];
+      times[0].tv_sec = FailTime;
+      times[1].tv_sec = FailTime;
+      times[0].tv_usec = times[1].tv_usec = 0;
+      utimes(Fd.Name().c_str(), times);
+      FailFd = -1;
    }
-   
+
    Res.LastModified = FailTime;
    Res.TakeHashes(Hash);
-   
-   // 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[])
-{ 
-   setlocale(LC_ALL, "");
-
+int main(int, const char *argv[])
+{
    /* See if we should be come the http client - we do this for http
       proxy urls */
    if (getenv("ftp_proxy") != 0)
@@ -1117,8 +1150,5 @@ int main(int argc,const char *argv[])
         exit(100);
       }      
    }
-   
-   FtpMethod Mth;
-   
-   return Mth.Run();
+   return FtpMethod().Run();
 }