]>
git.saurik.com Git - apt.git/blob - methods/ftp.cc
   1 // -*- mode: cpp; mode: fold -*- 
   3 // $Id: ftp.cc,v 1.31.2.1 2004/01/16 18:58:50 mdz Exp $ 
   4 /* ###################################################################### 
   6    FTP Aquire Method - This is the FTP aquire method for APT. 
   8    This is a very simple implementation that does not try to optimize 
   9    at all. Commands are sent syncronously with the FTP server (as the 
  10    rfc recommends, but it is not really necessary..) and no tricks are 
  11    done to speed things along. 
  13    RFC 2428 describes the IPv6 FTP behavior 
  15    ##################################################################### */ 
  17 // Include Files                                                        /*{{{*/ 
  20 #include <apt-pkg/fileutl.h> 
  21 #include <apt-pkg/acquire-method.h> 
  22 #include <apt-pkg/error.h> 
  23 #include <apt-pkg/hashes.h> 
  24 #include <apt-pkg/netrc.h> 
  25 #include <apt-pkg/configuration.h> 
  37 #include <netinet/in.h> 
  38 #include <sys/socket.h> 
  39 #include <arpa/inet.h> 
  42 #include "rfc2553emu.h" 
  50 /* This table is for the EPRT and EPSV commands, it maps the OS address 
  51    family to the IETF address families */ 
  55    unsigned long IETFFamily
; 
  59 struct AFMap AFMap
[] = {{AF_INET
,1},{}}; 
  61 struct AFMap AFMap
[] = {{AF_INET
,1},{AF_INET6
,2},{}}; 
  64 unsigned long TimeOut 
= 120; 
  66 string 
FtpMethod::FailFile
; 
  67 int FtpMethod::FailFd 
= -1; 
  68 time_t FtpMethod::FailTime 
= 0; 
  70 // FTPConn::FTPConn - Constructor                                       /*{{{*/ 
  71 // --------------------------------------------------------------------- 
  73 FTPConn::FTPConn(URI Srv
) : Len(0), ServerFd(-1), DataFd(-1),  
  74                             DataListenFd(-1), ServerName(Srv
), 
  75                             ForceExtended(false), TryPassive(true) 
  77    Debug 
= _config
->FindB("Debug::Acquire::Ftp",false); 
  82 // FTPConn::~FTPConn - Destructor                                       /*{{{*/ 
  83 // --------------------------------------------------------------------- 
  90 // FTPConn::Close - Close down the connection                           /*{{{*/ 
  91 // --------------------------------------------------------------------- 
  92 /* Just tear down the socket and data socket */ 
 103       freeaddrinfo(PasvAddr
); 
 107 // FTPConn::Open - Open a new connection                                /*{{{*/ 
 108 // --------------------------------------------------------------------- 
 109 /* Connect to the server using a non-blocking connection and perform a  
 111 bool FTPConn::Open(pkgAcqMethod 
*Owner
) 
 113    // Use the already open connection if possible. 
 119    // Determine the proxy setting 
 120    string SpecificProxy 
= _config
->Find("Acquire::ftp::Proxy::" + ServerName
.Host
); 
 121    if (!SpecificProxy
.empty()) 
 123            if (SpecificProxy 
== "DIRECT") 
 126                    Proxy 
= SpecificProxy
; 
 130            string DefProxy 
= _config
->Find("Acquire::ftp::Proxy"); 
 131            if (!DefProxy
.empty()) 
 137                    char* result 
= getenv("ftp_proxy"); 
 138                    Proxy 
= result 
? result 
: ""; 
 142    // Parse no_proxy, a , separated list of domains 
 143    if (getenv("no_proxy") != 0) 
 145       if (CheckDomainList(ServerName
.Host
,getenv("no_proxy")) == true) 
 149    // Determine what host and port to use based on the proxy settings 
 152    if (Proxy
.empty() == true) 
 154       if (ServerName
.Port 
!= 0) 
 155          Port 
= ServerName
.Port
; 
 156       Host 
= ServerName
.Host
; 
 165    /* Connect to the remote server. Since FTP is connection oriented we 
 166       want to make sure we get a new server every time we reconnect */ 
 168    if (Connect(Host
,Port
,"ftp",21,ServerFd
,TimeOut
,Owner
) == false) 
 171    // Login must be before getpeername otherwise dante won't work. 
 172    Owner
