]>
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 Acquire Method - This is the FTP acquire 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/error.h>
22 #include <apt-pkg/hashes.h>
23 #include <apt-pkg/netrc.h>
24 #include <apt-pkg/configuration.h>
25 #include <apt-pkg/strutl.h>
40 #include <netinet/in.h>
41 #include <arpa/inet.h>
44 #include "rfc2553emu.h"
53 /* This table is for the EPRT and EPSV commands, it maps the OS address
54 family to the IETF address families */
58 unsigned long IETFFamily
;
62 struct AFMap AFMap
[] = {{AF_INET
,1},{0, 0}};
64 struct AFMap AFMap
[] = {{AF_INET
,1},{AF_INET6
,2},{0, 0}};
67 unsigned long TimeOut
= 120;
69 string
FtpMethod::FailFile
;
70 int FtpMethod::FailFd
= -1;
71 time_t FtpMethod::FailTime
= 0;
73 // FTPConn::FTPConn - Constructor /*{{{*/
74 // ---------------------------------------------------------------------
76 FTPConn::FTPConn(URI Srv
) : Len(0), ServerFd(-1), DataFd(-1),
77 DataListenFd(-1), ServerName(Srv
),
78 ForceExtended(false), TryPassive(true),
79 PeerAddrLen(0), ServerAddrLen(0)
81 Debug
= _config
->FindB("Debug::Acquire::Ftp",false);
86 // FTPConn::~FTPConn - Destructor /*{{{*/
87 // ---------------------------------------------------------------------
94 // FTPConn::Close - Close down the connection /*{{{*/
95 // ---------------------------------------------------------------------
96 /* Just tear down the socket and data socket */
107 freeaddrinfo(PasvAddr
);
111 // FTPConn::Open - Open a new connection /*{{{*/
112 // ---------------------------------------------------------------------
113 /* Connect to the server using a non-blocking connection and perform a
115 bool FTPConn::Open(pkgAcqMethod
*Owner
)
117 // Use the already open connection if possible.
123 // Determine the proxy setting
124 string SpecificProxy
= _config
->Find("Acquire::ftp::Proxy::" + ServerName
.Host
);
125 if (!SpecificProxy
.empty())
127 if (SpecificProxy
== "DIRECT")
130 Proxy
= SpecificProxy
;
134 string DefProxy
= _config
->Find("Acquire::ftp::Proxy");
135 if (!DefProxy
.empty())
141 char* result
= getenv("ftp_proxy");
142 Proxy
= result
? result
: "";
146 // Parse no_proxy, a , separated list of domains
147 if (getenv("no_proxy") != 0)
149 if (CheckDomainList(ServerName
.Host
,getenv("no_proxy")) == true)
153 // Determine what host and port to use based on the proxy settings
156 if (Proxy
.empty() == true)
158 if (ServerName
.Port
!= 0)
159 Port
= ServerName
.Port
;
160 Host
= ServerName
.Host
;
169 /* Connect to the remote server. Since FTP is connection oriented we
170 want to make sure we get a new server every time we reconnect */
172 if (Connect(Host
,Port
,"ftp",21,ServerFd
,TimeOut
,Owner
) == false)
175 // Login must be before getpeername otherwise dante won't work.
176 Owner
->Status(_("Logging in"));
179 // Get the remote server's address
180 PeerAddrLen
= sizeof(PeerAddr
);
181 if (getpeername(ServerFd
,(sockaddr
*)&PeerAddr
,&PeerAddrLen
) != 0)
182 return _error
->Errno("getpeername",_("Unable to determine the peer name"));
184 // Get the local machine's address
185 ServerAddrLen
= sizeof(ServerAddr
);
186 if (getsockname(ServerFd
,(sockaddr
*)&ServerAddr
,&ServerAddrLen
) != 0)
187 return _error
->Errno("getsockname",_("Unable to determine the local name"));
192 // FTPConn::Login - Login to the remote server /*{{{*/
193 // ---------------------------------------------------------------------
194 /* This performs both normal login and proxy login using a simples script
195 stored in the config file. */
196 bool FTPConn::Login()
201 // Setup the variables needed for authentication
202 string User
= "anonymous";
203 string Pass
= "apt_get_ftp_2.1@debian.linux.user";
205 // Fill in the user/pass
206 if (ServerName
.User
.empty() == false)
207 User
= ServerName
.User
;
208 if (ServerName
.Password
.empty() == false)
209 Pass
= ServerName
.Password
;
211 // Perform simple login
212 if (Proxy
.empty() == true)
214 // Read the initial response
215 if (ReadResp(Tag
,Msg
) == false)
218 return _error
->Error(_("The server refused the connection and said: %s"),Msg
.c_str());
221 if (WriteMsg(Tag
,Msg
,"USER %s",User
.c_str()) == false)
224 return _error
->Error(_("USER failed, server said: %s"),Msg
.c_str());
226 if (Tag
== 331) { // 331 User name okay, need password.
228 if (WriteMsg(Tag
,Msg
,"PASS %s",Pass
.c_str()) == false)
231 return _error
->Error(_("PASS failed, server said: %s"),Msg
.c_str());
234 // Enter passive mode
235 if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true)
236 TryPassive
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true);
238 TryPassive
= _config
->FindB("Acquire::FTP::Passive",true);
242 // Read the initial response
243 if (ReadResp(Tag
,Msg
) == false)
246 return _error
->Error(_("The server refused the connection and said: %s"),Msg
.c_str());
248 // Perform proxy script execution
249 Configuration::Item
const *Opts
= _config
->Tree("Acquire::ftp::ProxyLogin");
250 if (Opts
== 0 || Opts
->Child
== 0)
251 return _error
->Error(_("A proxy server was specified but no login "
252 "script, Acquire::ftp::ProxyLogin is empty."));
255 // Iterate over the entire login script
256 for (; Opts
!= 0; Opts
= Opts
->Next
)
258 if (Opts
->Value
.empty() == true)
261 // Substitute the variables into the command
262 string Tmp
= Opts
->Value
;
263 Tmp
= SubstVar(Tmp
,"$(PROXY_USER)",Proxy
.User
);
264 Tmp
= SubstVar(Tmp
,"$(PROXY_PASS)",Proxy
.Password
);
265 Tmp
= SubstVar(Tmp
,"$(SITE_USER)",User
);
266 Tmp
= SubstVar(Tmp
,"$(SITE_PASS)",Pass
);
267 if (ServerName
.Port
!= 0)
269 std::string SitePort
;
270 strprintf(SitePort
, "%u", ServerName
.Port
);
271 Tmp
= SubstVar(Tmp
,"$(SITE_PORT)", SitePort
);
274 Tmp
= SubstVar(Tmp
,"$(SITE_PORT)", "21");
275 Tmp
= SubstVar(Tmp
,"$(SITE)",ServerName
.Host
);
278 if (WriteMsg(Tag
,Msg
,"%s",Tmp
.c_str()) == false)
281 return _error
->Error(_("Login script command '%s' failed, server said: %s"),Tmp
.c_str(),Msg
.c_str());
284 // Enter passive mode
286 if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true)
287 TryPassive
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true);
290 if (_config
->Exists("Acquire::FTP::Proxy::Passive") == true)
291 TryPassive
= _config
->FindB("Acquire::FTP::Proxy::Passive",true);
293 TryPassive
= _config
->FindB("Acquire::FTP::Passive",true);
297 // Force the use of extended commands
298 if (_config
->Exists("Acquire::FTP::ForceExtended::" + ServerName
.Host
) == true)
299 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended::" + ServerName
.Host
,true);
301 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended",false);
304 if (WriteMsg(Tag
,Msg
,"TYPE I") == false)
307 return _error
->Error(_("TYPE failed, server said: %s"),Msg
.c_str());
312 // FTPConn::ReadLine - Read a line from the server /*{{{*/
313 // ---------------------------------------------------------------------
314 /* This performs a very simple buffered read. */
315 bool FTPConn::ReadLine(string
&Text
)
321 while (Len
< sizeof(Buffer
))
323 // Scan the buffer for a new line
324 for (unsigned int I
= 0; I
!= Len
; I
++)
326 // Escape some special chars
331 if (Buffer
[I
] != '\n')
335 Text
= string(Buffer
,I
);
336 memmove(Buffer
,Buffer
+I
,Len
- I
);
341 // Wait for some data..
342 if (WaitFd(ServerFd
,false,TimeOut
) == false)
345 return _error
->Error(_("Connection timeout"));
349 int Res
= read(ServerFd
,Buffer
+ Len
,sizeof(Buffer
) - Len
);
351 _error
->Error(_("Server closed the connection"));
354 _error
->Errno("read",_("Read error"));
361 return _error
->Error(_("A response overflowed the buffer."));
364 // FTPConn::ReadResp - Read a full response from the server /*{{{*/
365 // ---------------------------------------------------------------------
366 /* This reads a reply code from the server, it handles both p */
367 bool FTPConn::ReadResp(unsigned int &Ret
,string
&Text
)
369 // Grab the first line of the response
371 if (ReadLine(Msg
) == false)
376 Ret
= strtol(Msg
.c_str(),&End
,10);
377 if (End
- Msg
.c_str() != 3)
378 return _error
->Error(_("Protocol corruption"));
381 Text
= Msg
.c_str()+4;
385 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
390 return _error
->Error(_("Protocol corruption"));
392 /* Okay, here we do the continued message trick. This is foolish, but
393 proftpd follows the protocol as specified and wu-ftpd doesn't, so
394 we filter. I wonder how many clients break if you use proftpd and
395 put a '- in the 3rd spot in the message? */
397 strncpy(Leader
,Msg
.c_str(),3);
399 while (ReadLine(Msg
) == true)
401 // Short, it must be using RFC continuation..
402 if (Msg
.length() < 4)
409 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == ' ')
411 Text
+= Msg
.c_str()+4;
415 // This message has the wu-ftpd style reply code prefixed
416 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == '-')
418 Text
+= Msg
.c_str()+4;
422 // Must be RFC style prefixing
426 if (Debug
== true && _error
->PendingError() == false)
427 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
429 return !_error
->PendingError();
432 // FTPConn::WriteMsg - Send a message to the server /*{{{*/
433 // ---------------------------------------------------------------------
434 /* Simple printf like function.. */
435 bool FTPConn::WriteMsg(unsigned int &Ret
,string
&Text
,const char *Fmt
,...)
440 // sprintf the description
442 vsnprintf(S
,sizeof(S
) - 4,Fmt
,args
);
447 cerr
<< "-> '" << QuoteString(S
,"") << "'" << endl
;
450 unsigned long Len
= strlen(S
);
451 unsigned long Start
= 0;
454 if (WaitFd(ServerFd
,true,TimeOut
) == false)
457 return _error
->Error(_("Connection timeout"));
460 int Res
= write(ServerFd
,S
+ Start
,Len
);
463 _error
->Errno("write",_("Write error"));
472 return ReadResp(Ret
,Text
);
475 // FTPConn::GoPasv - Enter Passive mode /*{{{*/
476 // ---------------------------------------------------------------------
477 /* Try to enter passive mode, the return code does not indicate if passive
478 mode could or could not be established, only if there was a fatal error.
479 We have to enter passive mode every time we make a data connection :| */
480 bool FTPConn::GoPasv()
482 /* The PASV command only works on IPv4 sockets, even though it could
483 in theory suppory IPv6 via an all zeros reply */
484 if (((struct sockaddr
*)&PeerAddr
)->sa_family
!= AF_INET
||
485 ForceExtended
== true)
489 freeaddrinfo(PasvAddr
);
492 // Try to enable pasv mode
495 if (WriteMsg(Tag
,Msg
,"PASV") == false)
498 // Unsupported function
499 string::size_type Pos
= Msg
.find('(');
503 //wu-2.6.2(1) ftp server, returns
504 //227 Entering Passive Mode 193,219,28,140,150,111
505 //without parentheses, let's try to cope with it.
506 //wget(1) and ftp(1) can.
507 if (Pos
== string::npos
)
508 Pos
= Msg
.rfind(' ');
512 // Still unsupported function
513 if (Pos
== string::npos
)
517 unsigned a0
,a1
,a2
,a3
,p0
,p1
;
518 if (sscanf(Msg
.c_str() + Pos
,"%u,%u,%u,%u,%u,%u",&a0
,&a1
,&a2
,&a3
,&p0
,&p1
) != 6)
521 /* Some evil servers return 0 to mean their addr. We can actually speak
522 to these servers natively using IPv6 */
523 if (a0
== 0 && a1
== 0 && a2
== 0 && a3
== 0)
525 // Get the IP in text form
526 char Name
[NI_MAXHOST
];
527 char Service
[NI_MAXSERV
];
528 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
529 Name
,sizeof(Name
),Service
,sizeof(Service
),
530 NI_NUMERICHOST
|NI_NUMERICSERV
);
532 struct addrinfo Hints
;
533 memset(&Hints
,0,sizeof(Hints
));
534 Hints
.ai_socktype
= SOCK_STREAM
;
535 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
536 Hints
.ai_flags
|= AI_NUMERICHOST
;
538 // Get a new passive address.
540 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
541 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
546 struct addrinfo Hints
;
547 memset(&Hints
,0,sizeof(Hints
));
548 Hints
.ai_socktype
= SOCK_STREAM
;
549 Hints
.ai_family
= AF_INET
;
550 Hints
.ai_flags
|= AI_NUMERICHOST
;
552 // Get a new passive address.
554 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
556 snprintf(Name
,sizeof(Name
),"%u.%u.%u.%u",a0
,a1
,a2
,a3
);
557 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
562 // FTPConn::ExtGoPasv - Enter Extended Passive mode /*{{{*/
563 // ---------------------------------------------------------------------
564 /* Try to enter extended passive mode. See GoPasv above and RFC 2428 */
565 bool FTPConn::ExtGoPasv()
568 freeaddrinfo(PasvAddr
);
571 // Try to enable pasv mode
574 if (WriteMsg(Tag
,Msg
,"EPSV") == false)
577 // Unsupported function
578 string::size_type Pos
= Msg
.find('(');
579 if (Tag
>= 400 || Pos
== string::npos
)
583 string::const_iterator List
[4];
586 for (string::const_iterator I
= Msg
.begin() + Pos
; I
< Msg
.end(); ++I
)
598 unsigned long Proto
= 0;
599 unsigned long Port
= 0;
601 IP
= string(List
[1]+1,List
[2]);
602 Port
= atoi(string(List
[2]+1,List
[3]).c_str());
603 if (IP
.empty() == false)
604 Proto
= atoi(string(List
[0]+1,List
[1]).c_str());
609 // String version of the port
611 snprintf(PStr
,sizeof(PStr
),"%lu",Port
);
613 // Get the IP in text form
614 struct addrinfo Hints
;
615 memset(&Hints
,0,sizeof(Hints
));
616 Hints
.ai_socktype
= SOCK_STREAM
;
617 Hints
.ai_flags
|= AI_NUMERICHOST
;
619 /* The RFC defined case, connect to the old IP/protocol using the
621 if (IP
.empty() == true)
623 // Get the IP in text form
624 char Name
[NI_MAXHOST
];
625 char Service
[NI_MAXSERV
];
626 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
627 Name
,sizeof(Name
),Service
,sizeof(Service
),
628 NI_NUMERICHOST
|NI_NUMERICSERV
);
630 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
636 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
637 if (AFMap
[J
].IETFFamily
== Proto
)
638 Hints
.ai_family
= AFMap
[J
].Family
;
639 if (Hints
.ai_family
== 0)
643 // Get a new passive address.
644 if (getaddrinfo(IP
.c_str(),PStr
,&Hints
,&PasvAddr
) != 0)
650 // FTPConn::Size - Return the size of a file /*{{{*/
651 // ---------------------------------------------------------------------
652 /* Grab the file size from the server, 0 means no size or empty file */
653 bool FTPConn::Size(const char *Path
,unsigned long long &Size
)
659 if (WriteMsg(Tag
,Msg
,"SIZE %s",Path
) == false)
663 Size
= strtoull(Msg
.c_str(),&End
,10);
664 if (Tag
>= 400 || End
== Msg
.c_str())
669 // FTPConn::ModTime - Return the modification time of the file /*{{{*/
670 // ---------------------------------------------------------------------
671 /* Like Size no error is returned if the command is not supported. If the
672 command fails then time is set to the current time of day to fool
674 bool FTPConn::ModTime(const char *Path
, time_t &Time
)
678 // Query the mod time
681 if (WriteMsg(Tag
,Msg
,"MDTM %s",Path
) == false)
683 if (Tag
>= 400 || Msg
.empty() == true || isdigit(Msg
[0]) == 0)
687 return FTPMDTMStrToTime(Msg
.c_str(), Time
);
690 // FTPConn::CreateDataFd - Get a data connection /*{{{*/
691 // ---------------------------------------------------------------------
692 /* Create the data connection. Call FinalizeDataFd after this though.. */
693 bool FTPConn::CreateDataFd()
698 // Attempt to enter passive mode.
699 if (TryPassive
== true)
701 if (GoPasv() == false)
704 // Oops, didn't work out, don't bother trying again.
713 if ((DataFd
= socket(PasvAddr
->ai_family
,PasvAddr
->ai_socktype
,
714 PasvAddr
->ai_protocol
)) < 0)
715 return _error
->Errno("socket",_("Could not create a socket"));
717 // Connect to the server
718 SetNonBlock(DataFd
,true);
719 if (connect(DataFd
,PasvAddr
->ai_addr
,PasvAddr
->ai_addrlen
) < 0 &&
720 errno
!= EINPROGRESS
)
721 return _error
->Errno("socket",_("Could not create a socket"));
723 /* This implements a timeout for connect by opening the connection
725 if (WaitFd(DataFd
,true,TimeOut
) == false)
726 return _error
->Error(_("Could not connect data socket, connection timed out"));
728 unsigned int Len
= sizeof(Err
);
729 if (getsockopt(DataFd
,SOL_SOCKET
,SO_ERROR
,&Err
,&Len
) != 0)
730 return _error
->Errno("getsockopt",_("Failed"));
732 return _error
->Error(_("Could not connect passive socket."));
741 // Get the information for a listening socket.
742 struct addrinfo
*BindAddr
= NULL
;
743 struct addrinfo Hints
;
744 memset(&Hints
,0,sizeof(Hints
));
745 Hints
.ai_socktype
= SOCK_STREAM
;
746 Hints
.ai_flags
|= AI_PASSIVE
;
747 Hints
.ai_family
= ((struct sockaddr
*)&ServerAddr
)->sa_family
;
748 if (getaddrinfo(0,"0",&Hints
,&BindAddr
) != 0 || BindAddr
== NULL
)
749 return _error
->Error(_("getaddrinfo was unable to get a listening socket"));
751 // Construct the socket
752 if ((DataListenFd
= socket(BindAddr
->ai_family
,BindAddr
->ai_socktype
,
753 BindAddr
->ai_protocol
)) < 0)
755 freeaddrinfo(BindAddr
);
756 return _error
->Errno("socket",_("Could not create a socket"));
760 if (::bind(DataListenFd
,BindAddr
->ai_addr
,BindAddr
->ai_addrlen
) < 0)
762 freeaddrinfo(BindAddr
);
763 return _error
->Errno("bind",_("Could not bind a socket"));
765 freeaddrinfo(BindAddr
);
766 if (listen(DataListenFd
,1) < 0)
767 return _error
->Errno("listen",_("Could not listen on the socket"));
768 SetNonBlock(DataListenFd
,true);
770 // Determine the name to send to the remote
771 struct sockaddr_storage Addr
;
772 socklen_t AddrLen
= sizeof(Addr
);
773 if (getsockname(DataListenFd
,(sockaddr
*)&Addr
,&AddrLen
) < 0)
774 return _error
->Errno("getsockname",_("Could not determine the socket's name"));
777 // Reverse the address. We need the server address and the data port.
778 char Name
[NI_MAXHOST
];
779 char Service
[NI_MAXSERV
];
780 char Service2
[NI_MAXSERV
];
781 getnameinfo((struct sockaddr
*)&Addr
,AddrLen
,
782 Name
,sizeof(Name
),Service
,sizeof(Service
),
783 NI_NUMERICHOST
|NI_NUMERICSERV
);
784 getnameinfo((struct sockaddr
*)&ServerAddr
,ServerAddrLen
,
785 Name
,sizeof(Name
),Service2
,sizeof(Service2
),
786 NI_NUMERICHOST
|NI_NUMERICSERV
);
788 // Send off an IPv4 address in the old port format
789 if (((struct sockaddr
*)&Addr
)->sa_family
== AF_INET
&&
790 ForceExtended
== false)
792 // Convert the dots in the quad into commas
793 for (char *I
= Name
; *I
!= 0; I
++)
796 unsigned long Port
= atoi(Service
);
798 // Send the port command
801 if (WriteMsg(Tag
,Msg
,"PORT %s,%d,%d",
803 (int)(Port
>> 8) & 0xff, (int)(Port
& 0xff)) == false)
806 return _error
->Error(_("Unable to send PORT command"));
810 // Construct an EPRT command
812 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
813 if (AFMap
[J
].Family
== ((struct sockaddr
*)&Addr
)->sa_family
)
814 Proto
= AFMap
[J
].IETFFamily
;
816 return _error
->Error(_("Unknown address family %u (AF_*)"),
817 ((struct sockaddr
*)&Addr
)->sa_family
);
819 // Send the EPRT command
822 if (WriteMsg(Tag
,Msg
,"EPRT |%u|%s|%s|",Proto
,Name
,Service
) == false)
825 return _error
->Error(_("EPRT failed, server said: %s"),Msg
.c_str());
829 // FTPConn::Finalize - Complete the Data connection /*{{{*/
830 // ---------------------------------------------------------------------
831 /* If the connection is in port mode this waits for the other end to hook
833 bool FTPConn::Finalize()
835 // Passive mode? Do nothing
839 // Close any old socket..
843 // Wait for someone to connect..
844 if (WaitFd(DataListenFd
,false,TimeOut
) == false)
845 return _error
->Error(_("Data socket connect timed out"));
847 // Accept the connection
848 struct sockaddr_in Addr
;
849 socklen_t Len
= sizeof(Addr
);
850 DataFd
= accept(DataListenFd
,(struct sockaddr
*)&Addr
,&Len
);
852 return _error
->Errno("accept",_("Unable to accept connection"));
860 // FTPConn::Get - Get a file /*{{{*/
861 // ---------------------------------------------------------------------
862 /* This opens a data connection, sends REST and RETR and then
863 transfers the file over. */
864 bool FTPConn::Get(const char *Path
,FileFd
&To
,unsigned long long Resume
,
865 Hashes
&Hash
,bool &Missing
, unsigned long long MaximumSize
,
869 if (CreateDataFd() == false)
876 if (WriteMsg(Tag
,Msg
,"REST %u",Resume
) == false)
882 if (To
.Truncate(Resume
) == false)
885 if (To
.Seek(0) == false)
890 if (Hash
.AddFD(To
,Resume
) == false)
892 _error
->Errno("read",_("Problem hashing file"));
897 // Send the get command
898 if (WriteMsg(Tag
,Msg
,"RETR %s",Path
) == false)
905 return _error
->Error(_("Unable to fetch file, server said '%s'"),Msg
.c_str());
908 // Finish off the data connection
909 if (Finalize() == false)
913 unsigned char Buffer
[4096];
916 // Wait for some data..
917 if (WaitFd(DataFd
,false,TimeOut
) == false)
920 return _error
->Error(_("Data socket timed out"));
924 int Res
= read(DataFd
,Buffer
,sizeof(Buffer
));
934 Hash
.Add(Buffer
,Res
);
935 if (To
.Write(Buffer
,Res
) == false)
941 if (MaximumSize
> 0 && To
.Tell() > MaximumSize
)
943 Owner
->SetFailReason("MaximumSizeExceeded");
944 return _error
->Error("Writing more data than expected (%llu > %llu)",
945 To
.Tell(), MaximumSize
);
953 // Read the closing message from the server
954 if (ReadResp(Tag
,Msg
) == false)
957 return _error
->Error(_("Data transfer failed, server said '%s'"),Msg
.c_str());
962 // FtpMethod::FtpMethod - Constructor /*{{{*/
963 // ---------------------------------------------------------------------
965 FtpMethod::FtpMethod() : aptMethod("ftp","1.0",SendConfig
)
967 signal(SIGTERM
,SigTerm
);
968 signal(SIGINT
,SigTerm
);
974 // FtpMethod::SigTerm - Handle a fatal signal /*{{{*/
975 // ---------------------------------------------------------------------
976 /* This closes and timestamps the open file. This is necessary to get
977 resume behavoir on user abort */
978 void FtpMethod::SigTerm(int)
984 struct timeval times
[2];
985 times
[0].tv_sec
= FailTime
;
986 times
[1].tv_sec
= FailTime
;
987 times
[0].tv_usec
= times
[1].tv_usec
= 0;
988 utimes(FailFile
.c_str(), times
);
995 // FtpMethod::Configuration - Handle a configuration message /*{{{*/
996 // ---------------------------------------------------------------------
997 /* We stash the desired pipeline depth */
998 bool FtpMethod::Configuration(string Message
)
1000 if (aptMethod::Configuration(Message
) == false)
1003 TimeOut
= _config
->FindI("Acquire::Ftp::Timeout",TimeOut
);
1008 // FtpMethod::Fetch - Fetch a file /*{{{*/
1009 // ---------------------------------------------------------------------
1010 /* Fetch a single file, called by the base class.. */
1011 bool FtpMethod::Fetch(FetchItem
*Itm
)
1014 const char *File
= Get
.Path
.c_str();
1016 Res
.Filename
= Itm
->DestFile
;
1019 maybe_add_auth (Get
, _config
->FindFile("Dir::Etc::netrc"));
1021 // Connect to the server
1022 if (Server
== 0 || Server
->Comp(Get
) == false)
1025 Server
= new FTPConn(Get
);
1028 // Could not connect is a transient error..
1029 if (Server
->Open(this) == false)
1036 // Get the files information
1038 unsigned long long Size
;
1039 if (Server
->Size(File
,Size
) == false ||
1040 Server
->ModTime(File
,FailTime
) == false)
1047 // See if it is an IMS hit
1048 if (Itm
->LastModified
== FailTime
)
1056 // See if the file exists
1058 if (stat(Itm
->DestFile
.c_str(),&Buf
) == 0)
1060 if (Size
== (unsigned long long)Buf
.st_size
&& FailTime
== Buf
.st_mtime
)
1062 Res
.Size
= Buf
.st_size
;
1063 Res
.LastModified
= Buf
.st_mtime
;
1064 Res
.ResumePoint
= Buf
.st_size
;
1070 if (FailTime
== Buf
.st_mtime
&& Size
> (unsigned long long)Buf
.st_size
)
1071 Res
.ResumePoint
= Buf
.st_size
;
1075 Hashes
Hash(Itm
->ExpectedHashes
);
1077 FileFd
Fd(Itm
->DestFile
,FileFd::WriteAny
);
1078 if (_error
->PendingError() == true)
1083 FailFile
= Itm
->DestFile
;
1084 FailFile
.c_str(); // Make sure we don't do a malloc in the signal handler
1088 if (Server
->Get(File
,Fd
,Res
.ResumePoint
,Hash
,Missing
,Itm
->MaximumSize
,this) == false)
1093 struct timeval times
[2];
1094 times
[0].tv_sec
= FailTime
;
1095 times
[1].tv_sec
= FailTime
;
1096 times
[0].tv_usec
= times
[1].tv_usec
= 0;
1097 utimes(FailFile
.c_str(), times
);
1099 // If the file is missing we hard fail and delete the destfile
1100 // otherwise transient fail
1101 if (Missing
== true) {
1102 RemoveFile("ftp", FailFile
);
1109 Res
.Size
= Fd
.Size();
1112 struct timeval times
[2];
1113 times
[0].tv_sec
= FailTime
;
1114 times
[1].tv_sec
= FailTime
;
1115 times
[0].tv_usec
= times
[1].tv_usec
= 0;
1116 utimes(Fd
.Name().c_str(), times
);
1120 Res
.LastModified
= FailTime
;
1121 Res
.TakeHashes(Hash
);
1129 int main(int, const char *argv
[])
1131 /* See if we should be come the http client - we do this for http
1133 if (getenv("ftp_proxy") != 0)
1135 URI Proxy
= string(getenv("ftp_proxy"));
1137 // Run the HTTP method
1138 if (Proxy
.Access
== "http")
1140 // Copy over the environment setting
1142 snprintf(S
,sizeof(S
),"http_proxy=%s",getenv("ftp_proxy"));
1144 putenv((char *)"no_proxy=");
1146 // Run the http method
1147 string Path
= flNotFile(argv
[0]) + "http";
1148 execl(Path
.c_str(),Path
.c_str(),(char *)NULL
);
1149 cerr
<< _("Unable to invoke ") << Path
<< endl
;
1153 return FtpMethod().Run();