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>
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
)
76 Debug
= _config
->FindB("Debug::Acquire::Ftp",false);
80 // FTPConn::~FTPConn - Destructor /*{{{*/
81 // ---------------------------------------------------------------------
88 // FTPConn::Close - Close down the connection /*{{{*/
89 // ---------------------------------------------------------------------
90 /* Just tear down the socket and data socket */
101 freeaddrinfo(PasvAddr
);
105 // FTPConn::Open - Open a new connection /*{{{*/
106 // ---------------------------------------------------------------------
107 /* Connect to the server using a non-blocking connection and perform a
109 bool FTPConn::Open(pkgAcqMethod
*Owner
)
111 // Use the already open connection if possible.
117 // Determine the proxy setting
118 string SpecificProxy
= _config
->Find("Acquire::ftp::Proxy::" + ServerName
.Host
);
119 if (!SpecificProxy
.empty())
121 if (SpecificProxy
== "DIRECT")
124 Proxy
= SpecificProxy
;
128 string DefProxy
= _config
->Find("Acquire::ftp::Proxy");
129 if (!DefProxy
.empty())
135 char* result
= getenv("ftp_proxy");
136 Proxy
= result
? result
: "";
140 // Parse no_proxy, a , separated list of domains
141 if (getenv("no_proxy") != 0)
143 if (CheckDomainList(ServerName
.Host
,getenv("no_proxy")) == true)
147 // Determine what host and port to use based on the proxy settings
150 if (Proxy
.empty() == true)
152 if (ServerName
.Port
!= 0)
153 Port
= ServerName
.Port
;
154 Host
= ServerName
.Host
;
163 /* Connect to the remote server. Since FTP is connection oriented we
164 want to make sure we get a new server every time we reconnect */
166 if (Connect(Host
,Port
,"ftp",21,ServerFd
,TimeOut
,Owner
) == false)
169 // Login must be before getpeername otherwise dante won't work.
170 Owner
->Status(_("Logging in"));
173 // Get the remote server's address
174 PeerAddrLen
= sizeof(PeerAddr
);
175 if (getpeername(ServerFd
,(sockaddr
*)&PeerAddr
,&PeerAddrLen
) != 0)
176 return _error
->Errno("getpeername",_("Unable to determine the peer name"));
178 // Get the local machine's address
179 ServerAddrLen
= sizeof(ServerAddr
);
180 if (getsockname(ServerFd
,(sockaddr
*)&ServerAddr
,&ServerAddrLen
) != 0)
181 return _error
->Errno("getsockname",_("Unable to determine the local name"));
186 // FTPConn::Login - Login to the remote server /*{{{*/
187 // ---------------------------------------------------------------------
188 /* This performs both normal login and proxy login using a simples script
189 stored in the config file. */
190 bool FTPConn::Login()
195 // Setup the variables needed for authentication
196 string User
= "anonymous";
197 string Pass
= "apt_get_ftp_2.1@debian.linux.user";
199 // Fill in the user/pass
200 if (ServerName
.User
.empty() == false)
201 User
= ServerName
.User
;
202 if (ServerName
.Password
.empty() == false)
203 Pass
= ServerName
.Password
;
205 // Perform simple login
206 if (Proxy
.empty() == true)
208 // Read the initial response
209 if (ReadResp(Tag
,Msg
) == false)
212 return _error
->Error(_("The server refused the connection and said: %s"),Msg
.c_str());
215 if (WriteMsg(Tag
,Msg
,"USER %s",User
.c_str()) == false)
218 return _error
->Error(_("USER failed, server said: %s"),Msg
.c_str());
220 if (Tag
== 331) { // 331 User name okay, need password.
222 if (WriteMsg(Tag
,Msg
,"PASS %s",Pass
.c_str()) == false)
225 return _error
->Error(_("PASS failed, server said: %s"),Msg
.c_str());
228 // Enter passive mode
229 if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true)
230 TryPassive
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true);
232 TryPassive
= _config
->FindB("Acquire::FTP::Passive",true);
236 // Read the initial response
237 if (ReadResp(Tag
,Msg
) == false)
240 return _error
->Error(_("The server refused the connection and said: %s"),Msg
.c_str());
242 // Perform proxy script execution
243 Configuration::Item
const *Opts
= _config
->Tree("Acquire::ftp::ProxyLogin");
244 if (Opts
== 0 || Opts
->Child
== 0)
245 return _error
->Error(_("A proxy server was specified but no login "
246 "script, Acquire::ftp::ProxyLogin is empty."));
249 // Iterate over the entire login script
250 for (; Opts
!= 0; Opts
= Opts
->Next
)
252 if (Opts
->Value
.empty() == true)
255 // Substitute the variables into the command
257 if (ServerName
.Port
!= 0)
258 sprintf(SitePort
,"%u",ServerName
.Port
);
260 strcpy(SitePort
,"21");
261 string Tmp
= Opts
->Value
;
262 Tmp
= SubstVar(Tmp
,"$(PROXY_USER)",Proxy
.User
);
263 Tmp
= SubstVar(Tmp
,"$(PROXY_PASS)",Proxy
.Password
);
264 Tmp
= SubstVar(Tmp
,"$(SITE_USER)",User
);
265 Tmp
= SubstVar(Tmp
,"$(SITE_PASS)",Pass
);
266 Tmp
= SubstVar(Tmp
,"$(SITE_PORT)",SitePort
);
267 Tmp
= SubstVar(Tmp
,"$(SITE)",ServerName
.Host
);
270 if (WriteMsg(Tag
,Msg
,"%s",Tmp
.c_str()) == false)
273 return _error
->Error(_("Login script command '%s' failed, server said: %s"),Tmp
.c_str(),Msg
.c_str());
276 // Enter passive mode
278 if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true)
279 TryPassive
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true);
282 if (_config
->Exists("Acquire::FTP::Proxy::Passive") == true)
283 TryPassive
= _config
->FindB("Acquire::FTP::Proxy::Passive",true);
285 TryPassive
= _config
->FindB("Acquire::FTP::Passive",true);
289 // Force the use of extended commands
290 if (_config
->Exists("Acquire::FTP::ForceExtended::" + ServerName
.Host
) == true)
291 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended::" + ServerName
.Host
,true);
293 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended",false);
296 if (WriteMsg(Tag
,Msg
,"TYPE I") == false)
299 return _error
->Error(_("TYPE failed, server said: %s"),Msg
.c_str());
304 // FTPConn::ReadLine - Read a line from the server /*{{{*/
305 // ---------------------------------------------------------------------
306 /* This performs a very simple buffered read. */
307 bool FTPConn::ReadLine(string
&Text
)
313 while (Len
< sizeof(Buffer
))
315 // Scan the buffer for a new line
316 for (unsigned int I
= 0; I
!= Len
; I
++)
318 // Escape some special chars
323 if (Buffer
[I
] != '\n')
327 Text
= string(Buffer
,I
);
328 memmove(Buffer
,Buffer
+I
,Len
- I
);
333 // Wait for some data..
334 if (WaitFd(ServerFd
,false,TimeOut
) == false)
337 return _error
->Error(_("Connection timeout"));
341 int Res
= read(ServerFd
,Buffer
+ Len
,sizeof(Buffer
) - Len
);
343 _error
->Error(_("Server closed the connection"));
346 _error
->Errno("read",_("Read error"));
353 return _error
->Error(_("A response overflowed the buffer."));
356 // FTPConn::ReadResp - Read a full response from the server /*{{{*/
357 // ---------------------------------------------------------------------
358 /* This reads a reply code from the server, it handles both p */
359 bool FTPConn::ReadResp(unsigned int &Ret
,string
&Text
)
361 // Grab the first line of the response
363 if (ReadLine(Msg
) == false)
368 Ret
= strtol(Msg
.c_str(),&End
,10);
369 if (End
- Msg
.c_str() != 3)
370 return _error
->Error(_("Protocol corruption"));
373 Text
= Msg
.c_str()+4;
377 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
382 return _error
->Error(_("Protocol corruption"));
384 /* Okay, here we do the continued message trick. This is foolish, but
385 proftpd follows the protocol as specified and wu-ftpd doesn't, so
386 we filter. I wonder how many clients break if you use proftpd and
387 put a '- in the 3rd spot in the message? */
389 strncpy(Leader
,Msg
.c_str(),3);
391 while (ReadLine(Msg
) == true)
393 // Short, it must be using RFC continuation..
394 if (Msg
.length() < 4)
401 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == ' ')
403 Text
+= Msg
.c_str()+4;
407 // This message has the wu-ftpd style reply code prefixed
408 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == '-')
410 Text
+= Msg
.c_str()+4;
414 // Must be RFC style prefixing
418 if (Debug
== true && _error
->PendingError() == false)
419 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
421 return !_error
->PendingError();
424 // FTPConn::WriteMsg - Send a message to the server /*{{{*/
425 // ---------------------------------------------------------------------
426 /* Simple printf like function.. */
427 bool FTPConn::WriteMsg(unsigned int &Ret
,string
&Text
,const char *Fmt
,...)
432 // sprintf the description
434 vsnprintf(S
,sizeof(S
) - 4,Fmt
,args
);
438 cerr
<< "-> '" << QuoteString(S
,"") << "'" << endl
;
441 unsigned long Len
= strlen(S
);
442 unsigned long Start
= 0;
445 if (WaitFd(ServerFd
,true,TimeOut
) == false)
448 return _error
->Error(_("Connection timeout"));
451 int Res
= write(ServerFd
,S
+ Start
,Len
);
454 _error
->Errno("write",_("Write error"));
463 return ReadResp(Ret
,Text
);
466 // FTPConn::GoPasv - Enter Passive mode /*{{{*/
467 // ---------------------------------------------------------------------
468 /* Try to enter passive mode, the return code does not indicate if passive
469 mode could or could not be established, only if there was a fatal error.
470 We have to enter passive mode every time we make a data connection :| */
471 bool FTPConn::GoPasv()
473 /* The PASV command only works on IPv4 sockets, even though it could
474 in theory suppory IPv6 via an all zeros reply */
475 if (((struct sockaddr
*)&PeerAddr
)->sa_family
!= AF_INET
||
476 ForceExtended
== true)
480 freeaddrinfo(PasvAddr
);
483 // Try to enable pasv mode
486 if (WriteMsg(Tag
,Msg
,"PASV") == false)
489 // Unsupported function
490 string::size_type Pos
= Msg
.find('(');
491 if (Tag
>= 400 || Pos
== string::npos
)
495 unsigned a0
,a1
,a2
,a3
,p0
,p1
;
496 if (sscanf(Msg
.c_str() + Pos
,"(%u,%u,%u,%u,%u,%u)",&a0
,&a1
,&a2
,&a3
,&p0
,&p1
) != 6)
499 /* Some evil servers return 0 to mean their addr. We can actually speak
500 to these servers natively using IPv6 */
501 if (a0
== 0 && a1
== 0 && a2
== 0 && a3
== 0)
503 // Get the IP in text form
504 char Name
[NI_MAXHOST
];
505 char Service
[NI_MAXSERV
];
506 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
507 Name
,sizeof(Name
),Service
,sizeof(Service
),
508 NI_NUMERICHOST
|NI_NUMERICSERV
);
510 struct addrinfo Hints
;
511 memset(&Hints
,0,sizeof(Hints
));
512 Hints
.ai_socktype
= SOCK_STREAM
;
513 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
514 Hints
.ai_flags
|= AI_NUMERICHOST
;
516 // Get a new passive address.
518 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
519 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
524 struct addrinfo Hints
;
525 memset(&Hints
,0,sizeof(Hints
));
526 Hints
.ai_socktype
= SOCK_STREAM
;
527 Hints
.ai_family
= AF_INET
;
528 Hints
.ai_flags
|= AI_NUMERICHOST
;
530 // Get a new passive address.
532 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
534 snprintf(Name
,sizeof(Name
),"%u.%u.%u.%u",a0
,a1
,a2
,a3
);
535 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
540 // FTPConn::ExtGoPasv - Enter Extended Passive mode /*{{{*/
541 // ---------------------------------------------------------------------
542 /* Try to enter extended passive mode. See GoPasv above and RFC 2428 */
543 bool FTPConn::ExtGoPasv()
546 freeaddrinfo(PasvAddr
);
549 // Try to enable pasv mode
552 if (WriteMsg(Tag
,Msg
,"EPSV") == false)
555 // Unsupported function
556 string::size_type Pos
= Msg
.find('(');
557 if (Tag
>= 400 || Pos
== string::npos
)
561 string::const_iterator List
[4];
564 for (string::const_iterator I
= Msg
.begin() + Pos
; I
< Msg
.end(); I
++)
576 unsigned long Proto
= 0;
577 unsigned long Port
= 0;
579 IP
= string(List
[1]+1,List
[2]);
580 Port
= atoi(string(List
[2]+1,List
[3]).c_str());
581 if (IP
.empty() == false)
582 Proto
= atoi(string(List
[0]+1,List
[1]).c_str());
587 // String version of the port
589 snprintf(PStr
,sizeof(PStr
),"%lu",Port
);
591 // Get the IP in text form
592 struct addrinfo Hints
;
593 memset(&Hints
,0,sizeof(Hints
));
594 Hints
.ai_socktype
= SOCK_STREAM
;
595 Hints
.ai_flags
|= AI_NUMERICHOST
;
597 /* The RFC defined case, connect to the old IP/protocol using the
599 if (IP
.empty() == true)
601 // Get the IP in text form
602 char Name
[NI_MAXHOST
];
603 char Service
[NI_MAXSERV
];
604 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
605 Name
,sizeof(Name
),Service
,sizeof(Service
),
606 NI_NUMERICHOST
|NI_NUMERICSERV
);
608 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
614 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
615 if (AFMap
[J
].IETFFamily
== Proto
)
616 Hints
.ai_family
= AFMap
[J
].Family
;
617 if (Hints
.ai_family
== 0)
621 // Get a new passive address.
623 if ((Res
= getaddrinfo(IP
.c_str(),PStr
,&Hints
,&PasvAddr
)) != 0)
629 // FTPConn::Size - Return the size of a file /*{{{*/
630 // ---------------------------------------------------------------------
631 /* Grab the file size from the server, 0 means no size or empty file */
632 bool FTPConn::Size(const char *Path
,unsigned long long &Size
)
638 if (WriteMsg(Tag
,Msg
,"SIZE %s",Path
) == false)
642 Size
= strtoull(Msg
.c_str(),&End
,10);
643 if (Tag
>= 400 || End
== Msg
.c_str())
648 // FTPConn::ModTime - Return the modification time of the file /*{{{*/
649 // ---------------------------------------------------------------------
650 /* Like Size no error is returned if the command is not supported. If the
651 command fails then time is set to the current time of day to fool
653 bool FTPConn::ModTime(const char *Path
, time_t &Time
)
657 // Query the mod time
660 if (WriteMsg(Tag
,Msg
,"MDTM %s",Path
) == false)
662 if (Tag
>= 400 || Msg
.empty() == true || isdigit(Msg
[0]) == 0)
666 return FTPMDTMStrToTime(Msg
.c_str(), Time
);
669 // FTPConn::CreateDataFd - Get a data connection /*{{{*/
670 // ---------------------------------------------------------------------
671 /* Create the data connection. Call FinalizeDataFd after this though.. */
672 bool FTPConn::CreateDataFd()
677 // Attempt to enter passive mode.
678 if (TryPassive
== true)
680 if (GoPasv() == false)
683 // Oops, didn't work out, don't bother trying again.
692 if ((DataFd
= socket(PasvAddr
->ai_family
,PasvAddr
->ai_socktype
,
693 PasvAddr
->ai_protocol
)) < 0)
694 return _error
->Errno("socket",_("Could not create a socket"));
696 // Connect to the server
697 SetNonBlock(DataFd
,true);
698 if (connect(DataFd
,PasvAddr
->ai_addr
,PasvAddr
->ai_addrlen
) < 0 &&
699 errno
!= EINPROGRESS
)
700 return _error
->Errno("socket",_("Could not create a socket"));
702 /* This implements a timeout for connect by opening the connection
704 if (WaitFd(DataFd
,true,TimeOut
) == false)
705 return _error
->Error(_("Could not connect data socket, connection timed out"));
707 unsigned int Len
= sizeof(Err
);
708 if (getsockopt(DataFd
,SOL_SOCKET
,SO_ERROR
,&Err
,&Len
) != 0)
709 return _error
->Errno("getsockopt",_("Failed"));
711 return _error
->Error(_("Could not connect passive socket."));
720 // Get the information for a listening socket.
721 struct addrinfo
*BindAddr
= 0;
722 struct addrinfo Hints
;
723 memset(&Hints
,0,sizeof(Hints
));
724 Hints
.ai_socktype
= SOCK_STREAM
;
725 Hints
.ai_flags
|= AI_PASSIVE
;
726 Hints
.ai_family
= ((struct sockaddr
*)&ServerAddr
)->sa_family
;
728 if ((Res
= getaddrinfo(0,"0",&Hints
,&BindAddr
)) != 0)
729 return _error
->Error(_("getaddrinfo was unable to get a listening socket"));
731 // Construct the socket
732 if ((DataListenFd
= socket(BindAddr
->ai_family
,BindAddr
->ai_socktype
,
733 BindAddr
->ai_protocol
)) < 0)
735 freeaddrinfo(BindAddr
);
736 return _error
->Errno("socket",_("Could not create a socket"));
740 if (bind(DataListenFd
,BindAddr
->ai_addr
,BindAddr
->ai_addrlen
) < 0)
742 freeaddrinfo(BindAddr
);
743 return _error
->Errno("bind",_("Could not bind a socket"));
745 freeaddrinfo(BindAddr
);
746 if (listen(DataListenFd
,1) < 0)
747 return _error
->Errno("listen",_("Could not listen on the socket"));
748 SetNonBlock(DataListenFd
,true);
750 // Determine the name to send to the remote
751 struct sockaddr_storage Addr
;
752 socklen_t AddrLen
= sizeof(Addr
);
753 if (getsockname(DataListenFd
,(sockaddr
*)&Addr
,&AddrLen
) < 0)
754 return _error
->Errno("getsockname",_("Could not determine the socket's name"));
757 // Reverse the address. We need the server address and the data port.
758 char Name
[NI_MAXHOST
];
759 char Service
[NI_MAXSERV
];
760 char Service2
[NI_MAXSERV
];
761 getnameinfo((struct sockaddr
*)&Addr
,AddrLen
,
762 Name
,sizeof(Name
),Service
,sizeof(Service
),
763 NI_NUMERICHOST
|NI_NUMERICSERV
);
764 getnameinfo((struct sockaddr
*)&ServerAddr
,ServerAddrLen
,
765 Name
,sizeof(Name
),Service2
,sizeof(Service2
),
766 NI_NUMERICHOST
|NI_NUMERICSERV
);
768 // Send off an IPv4 address in the old port format
769 if (((struct sockaddr
*)&Addr
)->sa_family
== AF_INET
&&
770 ForceExtended
== false)
772 // Convert the dots in the quad into commas
773 for (char *I
= Name
; *I
!= 0; I
++)
776 unsigned long Port
= atoi(Service
);
778 // Send the port command
781 if (WriteMsg(Tag
,Msg
,"PORT %s,%d,%d",
783 (int)(Port
>> 8) & 0xff, (int)(Port
& 0xff)) == false)
786 return _error
->Error(_("Unable to send PORT command"));
790 // Construct an EPRT command
792 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
793 if (AFMap
[J
].Family
== ((struct sockaddr
*)&Addr
)->sa_family
)
794 Proto
= AFMap
[J
].IETFFamily
;
796 return _error
->Error(_("Unknown address family %u (AF_*)"),
797 ((struct sockaddr
*)&Addr
)->sa_family
);
799 // Send the EPRT command
802 if (WriteMsg(Tag
,Msg
,"EPRT |%u|%s|%s|",Proto
,Name
,Service
) == false)
805 return _error
->Error(_("EPRT failed, server said: %s"),Msg
.c_str());
809 // FTPConn::Finalize - Complete the Data connection /*{{{*/
810 // ---------------------------------------------------------------------
811 /* If the connection is in port mode this waits for the other end to hook
813 bool FTPConn::Finalize()
815 // Passive mode? Do nothing
819 // Close any old socket..
823 // Wait for someone to connect..
824 if (WaitFd(DataListenFd
,false,TimeOut
) == false)
825 return _error
->Error(_("Data socket connect timed out"));
827 // Accept the connection
828 struct sockaddr_in Addr
;
829 socklen_t Len
= sizeof(Addr
);
830 DataFd
= accept(DataListenFd
,(struct sockaddr
*)&Addr
,&Len
);
832 return _error
->Errno("accept",_("Unable to accept connection"));
840 // FTPConn::Get - Get a file /*{{{*/
841 // ---------------------------------------------------------------------
842 /* This opens a data connection, sends REST and RETR and then
843 transfers the file over. */
844 bool FTPConn::Get(const char *Path
,FileFd
&To
,unsigned long long Resume
,
845 Hashes
&Hash
,bool &Missing
)
848 if (CreateDataFd() == false)
855 if (WriteMsg(Tag
,Msg
,"REST %u",Resume
) == false)
861 if (To
.Truncate(Resume
) == false)
864 if (To
.Seek(0) == false)
869 if (Hash
.AddFD(To
.Fd(),Resume
) == false)
871 _error
->Errno("read",_("Problem hashing file"));
876 // Send the get command
877 if (WriteMsg(Tag
,Msg
,"RETR %s",Path
) == false)
884 return _error
->Error(_("Unable to fetch file, server said '%s'"),Msg
.c_str());
887 // Finish off the data connection
888 if (Finalize() == false)
892 unsigned char Buffer
[4096];
895 // Wait for some data..
896 if (WaitFd(DataFd
,false,TimeOut
) == false)
899 return _error
->Error(_("Data socket timed out"));
903 int Res
= read(DataFd
,Buffer
,sizeof(Buffer
));
913 Hash
.Add(Buffer
,Res
);
914 if (To
.Write(Buffer
,Res
) == false)
925 // Read the closing message from the server
926 if (ReadResp(Tag
,Msg
) == false)
929 return _error
->Error(_("Data transfer failed, server said '%s'"),Msg
.c_str());
934 // FtpMethod::FtpMethod - Constructor /*{{{*/
935 // ---------------------------------------------------------------------
937 FtpMethod::FtpMethod() : pkgAcqMethod("1.0",SendConfig
)
939 signal(SIGTERM
,SigTerm
);
940 signal(SIGINT
,SigTerm
);
946 // FtpMethod::SigTerm - Handle a fatal signal /*{{{*/
947 // ---------------------------------------------------------------------
948 /* This closes and timestamps the open file. This is neccessary to get
949 resume behavoir on user abort */
950 void FtpMethod::SigTerm(int)
958 UBuf
.actime
= FailTime
;
959 UBuf
.modtime
= FailTime
;
960 utime(FailFile
.c_str(),&UBuf
);
965 // FtpMethod::Configuration - Handle a configuration message /*{{{*/
966 // ---------------------------------------------------------------------
967 /* We stash the desired pipeline depth */
968 bool FtpMethod::Configuration(string Message
)
970 if (pkgAcqMethod::Configuration(Message
) == false)
973 TimeOut
= _config
->FindI("Acquire::Ftp::Timeout",TimeOut
);
977 // FtpMethod::Fetch - Fetch a file /*{{{*/
978 // ---------------------------------------------------------------------
979 /* Fetch a single file, called by the base class.. */
980 bool FtpMethod::Fetch(FetchItem
*Itm
)
983 const char *File
= Get
.Path
.c_str();
985 Res
.Filename
= Itm
->DestFile
;
988 maybe_add_auth (Get
, _config
->FindFile("Dir::Etc::netrc"));
990 // Connect to the server
991 if (Server
== 0 || Server
->Comp(Get
) == false)
994 Server
= new FTPConn(Get
);
997 // Could not connect is a transient error..
998 if (Server
->Open(this) == false)
1005 // Get the files information
1007 unsigned long long Size
;
1008 if (Server
->Size(File
,Size
) == false ||
1009 Server
->ModTime(File
,FailTime
) == false)
1016 // See if it is an IMS hit
1017 if (Itm
->LastModified
== FailTime
)
1025 // See if the file exists
1027 if (stat(Itm
->DestFile
.c_str(),&Buf
) == 0)
1029 if (Size
== (unsigned long long)Buf
.st_size
&& FailTime
== Buf
.st_mtime
)
1031 Res
.Size
= Buf
.st_size
;
1032 Res
.LastModified
= Buf
.st_mtime
;
1033 Res
.ResumePoint
= Buf
.st_size
;
1039 if (FailTime
== Buf
.st_mtime
&& Size
> (unsigned long long)Buf
.st_size
)
1040 Res
.ResumePoint
= Buf
.st_size
;
1046 FileFd
Fd(Itm
->DestFile
,FileFd::WriteAny
);
1047 if (_error
->PendingError() == true)
1052 FailFile
= Itm
->DestFile
;
1053 FailFile
.c_str(); // Make sure we dont do a malloc in the signal handler
1057 if (Server
->Get(File
,Fd
,Res
.ResumePoint
,Hash
,Missing
) == false)
1062 struct utimbuf UBuf
;
1063 UBuf
.actime
= FailTime
;
1064 UBuf
.modtime
= FailTime
;
1065 utime(FailFile
.c_str(),&UBuf
);
1067 // If the file is missing we hard fail and delete the destfile
1068 // otherwise transient fail
1069 if (Missing
== true) {
1070 unlink(FailFile
.c_str());
1077 Res
.Size
= Fd
.Size();
1080 Res
.LastModified
= FailTime
;
1081 Res
.TakeHashes(Hash
);
1084 struct utimbuf UBuf
;
1085 UBuf
.actime
= FailTime
;
1086 UBuf
.modtime
= FailTime
;
1087 utime(Queue
->DestFile
.c_str(),&UBuf
);
1096 int main(int argc
,const char *argv
[])
1098 setlocale(LC_ALL
, "");
1100 /* See if we should be come the http client - we do this for http
1102 if (getenv("ftp_proxy") != 0)
1104 URI Proxy
= string(getenv("ftp_proxy"));
1106 // Run the HTTP method
1107 if (Proxy
.Access
== "http")
1109 // Copy over the environment setting
1111 snprintf(S
,sizeof(S
),"http_proxy=%s",getenv("ftp_proxy"));
1113 putenv((char *)"no_proxy=");
1115 // Run the http method
1116 string Path
= flNotFile(argv
[0]) + "http";
1117 execl(Path
.c_str(),Path
.c_str(),(char *)NULL
);
1118 cerr
<< _("Unable to invoke ") << Path
<< endl
;