->Status(_("Logging in")); 
 175    // Get the remote server's address 
 176    PeerAddrLen 
= sizeof(PeerAddr
); 
 177    if (getpeername(ServerFd
,(sockaddr 
*)&PeerAddr
,&PeerAddrLen
) != 0) 
 178       return _error
->Errno("getpeername",_("Unable to determine the peer name")); 
 180    // Get the local machine's address 
 181    ServerAddrLen 
= sizeof(ServerAddr
); 
 182    if (getsockname(ServerFd
,(sockaddr 
*)&ServerAddr
,&ServerAddrLen
) != 0) 
 183       return _error
->Errno("getsockname",_("Unable to determine the local name")); 
 188 // FTPConn::Login - Login to the remote server                          /*{{{*/ 
 189 // --------------------------------------------------------------------- 
 190 /* This performs both normal login and proxy login using a simples script 
 191    stored in the config file. */ 
 192 bool FTPConn::Login() 
 197    // Setup the variables needed for authentication 
 198    string User 
= "anonymous"; 
 199    string Pass 
= "apt_get_ftp_2.1@debian.linux.user"; 
 201    // Fill in the user/pass 
 202    if (ServerName
.User
.empty() == false) 
 203       User 
= ServerName
.User
; 
 204    if (ServerName
.Password
.empty() == false) 
 205       Pass 
= ServerName
.Password
; 
 207    // Perform simple login 
 208    if (Proxy
.empty() == true) 
 210       // Read the initial response 
 211       if (ReadResp(Tag
,Msg
) == false) 
 214          return _error
->Error(_("The server refused the connection and said: %s"),Msg
.c_str()); 
 217       if (WriteMsg(Tag
,Msg
,"USER %s",User
.c_str()) == false) 
 220          return _error
->Error(_("USER failed, server said: %s"),Msg
.c_str()); 
 222       if (Tag 
== 331) { // 331 User name okay, need password. 
 224          if (WriteMsg(Tag
,Msg
,"PASS %s",Pass
.c_str()) == false) 
 227             return _error
->Error(_("PASS failed, server said: %s"),Msg
.c_str()); 
 230       // Enter passive mode 
 231       if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true) 
 232          TryPassive 
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true); 
 234          TryPassive 
= _config
->FindB("Acquire::FTP::Passive",true);       
 238       // Read the initial response 
 239       if (ReadResp(Tag
,Msg
) == false) 
 242          return _error
->Error(_("The server refused the connection and said: %s"),Msg
.c_str()); 
 244       // Perform proxy script execution 
 245       Configuration::Item 
const *Opts 
= _config
->Tree("Acquire::ftp::ProxyLogin"); 
 246       if (Opts 
== 0 || Opts
->Child 
== 0) 
 247          return _error
->Error(_("A proxy server was specified but no login " 
 248                               "script, Acquire::ftp::ProxyLogin is empty.")); 
 251       // Iterate over the entire login script 
 252       for (; Opts 
!= 0; Opts 
= Opts
->Next
) 
 254          if (Opts
->Value
.empty() == true) 
 257          // Substitute the variables into the command 
 259          if (ServerName
.Port 
!= 0) 
 260             sprintf(SitePort
,"%u",ServerName
.Port
); 
 262             strcpy(SitePort
,"21"); 
 263          string Tmp 
= Opts
->Value
; 
 264          Tmp 
= SubstVar(Tmp
,"$(PROXY_USER)",Proxy
.User
); 
 265          Tmp 
= SubstVar(Tmp
,"$(PROXY_PASS)",Proxy
.Password
); 
 266          Tmp 
= SubstVar(Tmp
,"$(SITE_USER)",User
); 
 267          Tmp 
= SubstVar(Tmp
,"$(SITE_PASS)",Pass
); 
 268          Tmp 
= SubstVar(Tmp
,"$(SITE_PORT)",SitePort
); 
 269          Tmp 
= SubstVar(Tmp
,"$(SITE)",ServerName
.Host
); 
 272          if (WriteMsg(Tag
,Msg
,"%s",Tmp
.c_str()) == false) 
 275             return _error
