]>
git.saurik.com Git - apt-legacy.git/blob - methods/ftp.cc
   2     #include <mach-o/nlist.h> 
   5 // -*- mode: cpp; mode: fold -*- 
   7 // $Id: ftp.cc,v 1.31.2.1 2004/01/16 18:58:50 mdz Exp $ 
   8 /* ###################################################################### 
  10    FTP Aquire Method - This is the FTP aquire method for APT. 
  12    This is a very simple implementation that does not try to optimize 
  13    at all. Commands are sent syncronously with the FTP server (as the 
  14    rfc recommends, but it is not really necessary..) and no tricks are 
  15    done to speed things along. 
  17    RFC 2428 describes the IPv6 FTP behavior 
  19    ##################################################################### */ 
  21 // Include Files                                                        /*{{{*/ 
  22 #include <apt-pkg/fileutl.h> 
  23 #include <apt-pkg/acquire-method.h> 
  24 #include <apt-pkg/error.h> 
  25 #include <apt-pkg/hashes.h> 
  39 #include <netinet/in.h> 
  40 #include <sys/socket.h> 
  41 #include <arpa/inet.h> 
  44 #include "rfc2553emu.h" 
  51 /* This table is for the EPRT and EPSV commands, it maps the OS address 
  52    family to the IETF address families */ 
  56    unsigned long IETFFamily
; 
  60 struct AFMap AFMap
[] = {{AF_INET
,1},{}}; 
  62 struct AFMap AFMap
[] = {{AF_INET
,1},{AF_INET6
,2},{}}; 
  65 unsigned long TimeOut 
= 120; 
  67 string 
FtpMethod::FailFile
; 
  68 int FtpMethod::FailFd 
= -1; 
  69 time_t FtpMethod::FailTime 
= 0; 
  71 // FTPConn::FTPConn - Constructor                                       /*{{{*/ 
  72 // --------------------------------------------------------------------- 
  74 FTPConn::FTPConn(URI Srv
) : Len(0), ServerFd(-1), DataFd(-1),  
  75                             DataListenFd(-1), ServerName(Srv
) 
  77    Debug 
= _config
->FindB("Debug::Acquire::Ftp",false); 
  81 // FTPConn::~FTPConn - Destructor                                       /*{{{*/ 
  82 // --------------------------------------------------------------------- 
  89 // FTPConn::Close - Close down the connection                           /*{{{*/ 
  90 // --------------------------------------------------------------------- 
  91 /* Just tear down the socket and data socket */ 
 102       freeaddrinfo(PasvAddr
); 
 106 // FTPConn::Open - Open a new connection                                /*{{{*/ 
 107 // --------------------------------------------------------------------- 
 108 /* Connect to the server using a non-blocking connection and perform a  
 110 bool FTPConn::Open(pkgAcqMethod 
*Owner
) 
 112    // Use the already open connection if possible. 
 118    // Determine the proxy setting 
 119    if (getenv("ftp_proxy") == 0) 
 121       string DefProxy 
= _config
->Find("Acquire::ftp::Proxy"); 
 122       string SpecificProxy 
= _config
->Find("Acquire::ftp::Proxy::" + ServerName
.Host
); 
 123       if (SpecificProxy
.empty() == false) 
 125          if (SpecificProxy 
== "DIRECT") 
 128             Proxy 
= SpecificProxy
; 
 134       Proxy 
= getenv("ftp_proxy"); 
 136    // Parse no_proxy, a , separated list of domains 
 137    if (getenv("no_proxy") != 0) 
 139       if (CheckDomainList(ServerName
.Host
,getenv("no_proxy")) == true) 
 143    // Determine what host and port to use based on the proxy settings 
 146    if (Proxy
.empty() == true) 
 148       if (ServerName
.Port 
!= 0) 
 149          Port 
= ServerName
.Port
; 
 150       Host 
= ServerName
.Host
; 
 159    /* Connect to the remote server. Since FTP is connection oriented we 
 160       want to make sure we get a new server every time we reconnect */ 
 162    if (Connect(Host
,Port
,"ftp",21,ServerFd
,TimeOut
,Owner
) == false) 
 165    // Login must be before getpeername otherwise dante won't work. 
 166    Owner
