]>
git.saurik.com Git - apt.git/blob - methods/ftp.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: ftp.cc,v 1.31.2.1 2004/01/16 18:58:50 mdz Exp $
4 /* ######################################################################
6 FTP Aquire Method - This is the FTP aquire method for APT.
8 This is a very simple implementation that does not try to optimize
9 at all. Commands are sent syncronously with the FTP server (as the
10 rfc recommends, but it is not really necessary..) and no tricks are
11 done to speed things along.
13 RFC 2428 describes the IPv6 FTP behavior
15 ##################################################################### */
17 // Include Files /*{{{*/
20 #include <apt-pkg/fileutl.h>
21 #include <apt-pkg/acquire-method.h>
22 #include <apt-pkg/error.h>
23 #include <apt-pkg/hashes.h>
24 #include <apt-pkg/netrc.h>
37 #include <netinet/in.h>
38 #include <sys/socket.h>
39 #include <arpa/inet.h>
42 #include "rfc2553emu.h"
50 /* This table is for the EPRT and EPSV commands, it maps the OS address
51 family to the IETF address families */
55 unsigned long IETFFamily
;
59 struct AFMap AFMap
[] = {{AF_INET
,1},{}};
61 struct AFMap AFMap
[] = {{AF_INET
,1},{AF_INET6
,2},{}};
64 unsigned long TimeOut
= 120;
66 string
FtpMethod::FailFile
;
67 int FtpMethod::FailFd
= -1;
68 time_t FtpMethod::FailTime
= 0;
70 // FTPConn::FTPConn - Constructor /*{{{*/
71 // ---------------------------------------------------------------------
73 FTPConn::FTPConn(URI Srv
) : Len(0), ServerFd(-1), DataFd(-1),
74 DataListenFd(-1), ServerName(Srv
),
75 ForceExtended(false), TryPassive(true)
77 Debug
= _config
->FindB("Debug::Acquire::Ftp",false);
81 // FTPConn::~FTPConn - Destructor /*{{{*/
82 // ---------------------------------------------------------------------
89 // FTPConn::Close - Close down the connection /*{{{*/
90 // ---------------------------------------------------------------------
91 /* Just tear down the socket and data socket */
102 freeaddrinfo(PasvAddr
);
106 // FTPConn::Open - Open a new connection /*{{{*/
107 // ---------------------------------------------------------------------
108 /* Connect to the server using a non-blocking connection and perform a
110 bool FTPConn::Open(pkgAcqMethod
*Owner
)
112 // Use the already open connection if possible.
118 // Determine the proxy setting
119 string SpecificProxy
= _config
->Find("Acquire::ftp::Proxy::" + ServerName
.Host
);
120 if (!SpecificProxy
.empty())
122 if (SpecificProxy
== "DIRECT")
125 Proxy
= SpecificProxy
;
129 string DefProxy
= _config
->Find("Acquire::ftp::Proxy");
130 if (!DefProxy
.empty())
136 char* result
= getenv("ftp_proxy");
137 Proxy
= result
? result
: "";
141 // Parse no_proxy, a , separated list of domains
142 if (getenv("no_proxy") != 0)
144 if (CheckDomainList(ServerName
.Host
,getenv("no_proxy")) == true)
148 // Determine what host and port to use based on the proxy settings
151 if (Proxy
.empty() == true)
153 if (ServerName
.Port
!= 0)
154 Port
= ServerName
.Port
;
155 Host
= ServerName
.Host
;
164 /* Connect to the remote server. Since FTP is connection oriented we
165 want to make sure we get a new server every time we reconnect */
167 if (Connect(Host
,Port
,"ftp",21,ServerFd
,TimeOut
,Owner
) == false)
170 // Login must be before getpeername otherwise dante won't work.
171 Owner
->Status(_("Logging in"));
174 // Get the remote server's address
175 PeerAddrLen
= sizeof(PeerAddr
);
176 if (getpeername(ServerFd
,(sockaddr
*)&PeerAddr
,&PeerAddrLen
) != 0)
177 return _error
->Errno("getpeername",_("Unable to determine the peer name"));
179 // Get the local machine's address
180 ServerAddrLen
= sizeof(ServerAddr
);
181 if (getsockname(ServerFd
,(sockaddr
*)&ServerAddr
,&ServerAddrLen
) != 0)
182 return _error
->Errno("getsockname",_("Unable to determine the local name"));
187 // FTPConn::Login - Login to the remote server /*{{{*/
188 // ---------------------------------------------------------------------
189 /* This performs both normal login and proxy login using a simples script
190 stored in the config file. */
191 bool FTPConn::Login()
196 // Setup the variables needed for authentication
197 string User
= "anonymous";
198 string Pass
= "apt_get_ftp_2.1@debian.linux.user";
200 // Fill in the user/pass
201 if (ServerName
.User
.empty() == false)
202 User
= ServerName
.User
;
203 if (ServerName
.Password
.empty() == false)
204 Pass
= ServerName
.Password
;
206 // Perform simple login
207 if (Proxy
.empty() == true)
209 // Read the initial response
210 if (ReadResp(Tag
,Msg
) == false)
213 return _error
->Error(_("The server refused the connection and said: %s"),Msg
.c_str());
216 if (WriteMsg(Tag
,Msg
,"USER %s",User
.c_str()) == false)
219 return _error
->Error(_("USER failed, server said: %s"),Msg
.c_str());
221 if (Tag
== 331) { // 331 User name okay, need password.
223 if (WriteMsg(Tag
,Msg
,"PASS %s",Pass
.c_str()) == false)
226 return _error
->Error(_("PASS failed, server said: %s"),Msg
.c_str());
229 // Enter passive mode
230 if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true)
231 TryPassive
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true);
233 TryPassive
= _config
->FindB("Acquire::FTP::Passive",true);
237 // Read the initial response
238 if (ReadResp(Tag
,Msg
) == false)
241 return _error
->Error(_("The server refused the connection and said: %s"),Msg
.c_str());
243 // Perform proxy script execution
244 Configuration::Item
const *Opts
= _config
->Tree("Acquire::ftp::ProxyLogin");
245 if (Opts
== 0 || Opts
->Child
== 0)
246 return _error
->Error(_("A proxy server was specified but no login "
247 "script, Acquire::ftp::ProxyLogin is empty."));
250 // Iterate over the entire login script
251 for (; Opts
!= 0; Opts
= Opts
->Next
)
253 if (Opts
->Value
.empty() == true)
256 // Substitute the variables into the command
258 if (ServerName
.Port
!= 0)
259 sprintf(SitePort
,"%u",ServerName
.Port
);
261 strcpy(SitePort
,"21");
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 Tmp
= SubstVar(Tmp
,"$(SITE_PORT)",SitePort
);
268 Tmp
= SubstVar(Tmp
,"$(SITE)",ServerName
.Host
);
271 if (WriteMsg(Tag
,Msg
,"%s",Tmp
.c_str()) == false)
274 return _error
->Error(_("Login script command '%s' failed, server said: %s"),Tmp
.c_str(),Msg
.c_str());
277 // Enter passive mode
279 if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true)
280 TryPassive
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true);
283 if (_config
->Exists("Acquire::FTP::Proxy::Passive") == true)
284 TryPassive
= _config
->FindB("Acquire::FTP::Proxy::Passive",true);
286 TryPassive
= _config
->FindB("Acquire::FTP::Passive",true);
290 // Force the use of extended commands
291 if (_config
->Exists("Acquire::FTP::ForceExtended::" + ServerName
.Host
) == true)
292 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended::" + ServerName
.Host
,true);
294 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended",false);
297 if (WriteMsg(Tag
,Msg
,"TYPE I") == false)
300 return _error
->Error(_("TYPE failed, server said: %s"),Msg
.c_str());
305 // FTPConn::ReadLine - Read a line from the server /*{{{*/
306 // ---------------------------------------------------------------------
307 /* This performs a very simple buffered read. */
308 bool FTPConn::ReadLine(string
&Text
)
314 while (Len
< sizeof(Buffer
))
316 // Scan the buffer for a new line
317 for (unsigned int I
= 0; I
!= Len
; I
++)
319 // Escape some special chars
324 if (Buffer
[I
] != '\n')
328 Text
= string(Buffer
,I
);
329 memmove(Buffer
,Buffer
+I
,Len
- I
);
334 // Wait for some data..
335 if (WaitFd(ServerFd
,false,TimeOut
) == false)
338 return _error
->Error(_("Connection timeout"));
342 int Res
= read(ServerFd
,Buffer
+ Len
,sizeof(Buffer
) - Len
);
344 _error
->Error(_("Server closed the connection"));
347 _error
->Errno("read",_("Read error"));
354 return _error
->Error(_("A response overflowed the buffer."));
357 // FTPConn::ReadResp - Read a full response from the server /*{{{*/
358 // ---------------------------------------------------------------------
359 /* This reads a reply code from the server, it handles both p */
360 bool FTPConn::ReadResp(unsigned int &Ret
,string
&Text
)
362 // Grab the first line of the response
364 if (ReadLine(Msg
) == false)
369 Ret
= strtol(Msg
.c_str(),&End
,10);
370 if (End
- Msg
.c_str() != 3)
371 return _error
->Error(_("Protocol corruption"));
374 Text
= Msg
.c_str()+4;
378 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
383 return _error
->Error(_("Protocol corruption"));
385 /* Okay, here we do the continued message trick. This is foolish, but
386 proftpd follows the protocol as specified and wu-ftpd doesn't, so
387 we filter. I wonder how many clients break if you use proftpd and
388 put a '- in the 3rd spot in the message? */
390 strncpy(Leader
,Msg
.c_str(),3);
392 while (ReadLine(Msg
) == true)
394 // Short, it must be using RFC continuation..
395 if (Msg
.length() < 4)
402 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == ' ')
404 Text
+= Msg
.c_str()+4;
408 // This message has the wu-ftpd style reply code prefixed
409 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == '-')
411 Text
+= Msg
.c_str()+4;
415 // Must be RFC style prefixing
419 if (Debug
== true && _error
->PendingError() == false)
420 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
422 return !_error
->PendingError();
425 // FTPConn::WriteMsg - Send a message to the server /*{{{*/
426 // ---------------------------------------------------------------------
427 /* Simple printf like function.. */
428 bool FTPConn::WriteMsg(unsigned int &Ret
,string
&Text
,const char *Fmt
,...)
433 // sprintf the description
435 vsnprintf(S
,sizeof(S
) - 4,Fmt
,args
);
439 cerr
<< "-> '" << QuoteString(S
,"") << "'" << endl
;
442 unsigned long Len
= strlen(S
);
443 unsigned long Start
= 0;
446 if (WaitFd(ServerFd
,true,TimeOut
) == false)
449 return _error
->Error(_("Connection timeout"));
452 int Res
= write(ServerFd
,S
+ Start
,Len
);
455 _error
->Errno("write",_("Write error"));
464 return ReadResp(Ret
,Text
);
467 // FTPConn::GoPasv - Enter Passive mode /*{{{*/
468 // ---------------------------------------------------------------------
469 /* Try to enter passive mode, the return code does not indicate if passive
470 mode could or could not be established, only if there was a fatal error.
471 We have to enter passive mode every time we make a data connection :| */
472 bool FTPConn::GoPasv()
474 /* The PASV command only works on IPv4 sockets, even though it could
475 in theory suppory IPv6 via an all zeros reply */
476 if (((struct sockaddr
*)&PeerAddr
)->sa_family
!= AF_INET
||
477 ForceExtended
== true)
481 freeaddrinfo(PasvAddr
);
484 // Try to enable pasv mode
487 if (WriteMsg(Tag
,Msg
,"PASV") == false)
490 // Unsupported function
491 string::size_type Pos
= Msg
.find('(');
492 if (Tag
>= 400 || Pos
== string::npos
)
496 unsigned a0
,a1
,a2
,a3
,p0
,p1
;
497 if (sscanf(Msg
.c_str() + Pos
,"(%u,%u,%u,%u,%u,%u)",&a0
,&a1
,&a2
,&a3
,&p0
,&p1
) != 6)
500 /* Some evil servers return 0 to mean their addr. We can actually speak
501 to these servers natively using IPv6 */
502 if (a0
== 0 && a1
== 0 && a2
== 0 && a3
== 0)
504 // Get the IP in text form
505 char Name
[NI_MAXHOST
];
506 char Service
[NI_MAXSERV
];
507 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
508 Name
,sizeof(Name
),Service
,sizeof(Service
),
509 NI_NUMERICHOST
|NI_NUMERICSERV
);
511 struct addrinfo Hints
;
512 memset(&Hints
,0,sizeof(Hints
));
513 Hints
.ai_socktype
= SOCK_STREAM
;
514 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
515 Hints
.ai_flags
|= AI_NUMERICHOST
;
517 // Get a new passive address.
519 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
520 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
525 struct addrinfo Hints
;
526 memset(&Hints
,0,sizeof(Hints
));
527 Hints
.ai_socktype
= SOCK_STREAM
;
528 Hints
.ai_family
= AF_INET
;
529 Hints
.ai_flags
|= AI_NUMERICHOST
;
531 // Get a new passive address.
533 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
535 snprintf(Name
,sizeof(Name
),"%u.%u.%u.%u",a0
,a1
,a2
,a3
);
536 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
541 // FTPConn::ExtGoPasv - Enter Extended Passive mode /*{{{*/
542 // ---------------------------------------------------------------------
543 /* Try to enter extended passive mode. See GoPasv above and RFC 2428 */
544 bool FTPConn::ExtGoPasv()
547 freeaddrinfo(PasvAddr
);
550 // Try to enable pasv mode
553 if (WriteMsg(Tag
,Msg
,"EPSV") == false)
556 // Unsupported function
557 string::size_type Pos
= Msg
.find('(');
558 if (Tag
>= 400 || Pos
== string::npos
)
562 string::const_iterator List
[4];
565 for (string::const_iterator I
= Msg
.begin() + Pos
; I
< Msg
.end(); ++I
)
577 unsigned long Proto
= 0;
578 unsigned long Port
= 0;
580 IP
= string(List
[1]+1,List
[2]);
581 Port
= atoi(string(List
[2]+1,List
[3]).c_str());
582 if (IP
.empty() == false)
583 Proto
= atoi(string(List
[0]+1,List
[1]).c_str());
588 // String version of the port
590 snprintf(PStr
,sizeof(PStr
),"%lu",Port
);
592 // Get the IP in text form
593 struct addrinfo Hints
;
594 memset(&Hints
,0,sizeof(Hints
));
595 Hints
.ai_socktype
= SOCK_STREAM
;
596 Hints
.ai_flags
|= AI_NUMERICHOST
;
598 /* The RFC defined case, connect to the old IP/protocol using the
600 if (IP
.empty() == true)
602 // Get the IP in text form
603 char Name
[NI_MAXHOST
];
604 char Service
[NI_MAXSERV
];
605 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
606 Name
,sizeof(Name
),Service
,sizeof(Service
),
607 NI_NUMERICHOST
|NI_NUMERICSERV
);
609 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
615 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
616 if (AFMap
[J
].IETFFamily
== Proto
)
617 Hints
.ai_family
= AFMap
[J
].Family
;
618 if (Hints
.ai_family
== 0)
622 // Get a new passive address.
624 if ((Res
= getaddrinfo(IP
.c_str(),PStr
,&Hints
,&PasvAddr
)) != 0)
630 // FTPConn::Size - Return the size of a file /*{{{*/
631 // ---------------------------------------------------------------------
632 /* Grab the file size from the server, 0 means no size or empty file */
633 bool FTPConn::Size(const char *Path
,unsigned long long &Size
)
639 if (WriteMsg(Tag
,Msg
,"SIZE %s",Path
) == false)
643 Size
= strtoull(Msg
.c_str(),&End
,10);
644 if (Tag
>= 400 || End
== Msg
.c_str())
649 // FTPConn::ModTime - Return the modification time of the file /*{{{*/
650 // ---------------------------------------------------------------------
651 /* Like Size no error is returned if the command is not supported. If the
652 command fails then time is set to the current time of day to fool
654 bool FTPConn::ModTime(const char *Path
, time_t &Time
)
658 // Query the mod time
661 if (WriteMsg(Tag
,Msg
,"MDTM %s",Path
) == false)
663 if (Tag
>= 400 || Msg
.empty() == true || isdigit(Msg
[0]) == 0)
667 return FTPMDTMStrToTime(Msg
.c_str(), Time
);
670 // FTPConn::CreateDataFd - Get a data connection /*{{{*/
671 // ---------------------------------------------------------------------
672 /* Create the data connection. Call FinalizeDataFd after this though.. */
673 bool FTPConn::CreateDataFd()
678 // Attempt to enter passive mode.
679 if (TryPassive
== true)
681 if (GoPasv() == false)
684 // Oops, didn't work out, don't bother trying again.
693 if ((DataFd
= socket(PasvAddr
->ai_family
,PasvAddr
->ai_socktype
,
694 PasvAddr
->ai_protocol
)) < 0)
695 return _error
->Errno("socket",_("Could not create a socket"));
697 // Connect to the server
698 SetNonBlock(DataFd
,true);
699 if (connect(DataFd
,PasvAddr
->ai_addr
,PasvAddr
->ai_addrlen
) < 0 &&
700 errno
!= EINPROGRESS
)
701 return _error
->Errno("socket",_("Could not create a socket"));
703 /* This implements a timeout for connect by opening the connection
705 if (WaitFd(DataFd
,true,TimeOut
) == false)
706 return _error
->Error(_("Could not connect data socket, connection timed out"));
708 unsigned int Len
= sizeof(Err
);
709 if (getsockopt(DataFd
,SOL_SOCKET
,SO_ERROR
,&Err
,&Len
) != 0)
710 return _error
->Errno("getsockopt",_("Failed"));
712 return _error
->Error(_("Could not connect passive socket."));
721 // Get the information for a listening socket.
722 struct addrinfo
*BindAddr
= 0;
723 struct addrinfo Hints
;
724 memset(&Hints
,0,sizeof(Hints
));
725 Hints
.ai_socktype
= SOCK_STREAM
;
726 Hints
.ai_flags
|= AI_PASSIVE
;
727 Hints
.ai_family
= ((struct sockaddr
*)&ServerAddr
)->sa_family
;
729 if ((Res
= getaddrinfo(0,"0",&Hints
,&BindAddr
)) != 0)
730 return _error
->Error(_("getaddrinfo was unable to get a listening socket"));
732 // Construct the socket
733 if ((DataListenFd
= socket(BindAddr
->ai_family
,BindAddr
->ai_socktype
,
734 BindAddr
->ai_protocol
)) < 0)
736 freeaddrinfo(BindAddr
);
737 return _error
->Errno("socket",_("Could not create a socket"));
741 if (bind(DataListenFd
,BindAddr
->ai_addr
,BindAddr
->ai_addrlen
) < 0)
743 freeaddrinfo(BindAddr
);
744 return _error
->Errno("bind",_("Could not bind a socket"));
746 freeaddrinfo(BindAddr
);
747 if (listen(DataListenFd
,1) < 0)
748 return _error
->Errno("listen",_("Could not listen on the socket"));
749 SetNonBlock(DataListenFd
,true);
751 // Determine the name to send to the remote
752 struct sockaddr_storage Addr
;
753 socklen_t AddrLen
= sizeof(Addr
);
754 if (getsockname(DataListenFd
,(sockaddr
*)&Addr
,&AddrLen
) < 0)
755 return _error
->Errno("getsockname",_("Could not determine the socket's name"));
758 // Reverse the address. We need the server address and the data port.
759 char Name
[NI_MAXHOST
];
760 char Service
[NI_MAXSERV
];
761 char Service2
[NI_MAXSERV
];
762 getnameinfo((struct sockaddr
*)&Addr
,AddrLen
,
763 Name
,sizeof(Name
),Service
,sizeof(Service
),
764 NI_NUMERICHOST
|NI_NUMERICSERV
);
765 getnameinfo((struct sockaddr
*)&ServerAddr
,ServerAddrLen
,
766 Name
,sizeof(Name
),Service2
,sizeof(Service2
),
767 NI_NUMERICHOST
|NI_NUMERICSERV
);
769 // Send off an IPv4 address in the old port format
770 if (((struct sockaddr
*)&Addr
)->sa_family
== AF_INET
&&
771 ForceExtended
== false)
773 // Convert the dots in the quad into commas
774 for (char *I
= Name
; *I
!= 0; I
++)
777 unsigned long Port
= atoi(Service
);
779 // Send the port command
782 if (WriteMsg(Tag
,Msg
,"PORT %s,%d,%d",
784 (int)(Port
>> 8) & 0xff, (int)(Port
& 0xff)) == false)
787 return _error
->Error(_("Unable to send PORT command"));
791 // Construct an EPRT command
793 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
794 if (AFMap
[J
].Family
== ((struct sockaddr
*)&Addr
)->sa_family
)
795 Proto
= AFMap
[J
].IETFFamily
;
797 return _error
->Error(_("Unknown address family %u (AF_*)"),
798 ((struct sockaddr
*)&Addr
)->sa_family
);
800 // Send the EPRT command
803 if (WriteMsg(Tag
,Msg
,"EPRT |%u|%s|%s|",Proto
,Name
,Service
) == false)
806 return _error
->Error(_("EPRT failed, server said: %s"),Msg
.c_str());
810 // FTPConn::Finalize - Complete the Data connection /*{{{*/
811 // ---------------------------------------------------------------------
812 /* If the connection is in port mode this waits for the other end to hook
814 bool FTPConn::Finalize()
816 // Passive mode? Do nothing
820 // Close any old socket..
824 // Wait for someone to connect..
825 if (WaitFd(DataListenFd
,false,TimeOut
) == false)
826 return _error
->Error(_("Data socket connect timed out"));
828 // Accept the connection
829 struct sockaddr_in Addr
;
830 socklen_t Len
= sizeof(Addr
);
831 DataFd
= accept(DataListenFd
,(struct sockaddr
*)&Addr
,&Len
);
833 return _error
->Errno("accept",_("Unable to accept connection"));
841 // FTPConn::Get - Get a file /*{{{*/
842 // ---------------------------------------------------------------------
843 /* This opens a data connection, sends REST and RETR and then
844 transfers the file over. */
845 bool FTPConn::Get(const char *Path
,FileFd
&To
,unsigned long long Resume
,
846 Hashes
&Hash
,bool &Missing
)
849 if (CreateDataFd() == false)
856 if (WriteMsg(Tag
,Msg
,"REST %u",Resume
) == false)
862 if (To
.Truncate(Resume
) == false)
865 if (To
.Seek(0) == false)
870 if (Hash
.AddFD(To
.Fd(),Resume
) == false)
872 _error
->Errno("read",_("Problem hashing file"));
877 // Send the get command
878 if (WriteMsg(Tag
,Msg
,"RETR %s",Path
) == false)
885 return _error
->Error(_("Unable to fetch file, server said '%s'"),Msg
.c_str());
888 // Finish off the data connection
889 if (Finalize() == false)
893 unsigned char Buffer
[4096];
896 // Wait for some data..
897 if (WaitFd(DataFd
,false,TimeOut
) == false)
900 return _error
->Error(_("Data socket timed out"));
904 int Res
= read(DataFd
,Buffer
,sizeof(Buffer
));
914 Hash
.Add(Buffer
,Res
);
915 if (To
.Write(Buffer
,Res
) == false)
926 // Read the closing message from the server
927 if (ReadResp(Tag
,Msg
) == false)
930 return _error
->Error(_("Data transfer failed, server said '%s'"),Msg
.c_str());
935 // FtpMethod::FtpMethod - Constructor /*{{{*/
936 // ---------------------------------------------------------------------
938 FtpMethod::FtpMethod() : pkgAcqMethod("1.0",SendConfig
)
940 signal(SIGTERM
,SigTerm
);
941 signal(SIGINT
,SigTerm
);
947 // FtpMethod::SigTerm - Handle a fatal signal /*{{{*/
948 // ---------------------------------------------------------------------
949 /* This closes and timestamps the open file. This is neccessary to get
950 resume behavoir on user abort */
951 void FtpMethod::SigTerm(int)
959 UBuf
.actime
= FailTime
;
960 UBuf
.modtime
= FailTime
;
961 utime(FailFile
.c_str(),&UBuf
);
966 // FtpMethod::Configuration - Handle a configuration message /*{{{*/
967 // ---------------------------------------------------------------------
968 /* We stash the desired pipeline depth */
969 bool FtpMethod::Configuration(string Message
)
971 if (pkgAcqMethod::Configuration(Message
) == false)
974 TimeOut
= _config
->FindI("Acquire::Ftp::Timeout",TimeOut
);
978 // FtpMethod::Fetch - Fetch a file /*{{{*/
979 // ---------------------------------------------------------------------
980 /* Fetch a single file, called by the base class.. */
981 bool FtpMethod::Fetch(FetchItem
*Itm
)
984 const char *File
= Get
.Path
.c_str();
986 Res
.Filename
= Itm
->DestFile
;
989 maybe_add_auth (Get
, _config
->FindFile("Dir::Etc::netrc"));
991 // Connect to the server
992 if (Server
== 0 || Server
->Comp(Get
) == false)
995 Server
= new FTPConn(Get
);
998 // Could not connect is a transient error..
999 if (Server
->Open(this) == false)
1006 // Get the files information
1008 unsigned long long Size
;
1009 if (Server
->Size(File
,Size
) == false ||
1010 Server
->ModTime(File
,FailTime
) == false)
1017 // See if it is an IMS hit
1018 if (Itm
->LastModified
== FailTime
)
1026 // See if the file exists
1028 if (stat(Itm
->DestFile
.c_str(),&Buf
) == 0)
1030 if (Size
== (unsigned long long)Buf
.st_size
&& FailTime
== Buf
.st_mtime
)
1032 Res
.Size
= Buf
.st_size
;
1033 Res
.LastModified
= Buf
.st_mtime
;
1034 Res
.ResumePoint
= Buf
.st_size
;
1040 if (FailTime
== Buf
.st_mtime
&& Size
> (unsigned long long)Buf
.st_size
)
1041 Res
.ResumePoint
= Buf
.st_size
;
1047 FileFd
Fd(Itm
->DestFile
,FileFd::WriteAny
);
1048 if (_error
->PendingError() == true)
1053 FailFile
= Itm
->DestFile
;
1054 FailFile
.c_str(); // Make sure we dont do a malloc in the signal handler
1058 if (Server
->Get(File
,Fd
,Res
.ResumePoint
,Hash
,Missing
) == false)
1063 struct utimbuf UBuf
;
1064 UBuf
.actime
= FailTime
;
1065 UBuf
.modtime
= FailTime
;
1066 utime(FailFile
.c_str(),&UBuf
);
1068 // If the file is missing we hard fail and delete the destfile
1069 // otherwise transient fail
1070 if (Missing
== true) {
1071 unlink(FailFile
.c_str());
1078 Res
.Size
= Fd
.Size();
1081 Res
.LastModified
= FailTime
;
1082 Res
.TakeHashes(Hash
);
1085 struct utimbuf UBuf
;
1086 UBuf
.actime
= FailTime
;
1087 UBuf
.modtime
= FailTime
;
1088 utime(Queue
->DestFile
.c_str(),&UBuf
);
1097 int main(int argc
,const char *argv
[])
1099 setlocale(LC_ALL
, "");
1101 /* See if we should be come the http client - we do this for http
1103 if (getenv("ftp_proxy") != 0)
1105 URI Proxy
= string(getenv("ftp_proxy"));
1107 // Run the HTTP method
1108 if (Proxy
.Access
== "http")
1110 // Copy over the environment setting
1112 snprintf(S
,sizeof(S
),"http_proxy=%s",getenv("ftp_proxy"));
1114 putenv((char *)"no_proxy=");
1116 // Run the http method
1117 string Path
= flNotFile(argv
[0]) + "http";
1118 execl(Path
.c_str(),Path
.c_str(),(char *)NULL
);
1119 cerr
<< _("Unable to invoke ") << Path
<< endl
;