->Error(_("Login script command '%s' failed, server said: %s"),Tmp
.c_str(),Msg
.c_str());         
 278       // Enter passive mode 
 280       if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true) 
 281          TryPassive 
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true); 
 284          if (_config
->Exists("Acquire::FTP::Proxy::Passive") == true) 
 285             TryPassive 
= _config
->FindB("Acquire::FTP::Proxy::Passive",true); 
 287             TryPassive 
= _config
->FindB("Acquire::FTP::Passive",true); 
 291    // Force the use of extended commands 
 292    if (_config
->Exists("Acquire::FTP::ForceExtended::" + ServerName
.Host
) == true) 
 293       ForceExtended 
= _config
->FindB("Acquire::FTP::ForceExtended::" + ServerName
.Host
,true); 
 295       ForceExtended 
= _config
->FindB("Acquire::FTP::ForceExtended",false); 
 298    if (WriteMsg(Tag
,Msg
,"TYPE I") == false) 
 301       return _error
->Error(_("TYPE failed, server said: %s"),Msg
.c_str()); 
 306 // FTPConn::ReadLine - Read a line from the server                      /*{{{*/ 
 307 // --------------------------------------------------------------------- 
 308 /* This performs a very simple buffered read. */ 
 309 bool FTPConn::ReadLine(string 
&Text
) 
 315    while (Len 
< sizeof(Buffer
)) 
 317       // Scan the buffer for a new line 
 318       for (unsigned int I 
= 0; I 
!= Len
; I
++) 
 320          // Escape some special chars 
 325          if (Buffer
[I
] != '\n') 
 329          Text 
= string(Buffer
,I
); 
 330          memmove(Buffer
,Buffer
+I
,Len 
- I
); 
 335       // Wait for some data.. 
 336       if (WaitFd(ServerFd
,false,TimeOut
) == false) 
 339          return _error
->Error(_("Connection timeout")); 
 343       int Res 
= read(ServerFd
,Buffer 
+ Len
,sizeof(Buffer
) - Len
); 
 345          _error
->Error(_("Server closed the connection")); 
 348          _error
->Errno("read",_("Read error")); 
 355    return _error
->Error(_("A response overflowed the buffer.")); 
 358 // FTPConn::ReadResp - Read a full response from the server             /*{{{*/ 
 359 // --------------------------------------------------------------------- 
 360 /* This reads a reply code from the server, it handles both p */ 
 361 bool FTPConn::ReadResp(unsigned int &Ret
,string 
&Text
) 
 363    // Grab the first line of the response 
 365    if (ReadLine(Msg
) == false) 
 370    Ret 
= strtol(Msg
.c_str(),&End
,10); 
 371    if (End 
- Msg
.c_str() != 3) 
 372       return _error
->Error(_("Protocol corruption")); 
 375    Text 
= Msg
.c_str()+4; 
 379          cerr 
<< "<- '" << QuoteString(Text
,"") << "'" << endl
; 
 384       return _error
->Error(_("Protocol corruption")); 
 386    /* Okay, here we do the continued message trick. This is foolish, but 
 387       proftpd follows the protocol as specified and wu-ftpd doesn't, so  
 388       we filter. I wonder how many clients break if you use proftpd and 
 389       put a '- in the 3rd spot in the message? */ 
 391    strncpy(Leader
,Msg
.c_str(),3); 
 393    while (ReadLine(Msg
) == true) 
 395       // Short, it must be using RFC continuation.. 
 396       if (Msg
.length() < 4) 
 403       if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == ' ') 
 405          Text 
+= Msg
.c_str()+4; 
 409       // This message has the wu-ftpd style reply code prefixed 
 410       if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == '-') 
 412          Text 
+= Msg
.c_str()+4; 
 416       // Must be RFC style prefixing 
 420    if (Debug 
== true && _error
->PendingError() == false) 
 421       cerr 
<< "<- '" << QuoteString(Text
,"") << "'" << endl
; 
 423    return !_error
->PendingError(); 
 426 // FTPConn::WriteMsg - Send a message to the server                     /*{{{*/ 
 427 // --------------------------------------------------------------------- 
 428 /* Simple printf like function.. */ 
 429 bool FTPConn::WriteMsg(unsigned int &Ret
,string 
&Text
,const char *Fmt
,...) 
 434    // sprintf the description 
 436    vsnprintf(S
,sizeof(S
) - 4,Fmt
,args
); 
 441       cerr 