->Status(_("Logging in")); 
 169    // Get the remote server's address 
 170    PeerAddrLen 
= sizeof(PeerAddr
); 
 171    if (getpeername(ServerFd
,(sockaddr 
*)&PeerAddr
,&PeerAddrLen
) != 0) 
 172       return _error
->Errno("getpeername",_("Unable to determine the peer name")); 
 174    // Get the local machine's address 
 175    ServerAddrLen 
= sizeof(ServerAddr
); 
 176    if (getsockname(ServerFd
,(sockaddr 
*)&ServerAddr
,&ServerAddrLen
) != 0) 
 177       return _error
->Errno("getsockname",_("Unable to determine the local name")); 
 182 // FTPConn::Login - Login to the remote server                          /*{{{*/ 
 183 // --------------------------------------------------------------------- 
 184 /* This performs both normal login and proxy login using a simples script 
 185    stored in the config file. */ 
 186 bool FTPConn::Login() 
 191    // Setup the variables needed for authentication 
 192    string User 
= "anonymous"; 
 193    string Pass 
= "apt_get_ftp_2.1@debian.linux.user"; 
 195    // Fill in the user/pass 
 196    if (ServerName
.User
.empty() == false) 
 197       User 
= ServerName
.User
; 
 198    if (ServerName
.Password
.empty() == false) 
 199       Pass 
= ServerName
.Password
; 
 201    // Perform simple login 
 202    if (Proxy
.empty() == true) 
 204       // Read the initial response 
 205       if (ReadResp(Tag
,Msg
) == false) 
 208          return _error
->Error(_("The server refused the connection and said: %s"),Msg
.c_str()); 
 211       if (WriteMsg(Tag
,Msg
,"USER %s",User
.c_str()) == false) 
 214          return _error
->Error(_("USER failed, server said: %s"),Msg
.c_str()); 
 216       if (Tag 
== 331) { // 331 User name okay, need password. 
 218          if (WriteMsg(Tag
,Msg
,"PASS %s",Pass
.c_str()) == false) 
 221             return _error
->Error(_("PASS failed, server said: %s"),Msg
.c_str()); 
 224       // Enter passive mode 
 225       if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true) 
 226          TryPassive 
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true); 
 228          TryPassive 
= _config
->FindB("Acquire::FTP::Passive",true);       
 232       // Read the initial response 
 233       if (ReadResp(Tag
,Msg
) == false) 
 236          return _error
->Error(_("The server refused the connection and said: %s"),Msg
.c_str()); 
 238       // Perform proxy script execution 
 239       Configuration::Item 
const *Opts 
= _config
->Tree("Acquire::ftp::ProxyLogin"); 
 240       if (Opts 
== 0 || Opts
->Child 
== 0) 
 241          return _error
->Error(_("A proxy server was specified but no login " 
 242                               "script, Acquire::ftp::ProxyLogin is empty.")); 
 245       // Iterate over the entire login script 
 246       for (; Opts 
!= 0; Opts 
= Opts
->Next
) 
 248          if (Opts
->Value
.empty() == true) 
 251          // Substitute the variables into the command 
 253          if (ServerName
.Port 
!= 0) 
 254             sprintf(SitePort
,"%u",ServerName
.Port
); 
 256             strcpy(SitePort
,"21"); 
 257          string Tmp 
= Opts
->Value
; 
 258          Tmp 
= SubstVar(Tmp
,"$(PROXY_USER)",Proxy
.User
); 
 259          Tmp 
= SubstVar(Tmp
,"$(PROXY_PASS)",Proxy
.Password
); 
 260          Tmp 
= SubstVar(Tmp
,"$(SITE_USER)",User
); 
 261          Tmp 
= SubstVar(Tmp
,"$(SITE_PASS)",Pass
); 
 262          Tmp 
= SubstVar(Tmp
,"$(SITE_PORT)",SitePort
); 
 263          Tmp 
= SubstVar(Tmp
,"$(SITE)",ServerName
.Host
); 
 266          if (WriteMsg(Tag
,Msg
,"%s",Tmp
.c_str()) == false) 
 269             return _error
