X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/b2e465d6d32d2dc884f58b94acb7e35f671a87fe..efcbb0f0c0ec92d6b61c6349a5ad69952939272e:/methods/ftp.cc diff --git a/methods/ftp.cc b/methods/ftp.cc index 0d617dd8f..66787a7be 100644 --- a/methods/ftp.cc +++ b/methods/ftp.cc @@ -1,9 +1,9 @@ // -*- mode: cpp; mode: fold -*- // Description /*{{{*/ -// $Id: ftp.cc,v 1.21 2001/02/20 07:03:18 jgg Exp $ +// $Id: ftp.cc,v 1.31.2.1 2004/01/16 18:58:50 mdz Exp $ /* ###################################################################### - HTTP 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 @@ -15,19 +15,27 @@ ##################################################################### */ /*}}}*/ // Include Files /*{{{*/ +#include + #include #include #include -#include +#include +#include +#include +#include +#include +#include +#include #include #include -#include #include #include #include #include #include +#include // Internet stuff #include @@ -38,8 +46,12 @@ #include "rfc2553emu.h" #include "connect.h" #include "ftp.h" + +#include /*}}}*/ +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 @@ -49,9 +61,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; @@ -64,10 +76,12 @@ time_t FtpMethod::FailTime = 0; // --------------------------------------------------------------------- /* */ FTPConn::FTPConn(URI Srv) : Len(0), ServerFd(-1), DataFd(-1), - DataListenFd(-1), ServerName(Srv) + DataListenFd(-1), ServerName(Srv), + ForceExtended(false), TryPassive(true) { Debug = _config->FindB("Debug::Acquire::Ftp",false); PasvAddr = 0; + Buffer[0] = '\0'; } /*}}}*/ // FTPConn::~FTPConn - Destructor /*{{{*/ @@ -108,22 +122,34 @@ bool FTPConn::Open(pkgAcqMethod *Owner) Close(); // Determine the proxy setting - if (getenv("ftp_proxy") == 0) + string SpecificProxy = _config->Find("Acquire::ftp::Proxy::" + ServerName.Host); + if (!SpecificProxy.empty()) { - string DefProxy = _config->Find("Acquire::ftp::Proxy"); - string SpecificProxy = _config->Find("Acquire::ftp::Proxy::" + ServerName.Host); - if (SpecificProxy.empty() == false) - { - if (SpecificProxy == "DIRECT") - Proxy = ""; - else - Proxy = SpecificProxy; - } - else - Proxy = DefProxy; + if (SpecificProxy == "DIRECT") + Proxy = ""; + else + Proxy = SpecificProxy; } else - Proxy = getenv("ftp_proxy"); + { + string DefProxy = _config->Find("Acquire::ftp::Proxy"); + if (!DefProxy.empty()) + { + Proxy = DefProxy; + } + else + { + char* result = getenv("ftp_proxy"); + Proxy = result ? result : ""; + } + } + + // 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; @@ -146,19 +172,22 @@ bool FTPConn::Open(pkgAcqMethod *Owner) 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")); + 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"); + 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 _error->Errno("getsockname",_("Unable to determine the local name")); - Owner->Status("Logging in"); - return Login(); + return Res; } /*}}}*/ // FTPConn::Login - Login to the remote server /*{{{*/ @@ -172,7 +201,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) @@ -187,19 +216,21 @@ bool FTPConn::Login() if (ReadResp(Tag,Msg) == false) return false; if (Tag >= 400) - return _error->Error("Server refused our connection and said: %s",Msg.c_str()); + return _error->Error(_("The server refused the connection and said: %s"),Msg.c_str()); // Send the user if (WriteMsg(Tag,Msg,"USER %s",User.c_str()) == false) return false; if (Tag >= 400) - return _error->Error("USER failed, server said: %s",Msg.c_str()); + return _error->Error(_("USER failed, server said: %s"),Msg.c_str()); - // Send the Password - if (WriteMsg(Tag,Msg,"PASS %s",Pass.c_str()) == false) - return false; - if (Tag >= 400) - return _error->Error("PASS failed, server said: %s",Msg.c_str()); + if (Tag == 331) { // 331 User name okay, need password. + // Send the Password + if (WriteMsg(Tag,Msg,"PASS %s",Pass.c_str()) == false) + return false; + if (Tag >= 400) + return _error->Error(_("PASS failed, server said: %s"),Msg.c_str()); + } // Enter passive mode if (_config->Exists("Acquire::FTP::Passive::" + ServerName.Host) == true) @@ -213,13 +244,13 @@ bool FTPConn::Login() if (ReadResp(Tag,Msg) == false) return false; if (Tag >= 400) - return _error->Error("Server refused our connection and said: %s",Msg.c_str()); + return _error->Error(_("The server refused the connection and said: %s"),Msg.c_str()); // Perform proxy script execution Configuration::Item const *Opts = _config->Tree("Acquire::ftp::ProxyLogin"); if (Opts == 0 || Opts->Child == 0) - return _error->Error("A proxy server was specified but no login " - "script, Acquire::ftp::ProxyLogin is empty."); + return _error->Error(_("A proxy server was specified but no login " + "script, Acquire::ftp::ProxyLogin is empty.")); Opts = Opts->Child; // Iterate over the entire login script @@ -246,7 +277,7 @@ bool FTPConn::Login() if (WriteMsg(Tag,Msg,"%s",Tmp.c_str()) == false) return false; if (Tag >= 400) - return _error->Error("Login script command '%s' failed, server said: %s",Tmp.c_str(),Msg.c_str()); + return _error->Error(_("Login script command '%s' failed, server said: %s"),Tmp.c_str(),Msg.c_str()); } // Enter passive mode @@ -272,7 +303,7 @@ bool FTPConn::Login() if (WriteMsg(Tag,Msg,"TYPE I") == false) return false; if (Tag >= 400) - return _error->Error("TYPE failed, server said: %s",Msg.c_str()); + return _error->Error(_("TYPE failed, server said: %s"),Msg.c_str()); return true; } @@ -310,23 +341,23 @@ bool FTPConn::ReadLine(string &Text) if (WaitFd(ServerFd,false,TimeOut) == false) { Close(); - return _error->Error("Connection timeout"); + return _error->Error(_("Connection timeout")); } // Suck it back int Res = read(ServerFd,Buffer + Len,sizeof(Buffer) - Len); if (Res == 0) - _error->Error("Server closed the connection"); + _error->Error(_("Server closed the connection")); if (Res <= 0) { - _error->Errno("read","Read error"); + _error->Errno("read",_("Read error")); Close(); return false; } Len += Res; } - return _error->Error("A response overflowed the buffer."); + return _error->Error(_("A response overflowed the buffer.")); } /*}}}*/ // FTPConn::ReadResp - Read a full response from the server /*{{{*/ @@ -343,7 +374,7 @@ bool FTPConn::ReadResp(unsigned int &Ret,string &Text) char *End; Ret = strtol(Msg.c_str(),&End,10); if (End - Msg.c_str() != 3) - return _error->Error("Protocol corruption"); + return _error->Error(_("Protocol corruption")); // All done ? Text = Msg.c_str()+4; @@ -355,7 +386,7 @@ bool FTPConn::ReadResp(unsigned int &Ret,string &Text) } if (*End != '-') - return _error->Error("Protocol corruption"); + return _error->Error(_("Protocol corruption")); /* Okay, here we do the continued message trick. This is foolish, but proftpd follows the protocol as specified and wu-ftpd doesn't, so @@ -409,6 +440,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; @@ -421,13 +453,13 @@ bool FTPConn::WriteMsg(unsigned int &Ret,string &Text,const char *Fmt,...) 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) { - _error->Errno("write","Write Error"); + _error->Errno("write",_("Write error")); Close(); return false; } @@ -537,7 +569,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; @@ -595,8 +627,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; @@ -605,7 +636,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; @@ -615,7 +646,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; @@ -639,8 +670,7 @@ bool FTPConn::ModTime(const char *Path, time_t &Time) return true; // Parse it - StrToTime(Msg,Time); - return true; + return FTPMDTMStrToTime(Msg.c_str(), Time); } /*}}}*/ // FTPConn::CreateDataFd - Get a data connection /*{{{*/ @@ -668,24 +698,24 @@ bool FTPConn::CreateDataFd() // Get a socket if ((DataFd = socket(PasvAddr->ai_family,PasvAddr->ai_socktype, PasvAddr->ai_protocol)) < 0) - return _error->Errno("socket","Could not create a socket"); + return _error->Errno("socket",_("Could not create a socket")); // Connect to the server SetNonBlock(DataFd,true); if (connect(DataFd,PasvAddr->ai_addr,PasvAddr->ai_addrlen) < 0 && errno != EINPROGRESS) - return _error->Errno("socket","Could not create a socket"); + return _error->Errno("socket",_("Could not create a socket")); /* This implements a timeout for connect by opening the connection nonblocking */ if (WaitFd(DataFd,true,TimeOut) == false) - return _error->Error("Could not connect data socket, connection timed out"); + return _error->Error(_("Could not connect data socket, connection timed out")); unsigned int Err; unsigned int Len = sizeof(Err); if (getsockopt(DataFd,SOL_SOCKET,SO_ERROR,&Err,&Len) != 0) - return _error->Errno("getsockopt","Failed"); + return _error->Errno("getsockopt",_("Failed")); if (Err != 0) - return _error->Error("Could not connect passive socket."); + return _error->Error(_("Could not connect passive socket.")); return true; } @@ -695,40 +725,40 @@ 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) - return _error->Error("getaddrinfo was unable to get a listening socket"); + if (getaddrinfo(0,"0",&Hints,&BindAddr) != 0 || BindAddr == NULL) + 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"); + return _error->Errno("socket",_("Could not create a socket")); } // Bind and listen if (bind(DataListenFd,BindAddr->ai_addr,BindAddr->ai_addrlen) < 0) { freeaddrinfo(BindAddr); - return _error->Errno("bind","Could not bind a socket"); + 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"); + return _error->Errno("listen",_("Could not listen on the socket")); SetNonBlock(DataListenFd,true); // Determine the name to send to the remote 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"); + return _error->Errno("getsockname",_("Could not determine the socket's name")); + // Reverse the address. We need the server address and the data port. char Name[NI_MAXHOST]; @@ -759,7 +789,7 @@ bool FTPConn::CreateDataFd() (int)(Port >> 8) & 0xff, (int)(Port & 0xff)) == false) return false; if (Tag >= 400) - return _error->Error("Unable to send PORT command"); + return _error->Error(_("Unable to send PORT command")); return true; } @@ -769,7 +799,7 @@ bool FTPConn::CreateDataFd() if (AFMap[J].Family == ((struct sockaddr *)&Addr)->sa_family) Proto = AFMap[J].IETFFamily; if (Proto == 0) - return _error->Error("Unkonwn address family %u (AF_*)", + return _error->Error(_("Unknown address family %u (AF_*)"), ((struct sockaddr *)&Addr)->sa_family); // Send the EPRT command @@ -778,7 +808,7 @@ bool FTPConn::CreateDataFd() if (WriteMsg(Tag,Msg,"EPRT |%u|%s|%s|",Proto,Name,Service) == false) return false; if (Tag >= 400) - return _error->Error("EPRT failed, server said: %s",Msg.c_str()); + return _error->Error(_("EPRT failed, server said: %s"),Msg.c_str()); return true; } /*}}}*/ @@ -798,14 +828,14 @@ bool FTPConn::Finalize() // Wait for someone to connect.. if (WaitFd(DataListenFd,false,TimeOut) == false) - return _error->Error("Data socket connect timed out"); + return _error->Error(_("Data socket connect timed out")); // Accept the connection struct sockaddr_in Addr; socklen_t Len = sizeof(Addr); DataFd = accept(DataListenFd,(struct sockaddr *)&Addr,&Len); if (DataFd < 0) - return _error->Errno("accept","Unable to accept connection"); + return _error->Errno("accept",_("Unable to accept connection")); close(DataListenFd); DataListenFd = -1; @@ -817,8 +847,8 @@ 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) +bool FTPConn::Get(const char *Path,FileFd &To,unsigned long long Resume, + Hashes &Hash,bool &Missing) { Missing = false; if (CreateDataFd() == false) @@ -842,9 +872,9 @@ 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,Resume) == false) { - _error->Errno("read","Problem hashing file"); + _error->Errno("read",_("Problem hashing file")); return false; } } @@ -857,7 +887,7 @@ bool FTPConn::Get(const char *Path,FileFd &To,unsigned long Resume, { 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 @@ -872,7 +902,7 @@ bool FTPConn::Get(const char *Path,FileFd &To,unsigned long Resume, if (WaitFd(DataFd,false,TimeOut) == false) { Close(); - return _error->Error("Data socket timed out"); + return _error->Error(_("Data socket timed out")); } // Read the data.. @@ -886,7 +916,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(); @@ -902,7 +932,7 @@ bool FTPConn::Get(const char *Path,FileFd &To,unsigned long Resume, if (ReadResp(Tag,Msg) == false) return false; if (Tag >= 400) - return _error->Error("Data transfer failed, server said '%s'",Msg.c_str()); + return _error->Error(_("Data transfer failed, server said '%s'"),Msg.c_str()); return true; } /*}}}*/ @@ -921,20 +951,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); } /*}}}*/ @@ -960,7 +992,9 @@ bool FtpMethod::Fetch(FetchItem *Itm) FetchResult Res; Res.Filename = Itm->DestFile; Res.IMSHit = false; - + + maybe_add_auth (Get, _config->FindFile("Dir::Etc::netrc")); + // Connect to the server if (Server == 0 || Server->Comp(Get) == false) { @@ -977,8 +1011,8 @@ bool FtpMethod::Fetch(FetchItem *Itm) } // Get the files information - Status("Query"); - unsigned long Size; + Status(_("Query")); + unsigned long long Size; if (Server->Size(File,Size) == false || Server->ModTime(File,FailTime) == false) { @@ -1000,7 +1034,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; @@ -1010,12 +1044,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 - MD5Summation MD5; + Hashes Hash; { FileFd Fd(Itm->DestFile,FileFd::WriteAny); if (_error->PendingError() == true) @@ -1028,60 +1062,70 @@ 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) + 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()); return false; + } Fail(true); return true; } 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.MD5Sum = MD5.Result(); - - // Timestamp - struct utimbuf UBuf; - UBuf.actime = FailTime; - UBuf.modtime = FailTime; - utime(Queue->DestFile.c_str(),&UBuf); - FailFd = -1; + Res.TakeHashes(Hash); URIDone(Res); - + return true; } /*}}}*/ -int main(int argc,const char *argv[]) +int main(int, const char *argv[]) { + setlocale(LC_ALL, ""); + /* 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")); + + // 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((char *)"no_proxy="); // Run the http method - string Path = flNotFile(argv[0]) + "/http"; - execl(Path.c_str(),Path.c_str(),0); - cerr << "Unable to invoke " << Path << endl; + string Path = flNotFile(argv[0]) + "http"; + execl(Path.c_str(),Path.c_str(),(char *)NULL); + cerr << _("Unable to invoke ") << Path << endl; exit(100); } }