]>
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/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>
26 #include <apt-pkg/strutl.h>
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
45 #include "rfc2553emu.h"
54 /* This table is for the EPRT and EPSV commands, it maps the OS address
55 family to the IETF address families */
59 unsigned long IETFFamily
;
63 struct AFMap AFMap
[] = {{AF_INET
,1},{0, 0}};
65 struct AFMap AFMap
[] = {{AF_INET
,1},{AF_INET6
,2},{0, 0}};
68 unsigned long TimeOut
= 120;
70 string
FtpMethod::FailFile
;
71 int FtpMethod::FailFd
= -1;
72 time_t FtpMethod::FailTime
= 0;
74 // FTPConn::FTPConn - Constructor /*{{{*/
75 // ---------------------------------------------------------------------
77 FTPConn::FTPConn(URI Srv
) : Len(0), ServerFd(-1), DataFd(-1),
78 DataListenFd(-1), ServerName(Srv
),
79 ForceExtended(false), TryPassive(true),
80 PeerAddrLen(0), ServerAddrLen(0)
82 Debug
= _config
->FindB("Debug::Acquire::Ftp",false);
87 // FTPConn::~FTPConn - Destructor /*{{{*/
88 // ---------------------------------------------------------------------
95 // FTPConn::Close - Close down the connection /*{{{*/
96 // ---------------------------------------------------------------------
97 /* Just tear down the socket and data socket */
108 freeaddrinfo(PasvAddr
);
112 // FTPConn::Open - Open a new connection /*{{{*/
113 // ---------------------------------------------------------------------
114 /* Connect to the server using a non-blocking connection and perform a
116 bool FTPConn::Open(pkgAcqMethod
*Owner
)
118 // Use the already open connection if possible.
124 // Determine the proxy setting
125 string SpecificProxy
= _config
->Find("Acquire::ftp::Proxy::" + ServerName
.Host
);
126 if (!SpecificProxy
.empty())
128 if (SpecificProxy
== "DIRECT")
131 Proxy
= SpecificProxy
;
135 string DefProxy
= _config
->Find("Acquire::ftp::Proxy");
136 if (!DefProxy
.empty())
142 char* result
= getenv("ftp_proxy");
143 Proxy
= result
? result
: "";
147 // Parse no_proxy, a , separated list of domains
148 if (getenv("no_proxy") != 0)
150 if (CheckDomainList(ServerName
.Host
,getenv("no_proxy")) == true)
154 // Determine what host and port to use based on the proxy settings
157 if (Proxy
.empty() == true)
159 if (ServerName
.Port
!= 0)
160 Port
= ServerName
.Port
;
161 Host
= ServerName
.Host
;
170 /* Connect to the remote server. Since FTP is connection oriented we
171 want to make sure we get a new server every time we reconnect */
173 if (Connect(Host
,Port
,"ftp",21,ServerFd
,TimeOut
,Owner
) == false)
176 // Login must be before getpeername otherwise dante won't work.
177 Owner
->Status(_("Logging in"));
180 // Get the remote server's address
181 PeerAddrLen
= sizeof(PeerAddr
);
182 if (getpeername(ServerFd
,(sockaddr
*)&PeerAddr
,&PeerAddrLen
) != 0)
183 return _error
->Errno("getpeername",_("Unable to determine the peer name"));
185 // Get the local machine's address
186 ServerAddrLen
= sizeof(ServerAddr
);
187 if (getsockname(ServerFd
,(sockaddr
*)&ServerAddr
,&ServerAddrLen
) != 0)
188 return _error
->Errno("getsockname",_("Unable to determine the local name"));
193 // FTPConn::Login - Login to the remote server /*{{{*/
194 // ---------------------------------------------------------------------
195 /* This performs both normal login and proxy login using a simples script
196 stored in the config file. */
197 bool FTPConn::Login()
202 // Setup the variables needed for authentication
203 string User
= "anonymous";
204 string Pass
= "apt_get_ftp_2.1@debian.linux.user";
206 // Fill in the user/pass
207 if (ServerName
.User
.empty() == false)
208 User
= ServerName
.User
;
209 if (ServerName
.Password
.empty() == false)
210 Pass
= ServerName
.Password
;
212 // Perform simple login
213 if (Proxy
.empty() == true)
215 // Read the initial response
216 if (ReadResp(Tag
,Msg
) == false)
219 return _error
->Error(_("The server refused the connection and said: %s"),Msg
.c_str());
222 if (WriteMsg(Tag
,Msg
,"USER %s",User
.c_str()) == false)
225 return _error
->Error(_("USER failed, server said: %s"),Msg
.c_str());
227 if (Tag
== 331) { // 331 User name okay, need password.
229 if (WriteMsg(Tag
,Msg
,"PASS %s",Pass
.c_str()) == false)
232 return _error
->Error(_("PASS failed, server said: %s"),Msg
.c_str());
235 // Enter passive mode
236 if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true)
237 TryPassive
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true);
239 TryPassive
= _config
->FindB("Acquire::FTP::Passive",true);
243 // Read the initial response
244 if (ReadResp(Tag
,Msg
) == false)
247 return _error
->Error(_("The server refused the connection and said: %s"),Msg
.c_str());
249 // Perform proxy script execution
250 Configuration::Item
const *Opts
= _config
->Tree("Acquire::ftp::ProxyLogin");
251 if (Opts
== 0 || Opts
->Child
== 0)
252 return _error
->Error(_("A proxy server was specified but no login "
253 "script, Acquire::ftp::ProxyLogin is empty."));
256 // Iterate over the entire login script
257 for (; Opts
!= 0; Opts
= Opts
->Next
)
259 if (Opts
->Value
.empty() == true)
262 // Substitute the variables into the command
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 if (ServerName
.Port
!= 0)
270 std::string SitePort
;
271 strprintf(SitePort
, "%u", ServerName
.Port
);
272 Tmp
= SubstVar(Tmp
,"$(SITE_PORT)", SitePort
);
275 Tmp
= SubstVar(Tmp
,"$(SITE_PORT)", "21");
276 Tmp
= SubstVar(Tmp
,"$(SITE)",ServerName
.Host
);
279 if (WriteMsg(Tag
,Msg
,"%s",Tmp
.c_str()) == false)
282 return _error
->Error(_("Login script command '%s' failed, server said: %s"),Tmp
.c_str(),Msg
.c_str());
285 // Enter passive mode
287 if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true)
288 TryPassive
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true);
291 if (_config
->Exists("Acquire::FTP::Proxy::Passive") == true)
292 TryPassive
= _config
->FindB("Acquire::FTP::Proxy::Passive",true);
294 TryPassive
= _config
->FindB("Acquire::FTP::Passive",true);
298 // Force the use of extended commands
299 if (_config
->Exists("Acquire::FTP::ForceExtended::" + ServerName
.Host
) == true)
300 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended::" + ServerName
.Host
,true);
302 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended",false);
305 if (WriteMsg(Tag
,Msg
,"TYPE I") == false)
308 return _error
->Error(_("TYPE failed, server said: %s"),Msg
.c_str());
313 // FTPConn::ReadLine - Read a line from the server /*{{{*/
314 // ---------------------------------------------------------------------
315 /* This performs a very simple buffered read. */
316 bool FTPConn::ReadLine(string
&Text
)
322 while (Len
< sizeof(Buffer
))
324 // Scan the buffer for a new line
325 for (unsigned int I
= 0; I
!= Len
; I
++)
327 // Escape some special chars
332 if (Buffer
[I
] != '\n')
336 Text
= string(Buffer
,I
);
337 memmove(Buffer
,Buffer
+I
,Len
- I
);
342 // Wait for some data..
343 if (WaitFd(ServerFd
,false,TimeOut
) == false)
346 return _error
->Error(_("Connection timeout"));
350 int Res
= read(ServerFd
,Buffer
+ Len
,sizeof(Buffer
) - Len
);
352 _error
->Error(_("Server closed the connection"));
355 _error
->Errno("read",_("Read error"));
362 return _error
->Error(_("A response overflowed the buffer."));
365 // FTPConn::ReadResp - Read a full response from the server /*{{{*/
366 // ---------------------------------------------------------------------
367 /* This reads a reply code from the server, it handles both p */
368 bool FTPConn::ReadResp(unsigned int &Ret
,string
&Text
)
370 // Grab the first line of the response
372 if (ReadLine(Msg
) == false)
377 Ret
= strtol(Msg
.c_str(),&End
,10);
378 if (End
- Msg
.c_str() != 3)
379 return _error
->Error(_("Protocol corruption"));
382 Text
= Msg
.c_str()+4;
386 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
391 return _error
->Error(_("Protocol corruption"));
393 /* Okay, here we do the continued message trick. This is foolish, but
394 proftpd follows the protocol as specified and wu-ftpd doesn't, so
395 we filter. I wonder how many clients break if you use proftpd and
396 put a '- in the 3rd spot in the message? */
398 strncpy(Leader
,Msg
.c_str(),3);
400 while (ReadLine(Msg
) == true)
402 // Short, it must be using RFC continuation..
403 if (Msg
.length() < 4)
410 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == ' ')
412 Text
+= Msg
.c_str()+4;
416 // This message has the wu-ftpd style reply code prefixed
417 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == '-')
419 Text
+= Msg
.c_str()+4;
423 // Must be RFC style prefixing
427 if (Debug
== true && _error
->PendingError() == false)
428 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
430 return !_error
->PendingError();
433 // FTPConn::WriteMsg - Send a message to the server /*{{{*/
434 // ---------------------------------------------------------------------
435 /* Simple printf like function.. */
436 bool FTPConn::WriteMsg(unsigned int &Ret
,string
&Text
,const char *Fmt
,...)
441 // sprintf the description
443 vsnprintf(S
,sizeof(S
) - 4,Fmt
,args
);
448 cerr
<< "-> '" << QuoteString(S
,"") << "'" << endl
;
451 unsigned long Len
= strlen(S
);
452 unsigned long Start
= 0;
455 if (WaitFd(ServerFd
,true,TimeOut
) == false)
458 return _error
->Error(_("Connection timeout"));
461 int Res
= write(ServerFd
,S
+ Start
,Len
);
464 _error
->Errno("write",_("Write error"));
473 return ReadResp(Ret
,Text
);
476 // FTPConn::GoPasv - Enter Passive mode /*{{{*/
477 // ---------------------------------------------------------------------
478 /* Try to enter passive mode, the return code does not indicate if passive
479 mode could or could not be established, only if there was a fatal error.
480 We have to enter passive mode every time we make a data connection :| */
481 bool FTPConn::GoPasv()
483 /* The PASV command only works on IPv4 sockets, even though it could
484 in theory suppory IPv6 via an all zeros reply */
485 if (((struct sockaddr
*)&PeerAddr
)->sa_family
!= AF_INET
||
486 ForceExtended
== true)
490 freeaddrinfo(PasvAddr
);
493 // Try to enable pasv mode
496 if (WriteMsg(Tag
,Msg
,"PASV") == false)
499 // Unsupported function
500 string::size_type Pos
= Msg
.find('(');
504 //wu-2.6.2(1) ftp server, returns
505 //227 Entering Passive Mode 193,219,28,140,150,111
506 //without parentheses, let's try to cope with it.
507 //wget(1) and ftp(1) can.
508 if (Pos
== string::npos
)
509 Pos
= Msg
.rfind(' ');
513 // Still unsupported function
514 if (Pos
== string::npos
)
518 unsigned a0
,a1
,a2
,a3
,p0
,p1
;
519 if (sscanf(Msg
.c_str() + Pos
,"%u,%u,%u,%u,%u,%u",&a0
,&a1
,&a2
,&a3
,&p0
,&p1
) != 6)
522 /* Some evil servers return 0 to mean their addr. We can actually speak
523 to these servers natively using IPv6 */
524 if (a0
== 0 && a1
== 0 && a2
== 0 && a3
== 0)
526 // Get the IP in text form
527 char Name
[NI_MAXHOST
];
528 char Service
[NI_MAXSERV
];
529 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
530 Name
,sizeof(Name
),Service
,sizeof(Service
),
531 NI_NUMERICHOST
|NI_NUMERICSERV
);
533 struct addrinfo Hints
;
534 memset(&Hints
,0,sizeof(Hints
));
535 Hints
.ai_socktype
= SOCK_STREAM
;
536 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
537 Hints
.ai_flags
|= AI_NUMERICHOST
;
539 // Get a new passive address.
541 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
542 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
547 struct addrinfo Hints
;
548 memset(&Hints
,0,sizeof(Hints
));
549 Hints
.ai_socktype
= SOCK_STREAM
;
550 Hints
.ai_family
= AF_INET
;
551 Hints
.ai_flags
|= AI_NUMERICHOST
;
553 // Get a new passive address.
555 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
557 snprintf(Name
,sizeof(Name
),"%u.%u.%u.%u",a0
,a1
,a2
,a3
);
558 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
563 // FTPConn::ExtGoPasv - Enter Extended Passive mode /*{{{*/
564 // ---------------------------------------------------------------------
565 /* Try to enter extended passive mode. See GoPasv above and RFC 2428 */
566 bool FTPConn::ExtGoPasv()
569 freeaddrinfo(PasvAddr
);
572 // Try to enable pasv mode
575 if (WriteMsg(Tag
,Msg
,"EPSV") == false)
578 // Unsupported function
579 string::size_type Pos
= Msg
.find('(');
580 if (Tag
>= 400 || Pos
== string::npos
)
584 string::const_iterator List
[4];
587 for (string::const_iterator I
= Msg
.begin() + Pos
; I
< Msg
.end(); ++I
)
599 unsigned long Proto
= 0;
600 unsigned long Port
= 0;
602 IP
= string(List
[1]+1,List
[2]);
603 Port
= atoi(string(List
[2]+1,List
[3]).c_str());
604 if (IP
.empty() == false)
605 Proto
= atoi(string(List
[0]+1,List
[1]).c_str());
610 // String version of the port
612 snprintf(PStr
,sizeof(PStr
),"%lu",Port
);
614 // Get the IP in text form
615 struct addrinfo Hints
;
616 memset(&Hints
,0,sizeof(Hints
));
617 Hints
.ai_socktype
= SOCK_STREAM
;
618 Hints
.ai_flags
|= AI_NUMERICHOST
;
620 /* The RFC defined case, connect to the old IP/protocol using the
622 if (IP
.empty() == true)
624 // Get the IP in text form
625 char Name
[NI_MAXHOST
];
626 char Service
[NI_MAXSERV
];
627 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
628 Name
,sizeof(Name
),Service
,sizeof(Service
),
629 NI_NUMERICHOST
|NI_NUMERICSERV
);
631 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
637 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
638 if (AFMap
[J
].IETFFamily
== Proto
)
639 Hints
.ai_family
= AFMap
[J
].Family
;
640 if (Hints
.ai_family
== 0)
644 // Get a new passive address.
645 if (getaddrinfo(IP
.c_str(),PStr
,&Hints
,&PasvAddr
) != 0)
651 // FTPConn::Size - Return the size of a file /*{{{*/
652 // ---------------------------------------------------------------------
653 /* Grab the file size from the server, 0 means no size or empty file */
654 bool FTPConn::Size(const char *Path
,unsigned long long &Size
)
660 if (WriteMsg(Tag
,Msg
,"SIZE %s",Path
) == false)
664 Size
= strtoull(Msg
.c_str(),&End
,10);
665 if (Tag
>= 400 || End
== Msg
.c_str())
670 // FTPConn::ModTime - Return the modification time of the file /*{{{*/
671 // ---------------------------------------------------------------------
672 /* Like Size no error is returned if the command is not supported. If the
673 command fails then time is set to the current time of day to fool
675 bool FTPConn::ModTime(const char *Path
, time_t &Time
)
679 // Query the mod time
682 if (WriteMsg(Tag
,Msg
,"MDTM %s",Path
) == false)
684 if (Tag
>= 400 || Msg
.empty() == true || isdigit(Msg
[0]) == 0)
688 return FTPMDTMStrToTime(Msg
.c_str(), Time
);
691 // FTPConn::CreateDataFd - Get a data connection /*{{{*/
692 // ---------------------------------------------------------------------
693 /* Create the data connection. Call FinalizeDataFd after this though.. */
694 bool FTPConn::CreateDataFd()
699 // Attempt to enter passive mode.
700 if (TryPassive
== true)
702 if (GoPasv() == false)
705 // Oops, didn't work out, don't bother trying again.
714 if ((DataFd
= socket(PasvAddr
->ai_family
,PasvAddr
->ai_socktype
,
715 PasvAddr
->ai_protocol
)) < 0)
716 return _error
->Errno("socket",_("Could not create a socket"));
718 // Connect to the server
719 SetNonBlock(DataFd
,true);
720 if (connect(DataFd
,PasvAddr
->ai_addr
,PasvAddr
->ai_addrlen
) < 0 &&
721 errno
!= EINPROGRESS
)
722 return _error
->Errno("socket",_("Could not create a socket"));
724 /* This implements a timeout for connect by opening the connection
726 if (WaitFd(DataFd
,true,TimeOut
) == false)
727 return _error
->Error(_("Could not connect data socket, connection timed out"));
729 unsigned int Len
= sizeof(Err
);
730 if (getsockopt(DataFd
,SOL_SOCKET
,SO_ERROR
,&Err
,&Len
) != 0)
731 return _error
->Errno("getsockopt",_("Failed"));
733 return _error
->Error(_("Could not connect passive socket."));
742 // Get the information for a listening socket.
743 struct addrinfo
*BindAddr
= NULL
;
744 struct addrinfo Hints
;
745 memset(&Hints
,0,sizeof(Hints
));
746 Hints
.ai_socktype
= SOCK_STREAM
;
747 Hints
.ai_flags
|= AI_PASSIVE
;
748 Hints
.ai_family
= ((struct sockaddr
*)&ServerAddr
)->sa_family
;
749 if (getaddrinfo(0,"0",&Hints
,&BindAddr
) != 0 || BindAddr
== NULL
)
750 return _error
->Error(_("getaddrinfo was unable to get a listening socket"));
752 // Construct the socket
753 if ((DataListenFd
= socket(BindAddr
->ai_family
,BindAddr
->ai_socktype
,
754 BindAddr
->ai_protocol
)) < 0)
756 freeaddrinfo(BindAddr
);
757 return _error
->Errno("socket",_("Could not create a socket"));
761 if (::bind(DataListenFd
,BindAddr
->ai_addr
,BindAddr
->ai_addrlen
) < 0)
763 freeaddrinfo(BindAddr
);
764 return _error
->Errno("bind",_("Could not bind a socket"));
766 freeaddrinfo(BindAddr
);
767 if (listen(DataListenFd
,1) < 0)
768 return _error
->Errno("listen",_("Could not listen on the socket"));
769 SetNonBlock(DataListenFd
,true);
771 // Determine the name to send to the remote
772 struct sockaddr_storage Addr
;
773 socklen_t AddrLen
= sizeof(Addr
);
774 if (getsockname(DataListenFd
,(sockaddr
*)&Addr
,&AddrLen
) < 0)
775 return _error
->Errno("getsockname",_("Could not determine the socket's name"));
778 // Reverse the address. We need the server address and the data port.
779 char Name
[NI_MAXHOST
];
780 char Service
[NI_MAXSERV
];
781 char Service2
[NI_MAXSERV
];
782 getnameinfo((struct sockaddr
*)&Addr
,AddrLen
,
783 Name
,sizeof(Name
),Service
,sizeof(Service
),
784 NI_NUMERICHOST
|NI_NUMERICSERV
);
785 getnameinfo((struct sockaddr
*)&ServerAddr
,ServerAddrLen
,
786 Name
,sizeof(Name
),Service2
,sizeof(Service2
),
787 NI_NUMERICHOST
|NI_NUMERICSERV
);
789 // Send off an IPv4 address in the old port format
790 if (((struct sockaddr
*)&Addr
)->sa_family
== AF_INET
&&
791 ForceExtended
== false)
793 // Convert the dots in the quad into commas
794 for (char *I
= Name
; *I
!= 0; I
++)
797 unsigned long Port
= atoi(Service
);
799 // Send the port command
802 if (WriteMsg(Tag
,Msg
,"PORT %s,%d,%d",
804 (int)(Port
>> 8) & 0xff, (int)(Port
& 0xff)) == false)
807 return _error
->Error(_("Unable to send PORT command"));
811 // Construct an EPRT command
813 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
814 if (AFMap
[J
].Family
== ((struct sockaddr
*)&Addr
)->sa_family
)
815 Proto
= AFMap
[J
].IETFFamily
;
817 return _error
->Error(_("Unknown address family %u (AF_*)"),
818 ((struct sockaddr
*)&Addr
)->sa_family
);
820 // Send the EPRT command
823 if (WriteMsg(Tag
,Msg
,"EPRT |%u|%s|%s|",Proto
,Name
,Service
) == false)
826 return _error
->Error(_("EPRT failed, server said: %s"),Msg
.c_str());
830 // FTPConn::Finalize - Complete the Data connection /*{{{*/
831 // ---------------------------------------------------------------------
832 /* If the connection is in port mode this waits for the other end to hook
834 bool FTPConn::Finalize()
836 // Passive mode? Do nothing
840 // Close any old socket..
844 // Wait for someone to connect..
845 if (WaitFd(DataListenFd
,false,TimeOut
) == false)
846 return _error
->Error(_("Data socket connect timed out"));
848 // Accept the connection
849 struct sockaddr_in Addr
;
850 socklen_t Len
= sizeof(Addr
);
851 DataFd
= accept(DataListenFd
,(struct sockaddr
*)&Addr
,&Len
);
853 return _error
->Errno("accept",_("Unable to accept connection"));
861 // FTPConn::Get - Get a file /*{{{*/
862 // ---------------------------------------------------------------------
863 /* This opens a data connection, sends REST and RETR and then
864 transfers the file over. */
865 bool FTPConn::Get(const char *Path
,FileFd
&To
,unsigned long long Resume
,
866 Hashes
&Hash
,bool &Missing
, unsigned long long MaximumSize
,
870 if (CreateDataFd() == false)
877 if (WriteMsg(Tag
,Msg
,"REST %u",Resume
) == false)
883 if (To
.Truncate(Resume
) == false)
886 if (To
.Seek(0) == false)
891 if (Hash
.AddFD(To
,Resume
) == false)
893 _error
->Errno("read",_("Problem hashing file"));
898 // Send the get command
899 if (WriteMsg(Tag
,Msg
,"RETR %s",Path
) == false)
906 return _error
->Error(_("Unable to fetch file, server said '%s'"),Msg
.c_str());
909 // Finish off the data connection
910 if (Finalize() == false)
914 unsigned char Buffer
[4096];
917 // Wait for some data..
918 if (WaitFd(DataFd
,false,TimeOut
) == false)
921 return _error
->Error(_("Data socket timed out"));
925 int Res
= read(DataFd
,Buffer
,sizeof(Buffer
));
935 Hash
.Add(Buffer
,Res
);
936 if (To
.Write(Buffer
,Res
) == false)
942 if (MaximumSize
> 0 && To
.Tell() > MaximumSize
)
944 Owner
->SetFailReason("MaximumSizeExceeded");
945 return _error
->Error("Writing more data than expected (%llu > %llu)",
946 To
.Tell(), MaximumSize
);
954 // Read the closing message from the server
955 if (ReadResp(Tag
,Msg
) == false)
958 return _error
->Error(_("Data transfer failed, server said '%s'"),Msg
.c_str());
963 // FtpMethod::FtpMethod - Constructor /*{{{*/
964 // ---------------------------------------------------------------------
966 FtpMethod::FtpMethod() : aptMethod("ftp","1.0",SendConfig
)
968 signal(SIGTERM
,SigTerm
);
969 signal(SIGINT
,SigTerm
);
975 // FtpMethod::SigTerm - Handle a fatal signal /*{{{*/
976 // ---------------------------------------------------------------------
977 /* This closes and timestamps the open file. This is necessary to get
978 resume behavoir on user abort */
979 void FtpMethod::SigTerm(int)
985 struct timeval times
[2];
986 times
[0].tv_sec
= FailTime
;
987 times
[1].tv_sec
= FailTime
;
988 times
[0].tv_usec
= times
[1].tv_usec
= 0;
989 utimes(FailFile
.c_str(), times
);
996 // FtpMethod::Configuration - Handle a configuration message /*{{{*/
997 // ---------------------------------------------------------------------
998 /* We stash the desired pipeline depth */
999 bool FtpMethod::Configuration(string Message
)
1001 if (aptMethod::Configuration(Message
) == false)
1004 TimeOut
= _config
->FindI("Acquire::Ftp::Timeout",TimeOut
);
1009 // FtpMethod::Fetch - Fetch a file /*{{{*/
1010 // ---------------------------------------------------------------------
1011 /* Fetch a single file, called by the base class.. */
1012 bool FtpMethod::Fetch(FetchItem
*Itm
)
1015 const char *File
= Get
.Path
.c_str();
1017 Res
.Filename
= Itm
->DestFile
;
1020 maybe_add_auth (Get
, _config
->FindFile("Dir::Etc::netrc"));
1022 // Connect to the server
1023 if (Server
== 0 || Server
->Comp(Get
) == false)
1026 Server
= new FTPConn(Get
);
1029 // Could not connect is a transient error..
1030 if (Server
->Open(this) == false)
1037 // Get the files information
1039 unsigned long long Size
;
1040 if (Server
->Size(File
,Size
) == false ||
1041 Server
->ModTime(File
,FailTime
) == false)
1048 // See if it is an IMS hit
1049 if (Itm
->LastModified
== FailTime
)
1057 // See if the file exists
1059 if (stat(Itm
->DestFile
.c_str(),&Buf
) == 0)
1061 if (Size
== (unsigned long long)Buf
.st_size
&& FailTime
== Buf
.st_mtime
)
1063 Res
.Size
= Buf
.st_size
;
1064 Res
.LastModified
= Buf
.st_mtime
;
1065 Res
.ResumePoint
= Buf
.st_size
;
1071 if (FailTime
== Buf
.st_mtime
&& Size
> (unsigned long long)Buf
.st_size
)
1072 Res
.ResumePoint
= Buf
.st_size
;
1076 Hashes
Hash(Itm
->ExpectedHashes
);
1078 FileFd
Fd(Itm
->DestFile
,FileFd::WriteAny
);
1079 if (_error
->PendingError() == true)
1084 FailFile
= Itm
->DestFile
;
1085 FailFile
.c_str(); // Make sure we don't do a malloc in the signal handler
1089 if (Server
->Get(File
,Fd
,Res
.ResumePoint
,Hash
,Missing
,Itm
->MaximumSize
,this) == false)
1094 struct timeval times
[2];
1095 times
[0].tv_sec
= FailTime
;
1096 times
[1].tv_sec
= FailTime
;
1097 times
[0].tv_usec
= times
[1].tv_usec
= 0;
1098 utimes(FailFile
.c_str(), times
);
1100 // If the file is missing we hard fail and delete the destfile
1101 // otherwise transient fail
1102 if (Missing
== true) {
1103 RemoveFile("ftp", FailFile
);
1110 Res
.Size
= Fd
.Size();
1113 struct timeval times
[2];
1114 times
[0].tv_sec
= FailTime
;
1115 times
[1].tv_sec
= FailTime
;
1116 times
[0].tv_usec
= times
[1].tv_usec
= 0;
1117 utimes(Fd
.Name().c_str(), times
);
1121 Res
.LastModified
= FailTime
;
1122 Res
.TakeHashes(Hash
);
1130 int main(int, const char *argv
[])
1132 setlocale(LC_ALL
, "");
1134 /* See if we should be come the http client - we do this for http
1136 if (getenv("ftp_proxy") != 0)
1138 URI Proxy
= string(getenv("ftp_proxy"));
1140 // Run the HTTP method
1141 if (Proxy
.Access
== "http")
1143 // Copy over the environment setting
1145 snprintf(S
,sizeof(S
),"http_proxy=%s",getenv("ftp_proxy"));
1147 putenv((char *)"no_proxy=");
1149 // Run the http method
1150 string Path
= flNotFile(argv
[0]) + "http";
1151 execl(Path
.c_str(),Path
.c_str(),(char *)NULL
);
1152 cerr
<< _("Unable to invoke ") << Path
<< endl
;