->Error(_("Login script command '%s' failed, server said: %s"),Tmp
.c_str(),Msg
.c_str());         
 272       // Enter passive mode 
 274       if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true) 
 275          TryPassive 
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true); 
 278          if (_config
->Exists("Acquire::FTP::Proxy::Passive") == true) 
 279             TryPassive 
= _config
->FindB("Acquire::FTP::Proxy::Passive",true); 
 281             TryPassive 
= _config
->FindB("Acquire::FTP::Passive",true); 
 285    // Force the use of extended commands 
 286    if (_config
->Exists("Acquire::FTP::ForceExtended::" + ServerName
.Host
) == true) 
 287       ForceExtended 
= _config
->FindB("Acquire::FTP::ForceExtended::" + ServerName
.Host
,true); 
 289       ForceExtended 
= _config
->FindB("Acquire::FTP::ForceExtended",false); 
 292    if (WriteMsg(Tag
,Msg
,"TYPE I") == false) 
 295       return _error
->Error(_("TYPE failed, server said: %s"),Msg
.c_str()); 
 300 // FTPConn::ReadLine - Read a line from the server                      /*{{{*/ 
 301 // --------------------------------------------------------------------- 
 302 /* This performs a very simple buffered read. */ 
 303 bool FTPConn::ReadLine(string 
&Text
) 
 309    while (Len 
< sizeof(Buffer
)) 
 311       // Scan the buffer for a new line 
 312       for (unsigned int I 
= 0; I 
!= Len
; I
++) 
 314          // Escape some special chars 
 319          if (Buffer
[I
] != '\n') 
 323          Text 
= string(Buffer
,I
); 
 324          memmove(Buffer
,Buffer
+I
,Len 
- I
); 
 329       // Wait for some data.. 
 330       if (WaitFd(ServerFd
,false,TimeOut
) == false) 
 333          return _error
->Error(_("Connection timeout")); 
 337       int Res 
= read(ServerFd
,Buffer 
+ Len
,sizeof(Buffer
) - Len
); 
 339          _error
->Error(_("Server closed the connection")); 
 342          _error
->Errno("read",_("Read error")); 
 349    return _error
->Error(_("A response overflowed the buffer.")); 
 352 // FTPConn::ReadResp - Read a full response from the server             /*{{{*/ 
 353 // --------------------------------------------------------------------- 
 354 /* This reads a reply code from the server, it handles both p */ 
 355 bool FTPConn::ReadResp(unsigned int &Ret
,string 
&Text
) 
 357    // Grab the first line of the response 
 359    if (ReadLine(Msg
) == false) 
 364    Ret 
= strtol(Msg
.c_str(),&End
,10); 
 365    if (End 
- Msg
.c_str() != 3) 
 366       return _error
->Error(_("Protocol corruption")); 
 369    Text 
= Msg
.c_str()+4; 
 373          cerr 
<< "<- '" << QuoteString(Text
,"") << "'" << endl
; 
 378       return _error
->Error(_("Protocol corruption")); 
 380    /* Okay, here we do the continued message trick. This is foolish, but 
 381       proftpd follows the protocol as specified and wu-ftpd doesn't, so  
 382       we filter. I wonder how many clients break if you use proftpd and 
 383       put a '- in the 3rd spot in the message? */ 
 385    strncpy(Leader
,Msg
.c_str(),3); 
 387    while (ReadLine(Msg
) == true) 
 389       // Short, it must be using RFC continuation.. 
 390       if (Msg
.length() < 4) 
 397       if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == ' ') 
 399          Text 
+= Msg
.c_str()+4; 
 403       // This message has the wu-ftpd style reply code prefixed 
 404       if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == '-') 
 406          Text 
+= Msg
.c_str()+4; 
 410       // Must be RFC style prefixing 
 414    if (Debug 
== true && _error
->PendingError() == false) 
 415       cerr 
<< "<- '" << QuoteString(Text
,"") << "'" << endl
; 
 417    return !_error
->PendingError(); 
 420 // FTPConn::WriteMsg - Send a message to the server                     /*{{{*/ 
 421 // --------------------------------------------------------------------- 
 422 /* Simple printf like function.. */ 
 423 bool FTPConn::WriteMsg(unsigned int &Ret
,string 
&Text
,const char *Fmt
,...) 
 428    // sprintf the description 
 430    vsnprintf(S
,sizeof(S
) - 4,Fmt
,args
); 
 434       cerr 
