]>
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
;