<< "-> '" << QuoteString(S
,"") << "'" << endl
; 
 444    unsigned long Len 
= strlen(S
); 
 445    unsigned long Start 
= 0; 
 448       if (WaitFd(ServerFd
,true,TimeOut
) == false) 
 451          return _error
->Error(_("Connection timeout")); 
 454       int Res 
= write(ServerFd
,S 
+ Start
,Len
); 
 457          _error
->Errno("write",_("Write error")); 
 466    return ReadResp(Ret
,Text
); 
 469 // FTPConn::GoPasv - Enter Passive mode                                 /*{{{*/ 
 470 // --------------------------------------------------------------------- 
 471 /* Try to enter passive mode, the return code does not indicate if passive 
 472    mode could or could not be established, only if there was a fatal error.  
 473    We have to enter passive mode every time we make a data connection :| */ 
 474 bool FTPConn::GoPasv() 
 476    /* The PASV command only works on IPv4 sockets, even though it could 
 477       in theory suppory IPv6 via an all zeros reply */ 
 478    if (((struct sockaddr 
*)&PeerAddr
)->sa_family 
!= AF_INET 
||  
 479        ForceExtended 
== true) 
 483       freeaddrinfo(PasvAddr
); 
 486    // Try to enable pasv mode 
 489    if (WriteMsg(Tag
,Msg
,"PASV") == false) 
 492    // Unsupported function 
 493    string::size_type Pos 
= Msg
.find('('); 
 494    if (Tag 
>= 400 || Pos 
== string::npos
) 
 498    unsigned a0
,a1
,a2
,a3
,p0
,p1
; 
 499    if (sscanf(Msg
.c_str() + Pos
,"(%u,%u,%u,%u,%u,%u)",&a0
,&a1
,&a2
,&a3
,&p0
,&p1
) != 6) 
 502    /* Some evil servers return 0 to mean their addr. We can actually speak 
 503       to these servers natively using IPv6 */ 
 504    if (a0 
== 0 && a1 
== 0 && a2 
== 0 && a3 
== 0) 
 506       // Get the IP in text form 
 507       char Name
[NI_MAXHOST
]; 
 508       char Service
[NI_MAXSERV
]; 
 509       getnameinfo((struct sockaddr 
*)&PeerAddr
,PeerAddrLen
, 
 510                   Name
,sizeof(Name
),Service
,sizeof(Service
), 
 511                   NI_NUMERICHOST
|NI_NUMERICSERV
); 
 513       struct addrinfo Hints
; 
 514       memset(&Hints
,0,sizeof(Hints
)); 
 515       Hints
.ai_socktype 
= SOCK_STREAM
; 
 516       Hints
.ai_family 
= ((struct sockaddr 
*)&PeerAddr
)->sa_family
; 
 517       Hints
.ai_flags 
|= AI_NUMERICHOST
; 
 519       // Get a new passive address. 
 521       snprintf(Port
,sizeof(Port
),"%u",(p0 
<< 8) + p1
); 
 522       if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0) 
 527    struct addrinfo Hints
; 
 528    memset(&Hints
,0,sizeof(Hints
)); 
 529    Hints
.ai_socktype 
= SOCK_STREAM
; 
 530    Hints
.ai_family 
= AF_INET
; 
 531    Hints
.ai_flags 
|= AI_NUMERICHOST
; 
 533    // Get a new passive address. 
 535    snprintf(Port
,sizeof(Port
),"%u",(p0 
<< 8) + p1
); 
 537    snprintf(Name
,sizeof(Name
),"%u.%u.%u.%u",a0
,a1
,a2
,a3
); 
 538    if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0) 
 543 // FTPConn::ExtGoPasv - Enter Extended Passive mode                     /*{{{*/ 
 544 // --------------------------------------------------------------------- 
 545 /* Try to enter extended passive mode. See GoPasv above and RFC 2428 */ 
 546 bool FTPConn::ExtGoPasv() 
 549       freeaddrinfo(PasvAddr
); 
 552    // Try to enable pasv mode 
 555    if (WriteMsg(Tag
,Msg
,"EPSV") == false) 
 558    // Unsupported function 
 559    string::size_type Pos 