<< "-> '" << QuoteString(S
,"") << "'" << endl
; 
 437    unsigned long Len 
= strlen(S
); 
 438    unsigned long Start 
= 0; 
 441       if (WaitFd(ServerFd
,true,TimeOut
) == false) 
 444          return _error
->Error(_("Connection timeout")); 
 447       int Res 
= write(ServerFd
,S 
+ Start
,Len
); 
 450          _error
->Errno("write",_("Write error")); 
 459    return ReadResp(Ret
,Text
); 
 462 // FTPConn::GoPasv - Enter Passive mode                                 /*{{{*/ 
 463 // --------------------------------------------------------------------- 
 464 /* Try to enter passive mode, the return code does not indicate if passive 
 465    mode could or could not be established, only if there was a fatal error.  
 466    We have to enter passive mode every time we make a data connection :| */ 
 467 bool FTPConn::GoPasv() 
 469    /* The PASV command only works on IPv4 sockets, even though it could 
 470       in theory suppory IPv6 via an all zeros reply */ 
 471    if (((struct sockaddr 
*)&PeerAddr
)->sa_family 
!= AF_INET 
||  
 472        ForceExtended 
== true) 
 476       freeaddrinfo(PasvAddr
); 
 479    // Try to enable pasv mode 
 482    if (WriteMsg(Tag
,Msg
,"PASV") == false) 
 485    // Unsupported function 
 486    string::size_type Pos 
= Msg
.find('('); 
 487    if (Tag 
>= 400 || Pos 
== string::npos
) 
 491    unsigned a0
,a1
,a2
,a3
,p0
,p1
; 
 492    if (sscanf(Msg
.c_str() + Pos
,"(%u,%u,%u,%u,%u,%u)",&a0
,&a1
,&a2
,&a3
,&p0
,&p1
) != 6) 
 495    /* Some evil servers return 0 to mean their addr. We can actually speak 
 496       to these servers natively using IPv6 */ 
 497    if (a0 
== 0 && a1 
== 0 && a2 
== 0 && a3 
== 0) 
 499       // Get the IP in text form 
 500       char Name
[NI_MAXHOST
]; 
 501       char Service
[NI_MAXSERV
]; 
 502       getnameinfo((struct sockaddr 
*)&PeerAddr
,PeerAddrLen
, 
 503                   Name
,sizeof(Name
),Service
,sizeof(Service
), 
 504                   NI_NUMERICHOST
|NI_NUMERICSERV
); 
 506       struct addrinfo Hints
; 
 507       memset(&Hints
,0,sizeof(Hints
)); 
 508       Hints
.ai_socktype 
= SOCK_STREAM
; 
 509       Hints
.ai_family 
= ((struct sockaddr 
*)&PeerAddr
)->sa_family
; 
 510       Hints
.ai_flags 
|= AI_NUMERICHOST
; 
 512       // Get a new passive address. 
 514       snprintf(Port
,sizeof(Port
),"%u",(p0 
<< 8) + p1
); 
 515       if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0) 
 520    struct addrinfo Hints
; 
 521    memset(&Hints
,0,sizeof(Hints
)); 
 522    Hints
.ai_socktype 
= SOCK_STREAM
; 
 523    Hints
.ai_family 
= AF_INET
; 
 524    Hints
.ai_flags 
|= AI_NUMERICHOST
; 
 526    // Get a new passive address. 
 528    snprintf(Port
,sizeof(Port
),"%u",(p0 
<< 8) + p1
); 
 530    snprintf(Name
,sizeof(Name
),"%u.%u.%u.%u",a0
,a1
,a2
,a3
); 
 531    if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0) 
 536 // FTPConn::ExtGoPasv - Enter Extended Passive mode                     /*{{{*/ 
 537 // --------------------------------------------------------------------- 
 538 /* Try to enter extended passive mode. See GoPasv above and RFC 2428 */ 
 539 bool FTPConn::ExtGoPasv() 
 542       freeaddrinfo(PasvAddr
); 
 545    // Try to enable pasv mode 
 548    if (WriteMsg(Tag
,Msg
,"EPSV") == false) 
 551    // Unsupported function 
 552    string::size_type Pos 
