// $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;
};
#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;
// 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 /*{{{*/
{
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
char S[400];
vsnprintf(S,sizeof(S) - 4,Fmt,args);
strcat(S,"\r\n");
+ va_end(args);
if (Debug == true)
cerr << "-> '" << QuoteString(S,"") << "'" << endl;
// 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
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;
}
// 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;
// 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;
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;
return true;
// Parse it
- StrToTime(Msg,Time);
- return true;
+ return FTPMDTMStrToTime(Msg.c_str(), Time);
}
/*}}}*/
// FTPConn::CreateDataFd - Get a data connection /*{{{*/
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
}
// 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"));
// ---------------------------------------------------------------------
/* 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)
if (Resume != 0)
{
- if (Hash.AddFD(To.Fd(),Resume) == false)
+ if (Hash.AddFD(To,Resume) == false)
{
_error->Errno("read",_("Problem hashing file"));
return false;
{
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
// FtpMethod::FtpMethod - Constructor /*{{{*/
// ---------------------------------------------------------------------
/* */
-FtpMethod::FtpMethod() : pkgAcqMethod("1.0",SendConfig)
+FtpMethod::FtpMethod() : aptMethod("ftp","1.0",SendConfig)
{
signal(SIGTERM,SigTerm);
signal(SIGINT,SigTerm);
/*}}}*/
// 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);
}
/*}}}*/
/* 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;
}
/*}}}*/
// Get the files information
Status(_("Query"));
- unsigned long Size;
+ unsigned long long Size;
if (Server->Size(File,Size) == false ||
Server->ModTime(File,FailTime) == false)
{
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;
}
// 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)
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);
}
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)
exit(100);
}
}
-
- FtpMethod Mth;
-
- return Mth.Run();
+ return FtpMethod().Run();
}