= Msg
.find('('); 
 560    if (Tag 
>= 400 || Pos 
== string::npos
) 
 564    string::const_iterator List
[4]; 
 567    for (string::const_iterator I 
= Msg
.begin() + Pos
; I 
< Msg
.end(); ++I
) 
 579    unsigned long Proto 
= 0; 
 580    unsigned long Port 
= 0; 
 582    IP 
= string(List
[1]+1,List
[2]); 
 583    Port 
= atoi(string(List
[2]+1,List
[3]).c_str()); 
 584    if (IP
.empty() == false) 
 585       Proto 
= atoi(string(List
[0]+1,List
[1]).c_str()); 
 590    // String version of the port 
 592    snprintf(PStr
,sizeof(PStr
),"%lu",Port
); 
 594    // Get the IP in text form 
 595    struct addrinfo Hints
; 
 596    memset(&Hints
,0,sizeof(Hints
)); 
 597    Hints
.ai_socktype 
= SOCK_STREAM
; 
 598    Hints
.ai_flags 
|= AI_NUMERICHOST
; 
 600    /* The RFC defined case, connect to the old IP/protocol using the 
 602    if (IP
.empty() == true) 
 604       // Get the IP in text form 
 605       char Name
[NI_MAXHOST
]; 
 606       char Service
[NI_MAXSERV
]; 
 607       getnameinfo((struct sockaddr 
*)&PeerAddr
,PeerAddrLen
, 
 608                   Name
,sizeof(Name
),Service
,sizeof(Service
), 
 609                   NI_NUMERICHOST
|NI_NUMERICSERV
); 
 611       Hints
.ai_family 
= ((struct sockaddr 
*)&PeerAddr
)->sa_family
; 
 617       for (unsigned J 
= 0; AFMap
[J
].Family 
!= 0; J
++) 
 618          if (AFMap
[J
].IETFFamily 
== Proto
) 
 619             Hints
.ai_family 
= AFMap
[J
].Family
; 
 620       if (Hints
.ai_family 
== 0) 
 624    // Get a new passive address. 
 625    if (getaddrinfo(IP
.c_str(),PStr
,&Hints
,&PasvAddr
) != 0) 
 631 // FTPConn::Size - Return the size of a file                            /*{{{*/ 
 632 // --------------------------------------------------------------------- 
 633 /* Grab the file size from the server, 0 means no size or empty file */ 
 634 bool FTPConn::Size(const char *Path
,unsigned long long &Size
) 
 640    if (WriteMsg(Tag
,Msg
,"SIZE %s",Path
) == false) 
 644    Size 
= strtoull(Msg
.c_str(),&End
,10); 
 645    if (Tag 
>= 400 || End 
== Msg
.c_str()) 
 650 // FTPConn::ModTime - Return the modification time of the file          /*{{{*/ 
 651 // --------------------------------------------------------------------- 
 652 /* Like Size no error is returned if the command is not supported. If the 
 653    command fails then time is set to the current time of day to fool  
 655 bool FTPConn::ModTime(const char *Path
, time_t &Time
) 
 659    // Query the mod time 
 662    if (WriteMsg(Tag
,Msg
,"MDTM %s",Path
) == false) 
 664    if (Tag 
>= 400 || Msg
.empty() == true || isdigit(Msg
[0]) == 0) 
 668    return FTPMDTMStrToTime(Msg
.c_str(), Time
); 
 671 // FTPConn::CreateDataFd - Get a data connection                        /*{{{*/ 
 672 // --------------------------------------------------------------------- 
 673 /* Create the data connection. Call FinalizeDataFd after this though.. */ 
 674 bool FTPConn::CreateDataFd() 
 679    // Attempt to enter passive mode. 
 680    if (TryPassive 
== true) 
 682       if (GoPasv() == false) 
 685       // Oops, didn't work out, don't bother trying again. 
 694       if ((DataFd 
= socket(PasvAddr
->ai_family
,PasvAddr
->ai_socktype
, 
 695                            PasvAddr
->ai_protocol
)) < 0) 
 696          return _error