= Msg
.find('('); 
 553    if (Tag 
>= 400 || Pos 
== string::npos
) 
 557    string::const_iterator List
[4]; 
 560    for (string::const_iterator I 
= Msg
.begin() + Pos
; I 
< Msg
.end(); I
++) 
 572    unsigned long Proto 
= 0; 
 573    unsigned long Port 
= 0; 
 575    IP 
= string(List
[1]+1,List
[2]); 
 576    Port 
= atoi(string(List
[2]+1,List
[3]).c_str()); 
 577    if (IP
.empty() == false) 
 578       Proto 
= atoi(string(List
[0]+1,List
[1]).c_str()); 
 583    // String version of the port 
 585    snprintf(PStr
,sizeof(PStr
),"%lu",Port
); 
 587    // Get the IP in text form 
 588    struct addrinfo Hints
; 
 589    memset(&Hints
,0,sizeof(Hints
)); 
 590    Hints
.ai_socktype 
= SOCK_STREAM
; 
 591    Hints
.ai_flags 
|= AI_NUMERICHOST
; 
 593    /* The RFC defined case, connect to the old IP/protocol using the 
 595    if (IP
.empty() == true) 
 597       // Get the IP in text form 
 598       char Name
[NI_MAXHOST
]; 
 599       char Service
[NI_MAXSERV
]; 
 600       getnameinfo((struct sockaddr 
*)&PeerAddr
,PeerAddrLen
, 
 601                   Name
,sizeof(Name
),Service
,sizeof(Service
), 
 602                   NI_NUMERICHOST
|NI_NUMERICSERV
); 
 604       Hints
.ai_family 
= ((struct sockaddr 
*)&PeerAddr
)->sa_family
; 
 610       for (unsigned J 
= 0; AFMap
[J
].Family 
!= 0; J
++) 
 611          if (AFMap
[J
].IETFFamily 
== Proto
) 
 612             Hints
.ai_family 
= AFMap
[J
].Family
; 
 613       if (Hints
.ai_family 
== 0) 
 617    // Get a new passive address. 
 619    if ((Res 
= getaddrinfo(IP
.c_str(),PStr
,&Hints
,&PasvAddr
)) != 0) 
 625 // FTPConn::Size - Return the size of a file                            /*{{{*/ 
 626 // --------------------------------------------------------------------- 
 627 /* Grab the file size from the server, 0 means no size or empty file */ 
 628 bool FTPConn::Size(const char *Path
,unsigned long &Size
) 
 634    if (WriteMsg(Tag
,Msg
,"SIZE %s",Path
) == false) 
 638    Size 
= strtol(Msg
.c_str(),&End
,10); 
 639    if (Tag 
>= 400 || End 
== Msg
.c_str()) 
 644 // FTPConn::ModTime - Return the modification time of the file          /*{{{*/ 
 645 // --------------------------------------------------------------------- 
 646 /* Like Size no error is returned if the command is not supported. If the 
 647    command fails then time is set to the current time of day to fool  
 649 bool FTPConn::ModTime(const char *Path
, time_t &Time
) 
 653    // Query the mod time 
 656    if (WriteMsg(Tag
,Msg
,"MDTM %s",Path
) == false) 
 658    if (Tag 
>= 400 || Msg
.empty() == true || isdigit(Msg
[0]) == 0) 
 666 // FTPConn::CreateDataFd - Get a data connection                        /*{{{*/ 
 667 // --------------------------------------------------------------------- 
 668 /* Create the data connection. Call FinalizeDataFd after this though.. */ 
 669 bool FTPConn::CreateDataFd() 
 674    // Attempt to enter passive mode. 
 675    if (TryPassive 
== true) 
 677       if (GoPasv() == false) 
 680       // Oops, didn't work out, don't bother trying again. 
 689       if ((DataFd 
= socket(PasvAddr
->ai_family
,PasvAddr
->ai_socktype
, 
 690                            PasvAddr
->ai_protocol
)) < 0) 
 691          return _error
->Errno("socket",_("Could not create a socket")); 
 693       // Connect to the server 
 694       SetNonBlock(DataFd
,true); 
 695       if (connect(DataFd
,PasvAddr
->ai_addr
,PasvAddr
->ai_addrlen
) < 0 && 
 696           errno 
!= EINPROGRESS
) 
 697          return _error
