]>
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 /*{{{*/
18 #include <apt-pkg/fileutl.h>
19 #include <apt-pkg/acquire-method.h>
20 #include <apt-pkg/error.h>
21 #include <apt-pkg/hashes.h>
22 #include <apt-pkg/netrc.h>
36 #include <netinet/in.h>
37 #include <sys/socket.h>
38 #include <arpa/inet.h>
41 #include "rfc2553emu.h"
48 /* This table is for the EPRT and EPSV commands, it maps the OS address
49 family to the IETF address families */
53 unsigned long IETFFamily
;
57 struct AFMap AFMap
[] = {{AF_INET
,1},{}};
59 struct AFMap AFMap
[] = {{AF_INET
,1},{AF_INET6
,2},{}};
62 unsigned long TimeOut
= 120;
64 string
FtpMethod::FailFile
;
65 int FtpMethod::FailFd
= -1;
66 time_t FtpMethod::FailTime
= 0;
68 // FTPConn::FTPConn - Constructor /*{{{*/
69 // ---------------------------------------------------------------------
71 FTPConn::FTPConn(URI Srv
) : Len(0), ServerFd(-1), DataFd(-1),
72 DataListenFd(-1), ServerName(Srv
),
73 ForceExtended(false), TryPassive(true)
75 Debug
= _config
->FindB("Debug::Acquire::Ftp",false);
79 // FTPConn::~FTPConn - Destructor /*{{{*/
80 // ---------------------------------------------------------------------
87 // FTPConn::Close - Close down the connection /*{{{*/
88 // ---------------------------------------------------------------------
89 /* Just tear down the socket and data socket */
100 freeaddrinfo(PasvAddr
);
104 // FTPConn::Open - Open a new connection /*{{{*/
105 // ---------------------------------------------------------------------
106 /* Connect to the server using a non-blocking connection and perform a
108 bool FTPConn::Open(pkgAcqMethod
*Owner
)
110 // Use the already open connection if possible.
116 // Determine the proxy setting
117 string SpecificProxy
= _config
->Find("Acquire::ftp::Proxy::" + ServerName
.Host
);
118 if (!SpecificProxy
.empty())
120 if (SpecificProxy
== "DIRECT")
123 Proxy
= SpecificProxy
;
127 string DefProxy
= _config
->Find("Acquire::ftp::Proxy");
128 if (!DefProxy
.empty())
134 char* result
= getenv("ftp_proxy");
135 Proxy
= result
? result
: "";
139 // Parse no_proxy, a , separated list of domains
140 if (getenv("no_proxy") != 0)
142 if (CheckDomainList(ServerName
.Host
,getenv("no_proxy")) == true)
146 // Determine what host and port to use based on the proxy settings
149 if (Proxy
.empty() == true)
151 if (ServerName
.Port
!= 0)
152 Port
= ServerName
.Port
;
153 Host
= ServerName
.Host
;
162 /* Connect to the remote server. Since FTP is connection oriented we
163 want to make sure we get a new server every time we reconnect */
165 if (Connect(Host
,Port
,"ftp",21,ServerFd
,TimeOut
,Owner
) == false)
168 // Login must be before getpeername otherwise dante won't work.
169 Owner
->Status(_("Logging in"));
172 // Get the remote server's address
173 PeerAddrLen
= sizeof(PeerAddr
);
174 if (getpeername(ServerFd
,(sockaddr
*)&PeerAddr
,&PeerAddrLen
) != 0)
175 return _error
->Errno("getpeername",_("Unable to determine the peer name"));
177 // Get the local machine's address
178 ServerAddrLen
= sizeof(ServerAddr
);
179 if (getsockname(ServerFd
,(sockaddr
*)&ServerAddr
,&ServerAddrLen
) != 0)
180 return _error
->Errno("getsockname",_("Unable to determine the local name"));
185 // FTPConn::Login - Login to the remote server /*{{{*/
186 // ---------------------------------------------------------------------
187 /* This performs both normal login and proxy login using a simples script
188 stored in the config file. */
189 bool FTPConn::Login()
194 // Setup the variables needed for authentication
195 string User
= "anonymous";
196 string Pass
= "apt_get_ftp_2.1@debian.linux.user";
198 // Fill in the user/pass
199 if (ServerName
.User
.empty() == false)
200 User
= ServerName
.User
;
201 if (ServerName
.Password
.empty() == false)
202 Pass
= ServerName
.Password
;
204 // Perform simple login
205 if (Proxy
.empty() == true)
207 // Read the initial response
208 if (ReadResp(Tag
,Msg
) == false)
211 return _error
->Error(_("The server refused the connection and said: %s"),Msg
.c_str());
214 if (WriteMsg(Tag
,Msg
,"USER %s",User
.c_str()) == false)
217 return _error
->Error(_("USER failed, server said: %s"),Msg
.c_str());
219 if (Tag
== 331) { // 331 User name okay, need password.
221 if (WriteMsg(Tag
,Msg
,"PASS %s",Pass
.c_str()) == false)
224 return _error
->Error(_("PASS failed, server said: %s"),Msg
.c_str());
227 // Enter passive mode
228 if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true)
229 TryPassive
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true);
231 TryPassive
= _config
->FindB("Acquire::FTP::Passive",true);
235 // Read the initial response
236 if (ReadResp(Tag
,Msg
) == false)
239 return _error
->Error(_("The server refused the connection and said: %s"),Msg
.c_str());
241 // Perform proxy script execution
242 Configuration::Item
const *Opts
= _config
->Tree("Acquire::ftp::ProxyLogin");
243 if (Opts
== 0 || Opts
->Child
== 0)
244 return _error
->Error(_("A proxy server was specified but no login "
245 "script, Acquire::ftp::ProxyLogin is empty."));
248 // Iterate over the entire login script
249 for (; Opts
!= 0; Opts
= Opts
->Next
)
251 if (Opts
->Value
.empty() == true)
254 // Substitute the variables into the command
256 if (ServerName
.Port
!= 0)
257 sprintf(SitePort
,"%u",ServerName
.Port
);
259 strcpy(SitePort
,"21");
260 string Tmp
= Opts
->Value
;
261 Tmp
= SubstVar(Tmp
,"$(PROXY_USER)",Proxy
.User
);
262 Tmp
= SubstVar(Tmp
,"$(PROXY_PASS)",Proxy
.Password
);
263 Tmp
= SubstVar(Tmp
,"$(SITE_USER)",User
);
264 Tmp
= SubstVar(Tmp
,"$(SITE_PASS)",Pass
);
265 Tmp
= SubstVar(Tmp
,"$(SITE_PORT)",SitePort
);
266 Tmp
= SubstVar(Tmp
,"$(SITE)",ServerName
.Host
);
269 if (WriteMsg(Tag
,Msg
,"%s",Tmp
.c_str()) == false)
272 return _error
->Error(_("Login script command '%s' failed, server said: %s"),Tmp
.c_str(),Msg
.c_str());
275 // Enter passive mode
277 if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true)
278 TryPassive
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true);
281 if (_config
->Exists("Acquire::FTP::Proxy::Passive") == true)
282 TryPassive
= _config
->FindB("Acquire::FTP::Proxy::Passive",true);
284 TryPassive
= _config
->FindB("Acquire::FTP::Passive",true);
288 // Force the use of extended commands
289 if (_config
->Exists("Acquire::FTP::ForceExtended::" + ServerName
.Host
) == true)
290 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended::" + ServerName
.Host
,true);
292 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended",false);
295 if (WriteMsg(Tag
,Msg
,"TYPE I") == false)
298 return _error
->Error(_("TYPE failed, server said: %s"),Msg
.c_str());
303 // FTPConn::ReadLine - Read a line from the server /*{{{*/
304 // ---------------------------------------------------------------------
305 /* This performs a very simple buffered read. */
306 bool FTPConn::ReadLine(string
&Text
)
312 while (Len
< sizeof(Buffer
))
314 // Scan the buffer for a new line
315 for (unsigned int I
= 0; I
!= Len
; I
++)
317 // Escape some special chars
322 if (Buffer
[I
] != '\n')
326 Text
= string(Buffer
,I
);
327 memmove(Buffer
,Buffer
+I
,Len
- I
);
332 // Wait for some data..
333 if (WaitFd(ServerFd
,false,TimeOut
) == false)
336 return _error
->Error(_("Connection timeout"));
340 int Res
= read(ServerFd
,Buffer
+ Len
,sizeof(Buffer
) - Len
);
342 _error
->Error(_("Server closed the connection"));
345 _error
->Errno("read",_("Read error"));
352 return _error
->Error(_("A response overflowed the buffer."));
355 // FTPConn::ReadResp - Read a full response from the server /*{{{*/
356 // ---------------------------------------------------------------------
357 /* This reads a reply code from the server, it handles both p */
358 bool FTPConn::ReadResp(unsigned int &Ret
,string
&Text
)
360 // Grab the first line of the response
362 if (ReadLine(Msg
) == false)
367 Ret
= strtol(Msg
.c_str(),&End
,10);
368 if (End
- Msg
.c_str() != 3)
369 return _error
->Error(_("Protocol corruption"));
372 Text
= Msg
.c_str()+4;
376 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
381 return _error
->Error(_("Protocol corruption"));
383 /* Okay, here we do the continued message trick. This is foolish, but
384 proftpd follows the protocol as specified and wu-ftpd doesn't, so
385 we filter. I wonder how many clients break if you use proftpd and
386 put a '- in the 3rd spot in the message? */
388 strncpy(Leader
,Msg
.c_str(),3);
390 while (ReadLine(Msg
) == true)
392 // Short, it must be using RFC continuation..
393 if (Msg
.length() < 4)
400 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == ' ')
402 Text
+= Msg
.c_str()+4;
406 // This message has the wu-ftpd style reply code prefixed
407 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == '-')
409 Text
+= Msg
.c_str()+4;
413 // Must be RFC style prefixing
417 if (Debug
== true && _error
->PendingError() == false)
418 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
420 return !_error
->PendingError();
423 // FTPConn::WriteMsg - Send a message to the server /*{{{*/
424 // ---------------------------------------------------------------------
425 /* Simple printf like function.. */
426 bool FTPConn::WriteMsg(unsigned int &Ret
,string
&Text
,const char *Fmt
,...)
431 // sprintf the description
433 vsnprintf(S
,sizeof(S
) - 4,Fmt
,args
);
437 cerr
<< "-> '" << QuoteString(S
,"") << "'" << endl
;
440 unsigned long Len
= strlen(S
);
441 unsigned long Start
= 0;
444 if (WaitFd(ServerFd
,true,TimeOut
) == false)
447 return _error
->Error(_("Connection timeout"));
450 int Res
= write(ServerFd
,S
+ Start
,Len
);
453 _error
->Errno("write",_("Write error"));
462 return ReadResp(Ret
,Text
);
465 // FTPConn::GoPasv - Enter Passive mode /*{{{*/
466 // ---------------------------------------------------------------------
467 /* Try to enter passive mode, the return code does not indicate if passive
468 mode could or could not be established, only if there was a fatal error.
469 We have to enter passive mode every time we make a data connection :| */
470 bool FTPConn::GoPasv()
472 /* The PASV command only works on IPv4 sockets, even though it could
473 in theory suppory IPv6 via an all zeros reply */
474 if (((struct sockaddr
*)&PeerAddr
)->sa_family
!= AF_INET
||
475 ForceExtended
== true)
479 freeaddrinfo(PasvAddr
);
482 // Try to enable pasv mode
485 if (WriteMsg(Tag
,Msg
,"PASV") == false)
488 // Unsupported function
489 string::size_type Pos
= Msg
.find('(');
490 if (Tag
>= 400 || Pos
== string::npos
)
494 unsigned a0
,a1
,a2
,a3
,p0
,p1
;
495 if (sscanf(Msg
.c_str() + Pos
,"(%u,%u,%u,%u,%u,%u)",&a0
,&a1
,&a2
,&a3
,&p0
,&p1
) != 6)
498 /* Some evil servers return 0 to mean their addr. We can actually speak
499 to these servers natively using IPv6 */
500 if (a0
== 0 && a1
== 0 && a2
== 0 && a3
== 0)
502 // Get the IP in text form
503 char Name
[NI_MAXHOST
];
504 char Service
[NI_MAXSERV
];
505 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
506 Name
,sizeof(Name
),Service
,sizeof(Service
),
507 NI_NUMERICHOST
|NI_NUMERICSERV
);
509 struct addrinfo Hints
;
510 memset(&Hints
,0,sizeof(Hints
));
511 Hints
.ai_socktype
= SOCK_STREAM
;
512 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
513 Hints
.ai_flags
|= AI_NUMERICHOST
;
515 // Get a new passive address.
517 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
518 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
523 struct addrinfo Hints
;
524 memset(&Hints
,0,sizeof(Hints
));
525 Hints
.ai_socktype
= SOCK_STREAM
;
526 Hints
.ai_family
= AF_INET
;
527 Hints
.ai_flags
|= AI_NUMERICHOST
;
529 // Get a new passive address.
531 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
533 snprintf(Name
,sizeof(Name
),"%u.%u.%u.%u",a0
,a1
,a2
,a3
);
534 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
539 // FTPConn::ExtGoPasv - Enter Extended Passive mode /*{{{*/
540 // ---------------------------------------------------------------------
541 /* Try to enter extended passive mode. See GoPasv above and RFC 2428 */
542 bool FTPConn::ExtGoPasv()
545 freeaddrinfo(PasvAddr
);
548 // Try to enable pasv mode
551 if (WriteMsg(Tag
,Msg
,"EPSV") == false)
554 // Unsupported function
555 string::size_type Pos
= Msg
.find('(');
556 if (Tag
>= 400 || Pos
== string::npos
)
560 string::const_iterator List
[4];
563 for (string::const_iterator I
= Msg
.begin() + Pos
; I
< Msg
.end(); ++I
)
575 unsigned long Proto
= 0;
576 unsigned long Port
= 0;
578 IP
= string(List
[1]+1,List
[2]);
579 Port
= atoi(string(List
[2]+1,List
[3]).c_str());
580 if (IP
.empty() == false)
581 Proto
= atoi(string(List
[0]+1,List
[1]).c_str());
586 // String version of the port
588 snprintf(PStr
,sizeof(PStr
),"%lu",Port
);
590 // Get the IP in text form
591 struct addrinfo Hints
;
592 memset(&Hints
,0,sizeof(Hints
));
593 Hints
.ai_socktype
= SOCK_STREAM
;
594 Hints
.ai_flags
|= AI_NUMERICHOST
;
596 /* The RFC defined case, connect to the old IP/protocol using the
598 if (IP
.empty() == true)
600 // Get the IP in text form
601 char Name
[NI_MAXHOST
];
602 char Service
[NI_MAXSERV
];
603 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
604 Name
,sizeof(Name
),Service
,sizeof(Service
),
605 NI_NUMERICHOST
|NI_NUMERICSERV
);
607 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
613 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
614 if (AFMap
[J
].IETFFamily
== Proto
)
615 Hints
.ai_family
= AFMap
[J
].Family
;
616 if (Hints
.ai_family
== 0)
620 // Get a new passive address.
622 if ((Res
= getaddrinfo(IP
.c_str(),PStr
,&Hints
,&PasvAddr
)) != 0)
628 // FTPConn::Size - Return the size of a file /*{{{*/
629 // ---------------------------------------------------------------------
630 /* Grab the file size from the server, 0 means no size or empty file */
631 bool FTPConn::Size(const char *Path
,unsigned long &Size
)
637 if (WriteMsg(Tag
,Msg
,"SIZE %s",Path
) == false)
641 Size
= strtol(Msg
.c_str(),&End
,10);
642 if (Tag
>= 400 || End
== Msg
.c_str())
647 // FTPConn::ModTime - Return the modification time of the file /*{{{*/
648 // ---------------------------------------------------------------------
649 /* Like Size no error is returned if the command is not supported. If the
650 command fails then time is set to the current time of day to fool
652 bool FTPConn::ModTime(const char *Path
, time_t &Time
)
656 // Query the mod time
659 if (WriteMsg(Tag
,Msg
,"MDTM %s",Path
) == false)
661 if (Tag
>= 400 || Msg
.empty() == true || isdigit(Msg
[0]) == 0)
665 return FTPMDTMStrToTime(Msg
.c_str(), Time
);
668 // FTPConn::CreateDataFd - Get a data connection /*{{{*/
669 // ---------------------------------------------------------------------
670 /* Create the data connection. Call FinalizeDataFd after this though.. */
671 bool FTPConn::CreateDataFd()
676 // Attempt to enter passive mode.
677 if (TryPassive
== true)
679 if (GoPasv() == false)
682 // Oops, didn't work out, don't bother trying again.
691 if ((DataFd
= socket(PasvAddr
->ai_family
,PasvAddr
->ai_socktype
,
692 PasvAddr
->ai_protocol
)) < 0)
693 return _error
->Errno("socket",_("Could not create a socket"));
695 // Connect to the server
696 SetNonBlock(DataFd
,true);
697 if (connect(DataFd
,PasvAddr
->ai_addr
,PasvAddr
->ai_addrlen
) < 0 &&
698 errno
!= EINPROGRESS
)
699 return _error
->Errno("socket",_("Could not create a socket"));
701 /* This implements a timeout for connect by opening the connection
703 if (WaitFd(DataFd
,true,TimeOut
) == false)
704 return _error
->Error(_("Could not connect data socket, connection timed out"));
706 unsigned int Len
= sizeof(Err
);
707 if (getsockopt(DataFd
,SOL_SOCKET
,SO_ERROR
,&Err
,&Len
) != 0)
708 return _error
->Errno("getsockopt",_("Failed"));
710 return _error
->Error(_("Could not connect passive socket."));
719 // Get the information for a listening socket.
720 struct addrinfo
*BindAddr
= 0;
721 struct addrinfo Hints
;
722 memset(&Hints
,0,sizeof(Hints
));
723 Hints
.ai_socktype
= SOCK_STREAM
;
724 Hints
.ai_flags
|= AI_PASSIVE
;
725 Hints
.ai_family
= ((struct sockaddr
*)&ServerAddr
)->sa_family
;
727 if ((Res
= getaddrinfo(0,"0",&Hints
,&BindAddr
)) != 0)
728 return _error
->Error(_("getaddrinfo was unable to get a listening socket"));
730 // Construct the socket
731 if ((DataListenFd
= socket(BindAddr
->ai_family
,BindAddr
->ai_socktype
,
732 BindAddr
->ai_protocol
)) < 0)
734 freeaddrinfo(BindAddr
);
735 return _error
->Errno("socket",_("Could not create a socket"));
739 if (bind(DataListenFd
,BindAddr
->ai_addr
,BindAddr
->ai_addrlen
) < 0)
741 freeaddrinfo(BindAddr
);
742 return _error
->Errno("bind",_("Could not bind a socket"));
744 freeaddrinfo(BindAddr
);
745 if (listen(DataListenFd
,1) < 0)
746 return _error
->Errno("listen",_("Could not listen on the socket"));
747 SetNonBlock(DataListenFd
,true);
749 // Determine the name to send to the remote
750 struct sockaddr_storage Addr
;
751 socklen_t AddrLen
= sizeof(Addr
);
752 if (getsockname(DataListenFd
,(sockaddr
*)&Addr
,&AddrLen
) < 0)
753 return _error
->Errno("getsockname",_("Could not determine the socket's name"));
756 // Reverse the address. We need the server address and the data port.
757 char Name
[NI_MAXHOST
];
758 char Service
[NI_MAXSERV
];
759 char Service2
[NI_MAXSERV
];
760 getnameinfo((struct sockaddr
*)&Addr
,AddrLen
,
761 Name
,sizeof(Name
),Service
,sizeof(Service
),
762 NI_NUMERICHOST
|NI_NUMERICSERV
);
763 getnameinfo((struct sockaddr
*)&ServerAddr
,ServerAddrLen
,
764 Name
,sizeof(Name
),Service2
,sizeof(Service2
),
765 NI_NUMERICHOST
|NI_NUMERICSERV
);
767 // Send off an IPv4 address in the old port format
768 if (((struct sockaddr
*)&Addr
)->sa_family
== AF_INET
&&
769 ForceExtended
== false)
771 // Convert the dots in the quad into commas
772 for (char *I
= Name
; *I
!= 0; I
++)
775 unsigned long Port
= atoi(Service
);
777 // Send the port command
780 if (WriteMsg(Tag
,Msg
,"PORT %s,%d,%d",
782 (int)(Port
>> 8) & 0xff, (int)(Port
& 0xff)) == false)
785 return _error
->Error(_("Unable to send PORT command"));
789 // Construct an EPRT command
791 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
792 if (AFMap
[J
].Family
== ((struct sockaddr
*)&Addr
)->sa_family
)
793 Proto
= AFMap
[J
].IETFFamily
;
795 return _error
->Error(_("Unknown address family %u (AF_*)"),
796 ((struct sockaddr
*)&Addr
)->sa_family
);
798 // Send the EPRT command
801 if (WriteMsg(Tag
,Msg
,"EPRT |%u|%s|%s|",Proto
,Name
,Service
) == false)
804 return _error
->Error(_("EPRT failed, server said: %s"),Msg
.c_str());
808 // FTPConn::Finalize - Complete the Data connection /*{{{*/
809 // ---------------------------------------------------------------------
810 /* If the connection is in port mode this waits for the other end to hook
812 bool FTPConn::Finalize()
814 // Passive mode? Do nothing
818 // Close any old socket..
822 // Wait for someone to connect..
823 if (WaitFd(DataListenFd
,false,TimeOut
) == false)
824 return _error
->Error(_("Data socket connect timed out"));
826 // Accept the connection
827 struct sockaddr_in Addr
;
828 socklen_t Len
= sizeof(Addr
);
829 DataFd
= accept(DataListenFd
,(struct sockaddr
*)&Addr
,&Len
);
831 return _error
->Errno("accept",_("Unable to accept connection"));
839 // FTPConn::Get - Get a file /*{{{*/
840 // ---------------------------------------------------------------------
841 /* This opens a data connection, sends REST and RETR and then
842 transfers the file over. */
843 bool FTPConn::Get(const char *Path
,FileFd
&To
,unsigned long Resume
,
844 Hashes
&Hash
,bool &Missing
)
847 if (CreateDataFd() == false)
854 if (WriteMsg(Tag
,Msg
,"REST %u",Resume
) == false)
860 if (To
.Truncate(Resume
) == false)
863 if (To
.Seek(0) == false)
868 if (Hash
.AddFD(To
.Fd(),Resume
) == false)
870 _error
->Errno("read",_("Problem hashing file"));
875 // Send the get command
876 if (WriteMsg(Tag
,Msg
,"RETR %s",Path
) == false)
883 return _error
->Error(_("Unable to fetch file, server said '%s'"),Msg
.c_str());
886 // Finish off the data connection
887 if (Finalize() == false)
891 unsigned char Buffer
[4096];
894 // Wait for some data..
895 if (WaitFd(DataFd
,false,TimeOut
) == false)
898 return _error
->Error(_("Data socket timed out"));
902 int Res
= read(DataFd
,Buffer
,sizeof(Buffer
));
912 Hash
.Add(Buffer
,Res
);
913 if (To
.Write(Buffer
,Res
) == false)
924 // Read the closing message from the server
925 if (ReadResp(Tag
,Msg
) == false)
928 return _error
->Error(_("Data transfer failed, server said '%s'"),Msg
.c_str());
933 // FtpMethod::FtpMethod - Constructor /*{{{*/
934 // ---------------------------------------------------------------------
936 FtpMethod::FtpMethod() : pkgAcqMethod("1.0",SendConfig
)
938 signal(SIGTERM
,SigTerm
);
939 signal(SIGINT
,SigTerm
);
945 // FtpMethod::SigTerm - Handle a fatal signal /*{{{*/
946 // ---------------------------------------------------------------------
947 /* This closes and timestamps the open file. This is neccessary to get
948 resume behavoir on user abort */
949 void FtpMethod::SigTerm(int)
957 UBuf
.actime
= FailTime
;
958 UBuf
.modtime
= FailTime
;
959 utime(FailFile
.c_str(),&UBuf
);
964 // FtpMethod::Configuration - Handle a configuration message /*{{{*/
965 // ---------------------------------------------------------------------
966 /* We stash the desired pipeline depth */
967 bool FtpMethod::Configuration(string Message
)
969 if (pkgAcqMethod::Configuration(Message
) == false)
972 TimeOut
= _config
->FindI("Acquire::Ftp::Timeout",TimeOut
);
976 // FtpMethod::Fetch - Fetch a file /*{{{*/
977 // ---------------------------------------------------------------------
978 /* Fetch a single file, called by the base class.. */
979 bool FtpMethod::Fetch(FetchItem
*Itm
)
982 const char *File
= Get
.Path
.c_str();
984 Res
.Filename
= Itm
->DestFile
;
987 maybe_add_auth (Get
, _config
->FindFile("Dir::Etc::netrc"));
989 // Connect to the server
990 if (Server
== 0 || Server
->Comp(Get
) == false)
993 Server
= new FTPConn(Get
);
996 // Could not connect is a transient error..
997 if (Server
->Open(this) == false)
1004 // Get the files information
1007 if (Server
->Size(File
,Size
) == false ||
1008 Server
->ModTime(File
,FailTime
) == false)
1015 // See if it is an IMS hit
1016 if (Itm
->LastModified
== FailTime
)
1024 // See if the file exists
1026 if (stat(Itm
->DestFile
.c_str(),&Buf
) == 0)
1028 if (Size
== (unsigned)Buf
.st_size
&& FailTime
== Buf
.st_mtime
)
1030 Res
.Size
= Buf
.st_size
;
1031 Res
.LastModified
= Buf
.st_mtime
;
1032 Res
.ResumePoint
= Buf
.st_size
;
1038 if (FailTime
== Buf
.st_mtime
&& Size
> (unsigned)Buf
.st_size
)
1039 Res
.ResumePoint
= Buf
.st_size
;
1045 FileFd
Fd(Itm
->DestFile
,FileFd::WriteAny
);
1046 if (_error
->PendingError() == true)
1051 FailFile
= Itm
->DestFile
;
1052 FailFile
.c_str(); // Make sure we dont do a malloc in the signal handler
1056 if (Server
->Get(File
,Fd
,Res
.ResumePoint
,Hash
,Missing
) == false)
1061 struct utimbuf UBuf
;
1062 UBuf
.actime
= FailTime
;
1063 UBuf
.modtime
= FailTime
;
1064 utime(FailFile
.c_str(),&UBuf
);
1066 // If the file is missing we hard fail and delete the destfile
1067 // otherwise transient fail
1068 if (Missing
== true) {
1069 unlink(FailFile
.c_str());
1076 Res
.Size
= Fd
.Size();
1079 Res
.LastModified
= FailTime
;
1080 Res
.TakeHashes(Hash
);
1083 struct utimbuf UBuf
;
1084 UBuf
.actime
= FailTime
;
1085 UBuf
.modtime
= FailTime
;
1086 utime(Queue
->DestFile
.c_str(),&UBuf
);
1095 int main(int argc
,const char *argv
[])
1097 setlocale(LC_ALL
, "");
1099 /* See if we should be come the http client - we do this for http
1101 if (getenv("ftp_proxy") != 0)
1103 URI Proxy
= string(getenv("ftp_proxy"));
1105 // Run the HTTP method
1106 if (Proxy
.Access
== "http")
1108 // Copy over the environment setting
1110 snprintf(S
,sizeof(S
),"http_proxy=%s",getenv("ftp_proxy"));
1112 putenv((char *)"no_proxy=");
1114 // Run the http method
1115 string Path
= flNotFile(argv
[0]) + "http";
1116 execl(Path
.c_str(),Path
.c_str(),(char *)NULL
);
1117 cerr
<< _("Unable to invoke ") << Path
<< endl
;