->Errno("socket",_("Could not create a socket")); 
 698       // Connect to the server 
 699       SetNonBlock(DataFd
,true); 
 700       if (connect(DataFd
,PasvAddr
->ai_addr
,PasvAddr
->ai_addrlen
) < 0 && 
 701           errno 
!= EINPROGRESS
) 
 702          return _error
->Errno("socket",_("Could not create a socket")); 
 704       /* This implements a timeout for connect by opening the connection 
 706       if (WaitFd(DataFd
,true,TimeOut
) == false) 
 707          return _error
->Error(_("Could not connect data socket, connection timed out")); 
 709       unsigned int Len 
= sizeof(Err
); 
 710       if (getsockopt(DataFd
,SOL_SOCKET
,SO_ERROR
,&Err
,&Len
) != 0) 
 711          return _error
->Errno("getsockopt",_("Failed")); 
 713          return _error
->Error(_("Could not connect passive socket.")); 
 722    // Get the information for a listening socket. 
 723    struct addrinfo 
*BindAddr 
= NULL
; 
 724    struct addrinfo Hints
; 
 725    memset(&Hints
,0,sizeof(Hints
)); 
 726    Hints
.ai_socktype 
= SOCK_STREAM
; 
 727    Hints
.ai_flags 
|= AI_PASSIVE
; 
 728    Hints
.ai_family 
= ((struct sockaddr 
*)&ServerAddr
)->sa_family
; 
 729    if (getaddrinfo(0,"0",&Hints
,&BindAddr
) != 0 || BindAddr 
== NULL
) 
 730       return _error
->Error(_("getaddrinfo was unable to get a listening socket")); 
 732    // Construct the socket 
 733    if ((DataListenFd 
= socket(BindAddr
->ai_family
,BindAddr
->ai_socktype
, 
 734                               BindAddr
->ai_protocol
)) < 0) 
 736       freeaddrinfo(BindAddr
); 
 737       return _error
->Errno("socket",_("Could not create a socket")); 
 741    if (bind(DataListenFd
,BindAddr
->ai_addr
,BindAddr
->ai_addrlen
) < 0) 
 743       freeaddrinfo(BindAddr
); 
 744       return _error
->Errno("bind",_("Could not bind a socket")); 
 746    freeaddrinfo(BindAddr
);    
 747    if (listen(DataListenFd
,1) < 0) 
 748       return _error
->Errno("listen",_("Could not listen on the socket")); 
 749    SetNonBlock(DataListenFd
,true); 
 751    // Determine the name to send to the remote 
 752    struct sockaddr_storage Addr
; 
 753    socklen_t AddrLen 
= sizeof(Addr
); 
 754    if (getsockname(DataListenFd
,(sockaddr 
*)&Addr
,&AddrLen
) < 0) 
 755       return _error
->Errno("getsockname",_("Could not determine the socket's name")); 
 758    // Reverse the address. We need the server address and the data port. 
 759    char Name
[NI_MAXHOST
]; 
 760    char Service
[NI_MAXSERV
]; 
 761    char Service2
[NI_MAXSERV
]; 
 762    getnameinfo((struct sockaddr 
*)&Addr
,AddrLen
, 
 763                Name
,sizeof(Name
),Service
,sizeof(Service
), 
 764                NI_NUMERICHOST
|NI_NUMERICSERV
); 
 765    getnameinfo((struct sockaddr 
*)&ServerAddr
,ServerAddrLen
, 
 766                Name
,sizeof(Name
),Service2
,sizeof(Service2
), 
 767                NI_NUMERICHOST
|NI_NUMERICSERV
); 
 769    // Send off an IPv4 address in the old port format 
 770    if (((struct sockaddr 
*)&Addr
)->sa_family 
== AF_INET 
&&  
 771        ForceExtended 
== false) 
 773       // Convert the dots in the quad into commas 
 774       for (char *I 
= Name
; *I 
!= 0; I
++) 
 777       unsigned long Port 
= atoi(Service
); 
 779       // Send the port command 
 782       if (WriteMsg(Tag
,Msg
,"PORT %s,%d,%d", 
 784                    (int)(Port 
>> 8) & 0xff, (int)(Port 
& 0xff)) == false) 
 787          return _error
->Error(_("Unable to send PORT command")); 
 791    // Construct an EPRT command 
 793    for (unsigned J 
= 0; AFMap
[J
].Family 
!= 0; J
++) 
 794       if (AFMap
[J
].Family 
== ((struct sockaddr 
*)&Addr
)->sa_family
) 
 795          Proto 
= AFMap
[J
].IETFFamily
; 
 797       return _error
->Error(_("Unknown address family %u (AF_*)"), 
 798                            ((struct sockaddr 
*)&Addr
)->sa_family
); 
 800    // Send the EPRT command 
 803    if (WriteMsg(Tag
,Msg
,"EPRT |%u|%s|%s|",Proto
,Name
,Service
) == false) 
 806       return _error
->Error(_("EPRT failed, server said: %s"),Msg
.c_str()); 
 810 // FTPConn::Finalize - Complete the Data connection                     /*{{{*/ 
 811 // --------------------------------------------------------------------- 
 812 /* If the connection is in port mode this waits for the other end to hook 
 814 bool FTPConn::Finalize() 
 816    // Passive mode? Do nothing 
 820    // Close any old socket.. 
 824    // Wait for someone to connect.. 
 825    if (WaitFd(DataListenFd
,false,TimeOut
) == false) 
 826       return _error
->Error(_("Data socket connect timed out")); 
 828    // Accept the connection 
 829    struct sockaddr_in Addr
; 
 830    socklen_t Len 
= sizeof(Addr
); 
 831    DataFd 
= accept(DataListenFd
,(struct sockaddr 
*)&Addr
,&Len
); 
 833       return _error
->Errno("accept",_("Unable to accept connection")); 
 841 // FTPConn::Get - Get a file                                            /*{{{*/ 
 842 // --------------------------------------------------------------------- 
 843 /* This opens a data connection, sends REST and RETR and then 
 844    transfers the file over. */ 
 845 bool FTPConn::Get(const char *Path
,FileFd 
&To
,unsigned long long Resume
, 
 846                   Hashes 
&Hash
,bool &Missing
) 
 849    if (CreateDataFd() == false) 
 856       if (WriteMsg(Tag
,Msg
,"REST %u",Resume
) == false) 
 862    if (To
.Truncate(Resume
) == false) 
 865    if (To
.Seek(0) == false) 
 870       if (Hash
.AddFD(To
,Resume
) == false) 
 872          _error