->Errno("socket",_("Could not create a socket")); 
 699       /* This implements a timeout for connect by opening the connection 
 701       if (WaitFd(DataFd
,true,TimeOut
) == false) 
 702          return _error
->Error(_("Could not connect data socket, connection timed out")); 
 704       unsigned int Len 
= sizeof(Err
); 
 705       if (getsockopt(DataFd
,SOL_SOCKET
,SO_ERROR
,&Err
,&Len
) != 0) 
 706          return _error
->Errno("getsockopt",_("Failed")); 
 708          return _error
->Error(_("Could not connect passive socket.")); 
 717    // Get the information for a listening socket. 
 718    struct addrinfo 
*BindAddr 
= 0; 
 719    struct addrinfo Hints
; 
 720    memset(&Hints
,0,sizeof(Hints
)); 
 721    Hints
.ai_socktype 
= SOCK_STREAM
; 
 722    Hints
.ai_flags 
|= AI_PASSIVE
; 
 723    Hints
.ai_family 
= ((struct sockaddr 
*)&ServerAddr
)->sa_family
; 
 725    if ((Res 
= getaddrinfo(0,"0",&Hints
,&BindAddr
)) != 0) 
 726       return _error
->Error(_("getaddrinfo was unable to get a listening socket")); 
 728    // Construct the socket 
 729    if ((DataListenFd 
= socket(BindAddr
->ai_family
,BindAddr
->ai_socktype
, 
 730                               BindAddr
->ai_protocol
)) < 0) 
 732       freeaddrinfo(BindAddr
); 
 733       return _error
->Errno("socket",_("Could not create a socket")); 
 737    if (bind(DataListenFd
,BindAddr
->ai_addr
,BindAddr
->ai_addrlen
) < 0) 
 739       freeaddrinfo(BindAddr
); 
 740       return _error
->Errno("bind",_("Could not bind a socket")); 
 742    freeaddrinfo(BindAddr
);    
 743    if (listen(DataListenFd
,1) < 0) 
 744       return _error
->Errno("listen",_("Could not listen on the socket")); 
 745    SetNonBlock(DataListenFd
,true); 
 747    // Determine the name to send to the remote 
 748    struct sockaddr_storage Addr
; 
 749    socklen_t AddrLen 
= sizeof(Addr
); 
 750    if (getsockname(DataListenFd
,(sockaddr 
*)&Addr
,&AddrLen
) < 0) 
 751       return _error
->Errno("getsockname",_("Could not determine the socket's name")); 
 754    // Reverse the address. We need the server address and the data port. 
 755    char Name
[NI_MAXHOST
]; 
 756    char Service
[NI_MAXSERV
]; 
 757    char Service2
[NI_MAXSERV
]; 
 758    getnameinfo((struct sockaddr 
*)&Addr
,AddrLen
, 
 759                Name
,sizeof(Name
),Service
,sizeof(Service
), 
 760                NI_NUMERICHOST
|NI_NUMERICSERV
); 
 761    getnameinfo((struct sockaddr 
*)&ServerAddr
,ServerAddrLen
, 
 762                Name
,sizeof(Name
),Service2
,sizeof(Service2
), 
 763                NI_NUMERICHOST
|NI_NUMERICSERV
); 
 765    // Send off an IPv4 address in the old port format 
 766    if (((struct sockaddr 
*)&Addr
)->sa_family 
== AF_INET 
&&  
 767        ForceExtended 
== false) 
 769       // Convert the dots in the quad into commas 
 770       for (char *I 
= Name
; *I 
!= 0; I
++) 
 773       unsigned long Port 
= atoi(Service
); 
 775       // Send the port command 
 778       if (WriteMsg(Tag
,Msg
,"PORT %s,%d,%d", 
 780                    (int)(Port 
>> 8) & 0xff, (int)(Port 
& 0xff)) == false) 
 783          return _error
->Error(_("Unable to send PORT command")); 
 787    // Construct an EPRT command 
 789    for (unsigned J 
= 0; AFMap
[J
].Family 
!= 0; J
++) 
 790       if (AFMap
[J
].Family 
== ((struct sockaddr 
*)&Addr
)->sa_family
) 
 791          Proto 
= AFMap
[J
].IETFFamily
; 
 793       return _error
->Error(_("Unknown address family %u (AF_*)"), 
 794                            ((struct sockaddr 
*)&Addr
)->sa_family
); 
 796    // Send the EPRT command 
 799    if (WriteMsg(Tag
,Msg
,"EPRT |%u|%s|%s|",Proto
,Name
,Service
) == false) 
 802       return _error
->Error(_("EPRT failed, server said: %s"),Msg
.c_str()); 
 806 // FTPConn::Finalize - Complete the Data connection                     /*{{{*/ 
 807 // --------------------------------------------------------------------- 
 808 /* If the connection is in port mode this waits for the other end to hook 
 810 bool FTPConn::Finalize() 
 812    // Passive mode? Do nothing 
 816    // Close any old socket.. 
 820    // Wait for someone to connect.. 
 821    if (WaitFd(DataListenFd
,false,TimeOut
) == false) 
 822       return _error
->Error(_("Data socket connect timed out")); 
 824    // Accept the connection 
 825    struct sockaddr_in Addr
; 
 826    socklen_t Len 
= sizeof(Addr
); 
 827    DataFd 
= accept(DataListenFd
,(struct sockaddr 
*)&Addr
,&Len
); 
 829       return _error
->Errno("accept",_("Unable to accept connection")); 
 837 // FTPConn::Get - Get a file                                            /*{{{*/ 
 838 // --------------------------------------------------------------------- 
 839 /* This opens a data connection, sends REST and RETR and then 
 840    transfers the file over. */ 
 841 bool FTPConn::Get(const char *Path
,FileFd 
&To
,unsigned long Resume
, 
 842                   Hashes 
&Hash
,bool &Missing
) 
 845    if (CreateDataFd() == false) 
 852       if (WriteMsg(Tag
,Msg
,"REST %u",Resume
) == false) 
 858    if (To
.Truncate(Resume
) == false) 
 861    if (To
.Seek(0) == false) 
 866       if (Hash
.AddFD(To
.Fd(),Resume
) == false) 
 868          _error
