]>
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
[])
1093 #if !defined(__ENVIRONMENT_ASPEN_VERSION_MIN_REQUIRED__) || __ENVIRONMENT_ASPEN_VERSION_MIN_REQUIRED__ < 10200
1095 memset(nl
, 0, sizeof(nl
));
1096 nl
[0].n_un
.n_name
= (char *) "_useMDNSResponder";
1097 nlist("/usr/lib/libc.dylib", nl
);
1098 if (nl
[0].n_type
!= N_UNDF
)
1099 *(int *) nl
[0].n_value
= 0;
1102 setlocale(LC_ALL
, "");
1104 /* See if we should be come the http client - we do this for http
1106 if (getenv("ftp_proxy") != 0)
1108 URI Proxy
= string(getenv("ftp_proxy"));
1110 // Run the HTTP method
1111 if (Proxy
.Access
== "http")
1113 // Copy over the environment setting
1115 snprintf(S
,sizeof(S
),"http_proxy=%s",getenv("ftp_proxy"));
1117 putenv("no_proxy=");
1119 // Run the http method
1120 string Path
= flNotFile(argv
[0]) + "http";
1121 execl(Path
.c_str(),Path
.c_str(),(char *)NULL
);
1122 cerr
<< _("Unable to invoke ") << Path
<< endl
;