->Errno("read",_("Problem hashing file")); 
 877    // Send the get command 
 878    if (WriteMsg(Tag
,Msg
,"RETR %s",Path
) == false) 
 885       return _error
->Error(_("Unable to fetch file, server said '%s'"),Msg
.c_str()); 
 888    // Finish off the data connection 
 889    if (Finalize() == false) 
 893    unsigned char Buffer
[4096]; 
 896       // Wait for some data.. 
 897       if (WaitFd(DataFd
,false,TimeOut
) == false) 
 900          return _error
->Error(_("Data socket timed out")); 
 904       int Res 
= read(DataFd
,Buffer
,sizeof(Buffer
)); 
 914       Hash
.Add(Buffer
,Res
); 
 915       if (To
.Write(Buffer
,Res
) == false) 
 926    // Read the closing message from the server 
 927    if (ReadResp(Tag
,Msg
) == false) 
 930       return _error
->Error(_("Data transfer failed, server said '%s'"),Msg
.c_str()); 
 935 // FtpMethod::FtpMethod - Constructor                                   /*{{{*/ 
 936 // --------------------------------------------------------------------- 
 938 FtpMethod::FtpMethod() : pkgAcqMethod("1.0",SendConfig
) 
 940    signal(SIGTERM
,SigTerm
); 
 941    signal(SIGINT
,SigTerm
); 
 947 // FtpMethod::SigTerm - Handle a fatal signal                           /*{{{*/ 
 948 // --------------------------------------------------------------------- 
 949 /* This closes and timestamps the open file. This is neccessary to get  
 950    resume behavoir on user abort */ 
 951 void FtpMethod::SigTerm(int) 
 957    struct timespec times
[2]; 
 958    times
[0].tv_sec 
= FailTime
; 
 959    times
[1].tv_sec 
= FailTime
; 
 960    times