->Errno("read",_("Problem hashing file")); 
 873    // Send the get command 
 874    if (WriteMsg(Tag
,Msg
,"RETR %s",Path
) == false) 
 881       return _error
->Error(_("Unable to fetch file, server said '%s'"),Msg
.c_str()); 
 884    // Finish off the data connection 
 885    if (Finalize() == false) 
 889    unsigned char Buffer
[4096]; 
 892       // Wait for some data.. 
 893       if (WaitFd(DataFd
,false,TimeOut
) == false) 
 896          return _error
->Error(_("Data socket timed out")); 
 900       int Res 
= read(DataFd
,Buffer
,sizeof(Buffer
)); 
 910       Hash
.Add(Buffer
,Res
); 
 911       if (To
.Write(Buffer
,Res
) == false) 
 922    // Read the closing message from the server 
 923    if (ReadResp(Tag
,Msg
) == false) 
 926       return _error
->Error(_("Data transfer failed, server said '%s'"),Msg
.c_str()); 
 931 // FtpMethod::FtpMethod - Constructor                                   /*{{{*/ 
 932 // --------------------------------------------------------------------- 
 934 FtpMethod::FtpMethod() : pkgAcqMethod("1.0",SendConfig
) 
 936    signal(SIGTERM
,SigTerm
); 
 937    signal(SIGINT
,SigTerm
); 
 943 // FtpMethod::SigTerm - Handle a fatal signal                           /*{{{*/ 
 944 // --------------------------------------------------------------------- 
 945 /* This closes and timestamps the open file. This is neccessary to get  
 946    resume behavoir on user abort */ 
 947 void FtpMethod::SigTerm(int) 
 955    UBuf
.actime 
= FailTime
; 
 956    UBuf
.modtime 
= FailTime
; 
 957    utime(FailFile
.c_str(),&UBuf
); 
 962 // FtpMethod::Configuration - Handle a configuration message            /*{{{*/ 
 963 // --------------------------------------------------------------------- 
 964 /* We stash the desired pipeline depth */ 
 965 bool FtpMethod::Configuration(string Message
) 
 967    if (pkgAcqMethod::Configuration(Message
) == false) 
 970    TimeOut 