[0].tv_nsec 
= times
[1].tv_nsec 
= 0; 
 961    futimens(FailFd
, times
); 
 968 // FtpMethod::Configuration - Handle a configuration message            /*{{{*/ 
 969 // --------------------------------------------------------------------- 
 970 /* We stash the desired pipeline depth */ 
 971 bool FtpMethod::Configuration(string Message
) 
 973    if (pkgAcqMethod::Configuration(Message
) == false) 
 976    TimeOut 
= _config
->FindI("Acquire::Ftp::Timeout",TimeOut
); 
 980 // FtpMethod::Fetch - Fetch a file                                      /*{{{*/ 
 981 // --------------------------------------------------------------------- 
 982 /* Fetch a single file, called by the base class..  */ 
 983 bool FtpMethod::Fetch(FetchItem 
*Itm
) 
 986    const char *File 
= Get
.Path
.c_str(); 
 988    Res
.Filename 
= Itm
->DestFile
; 
 991    maybe_add_auth (Get
, _config
->FindFile("Dir::Etc::netrc")); 
 993    // Connect to the server 
 994    if (Server 
== 0 || Server
->Comp(Get
) == false) 
 997       Server 
= new FTPConn(Get
); 
1000    // Could not connect is a transient error.. 
1001    if (Server
->Open(this) == false) 
1008    // Get the files information 
1010    unsigned long long Size
; 
1011    if (Server
->Size(File
,Size
) == false || 
1012        Server
->ModTime(File
,FailTime
) == false) 
1019    // See if it is an IMS hit 
1020    if (Itm
->LastModified 
== FailTime
) 
1028    // See if the file exists 
1030    if (stat(Itm
->DestFile
.c_str(),&Buf
) == 0) 
1032       if (Size 
== (unsigned long long)Buf
.st_size 
&& FailTime 
== Buf
.st_mtime
) 
1034          Res
.Size 
= Buf
.st_size
; 
1035          Res
.LastModified 
= Buf
.st_mtime
; 
1036          Res
.ResumePoint 
= Buf
.st_size
; 
1042       if (FailTime 
== Buf
.st_mtime 
&& Size 
> (unsigned long long)Buf
.st_size
) 
1043          Res
.ResumePoint 
= Buf
.st_size
; 
1049       FileFd 
Fd(Itm
->DestFile
,FileFd::WriteAny
); 
1050       if (_error
->PendingError() == true) 
1055       FailFile 
= Itm
->DestFile
; 
1056       FailFile
.c_str();   // Make sure we dont do a malloc in the signal handler 
1060       if (Server
->Get(File
,Fd
,Res
.ResumePoint
,Hash
,Missing
) == false) 
1065          struct timespec times
[2]; 
1066          times
[0].tv_sec 
= FailTime
; 
1067          times
[1].tv_sec 
= FailTime
; 
1068          times
[0].tv_nsec 
= times
[1].tv_nsec 
= 0; 
1069          futimens(FailFd
, times
); 
1071          // If the file is missing we hard fail and delete the destfile 
1072          // otherwise transient fail 
1073          if (Missing 
== true) { 
1074             unlink(FailFile
.c_str()); 
1081       Res
.Size 
= Fd
.Size(); 
1084       struct timespec times
[2]; 
1085       times
[0].tv_sec 
= FailTime
; 
1086       times
[1].tv_sec 
= FailTime
; 
1087       times
[0].tv_nsec 
= times
[1].tv_nsec 
= 0; 
1088       futimens(Fd
.Fd(), times
); 
1092    Res
.LastModified 
= FailTime
; 
1093    Res
.TakeHashes(Hash
); 
1101 int main(int argc
,const char *argv
[]) 
1103    setlocale(LC_ALL
, ""); 
1105    /* See if we should be come the http client - we do this for http 
1107    if (getenv("ftp_proxy") != 0) 
1109       URI Proxy 
= string(getenv("ftp_proxy")); 
1111       // Run the HTTP method 
1112       if (Proxy
.Access 
== "http") 
1114          // Copy over the environment setting 
1116          snprintf(S
,sizeof(S
),"http_proxy=%s",getenv("ftp_proxy")); 
1118          putenv((char *)"no_proxy="); 
1120          // Run the http method 
1121          string Path 
= flNotFile(argv
[0]) + "http"; 
1122          execl(Path
.c_str(),Path
.c_str(),(char *)NULL
); 
1123          cerr 
<< _("Unable to invoke ") << Path 
<< endl
;