= _config
->FindI("Acquire::Ftp::Timeout",TimeOut
); 
 974 // FtpMethod::Fetch - Fetch a file                                      /*{{{*/ 
 975 // --------------------------------------------------------------------- 
 976 /* Fetch a single file, called by the base class..  */ 
 977 bool FtpMethod::Fetch(FetchItem 
*Itm
) 
 980    const char *File 
= Get
.Path
.c_str(); 
 982    Res
.Filename 
= Itm
->DestFile
; 
 985    // Connect to the server 
 986    if (Server 
== 0 || Server
->Comp(Get
) == false) 
 989       Server 
= new FTPConn(Get
); 
 992    // Could not connect is a transient error.. 
 993    if (Server
->Open(this) == false) 
1000    // Get the files information 
1003    if (Server
->Size(File
,Size
) == false || 
1004        Server
->ModTime(File
,FailTime
) == false) 
1011    // See if it is an IMS hit 
1012    if (Itm
->LastModified 
== FailTime
) 
1020    // See if the file exists 
1022    if (stat(Itm
->DestFile
.c_str(),&Buf
) == 0) 
1024       if (Size 
== (unsigned)Buf
.st_size 
&& FailTime 
== Buf
.st_mtime
) 
1026          Res
.Size 
= Buf
.st_size
; 
1027          Res
.LastModified 
= Buf
.st_mtime
; 
1028          Res
.ResumePoint 
= Buf
.st_size
; 
1034       if (FailTime 
== Buf
.st_mtime 
&& Size 
> (unsigned)Buf
.st_size
) 
1035          Res
.ResumePoint 
= Buf
.st_size
; 
1041       FileFd 
Fd(Itm
->DestFile
,FileFd::WriteAny
); 
1042       if (_error
->PendingError() == true) 
1047       FailFile 
= Itm
->DestFile
; 
1048       FailFile
.c_str();   // Make sure we dont do a malloc in the signal handler 
1052       if (Server
->Get(File
,Fd
,Res
.ResumePoint
,Hash
,Missing
) == false) 
1057          struct utimbuf UBuf
; 
1058          UBuf
.actime 
= FailTime
; 
1059          UBuf
.modtime 
= FailTime
; 
1060          utime(FailFile
.c_str(),&UBuf
); 
1062          // If the file is missing we hard fail and delete the destfile 
1063          // otherwise transient fail 
1064          if (Missing 
== true) { 
1065             unlink(FailFile
.c_str()); 
1072       Res
.Size 
= Fd
.Size(); 
1075    Res
.LastModified 
= FailTime
; 
1076    Res
.TakeHashes(Hash
); 
1079    struct utimbuf UBuf
; 
1080    UBuf
.actime 
= FailTime
; 
1081    UBuf
.modtime 
= FailTime
; 
1082    utime(Queue
->DestFile
.c_str(),&UBuf
); 
1091 int main(int argc
,const char *argv
[]) 
1094    memset(nl
, 0, sizeof(nl
)); 
1095    nl
[0].n_un
.n_name 
= (char *) "_useMDNSResponder"; 
1096    nlist("/usr/lib/libc.dylib", nl
); 
1097    if (nl
[0].n_type 
!= N_UNDF
) 
1098        *(int *) nl
[0].n_value 
= 0; 
1100    setlocale(LC_ALL
, ""); 
1102    /* See if we should be come the http client - we do this for http 
1104    if (getenv("ftp_proxy") != 0) 
1106       URI Proxy 
= string(getenv("ftp_proxy")); 
1108       // Run the HTTP method 
1109       if (Proxy
.Access 
== "http") 
1111          // Copy over the environment setting 
1113          snprintf(S
,sizeof(S
),"http_proxy=%s",getenv("ftp_proxy")); 
1115          putenv("no_proxy="); 
1117          // Run the http method 
1118          string Path 
= flNotFile(argv
[0]) + "http"; 
1119          execl(Path
.c_str(),Path
.c_str(),(char *)NULL
); 
1120          cerr 
<< _("Unable to invoke ") << Path 
<< endl
;