]>
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>
25 #include <apt-pkg/configuration.h>
38 #include <netinet/in.h>
39 #include <sys/socket.h>
40 #include <arpa/inet.h>
43 #include "rfc2553emu.h"
51 /* This table is for the EPRT and EPSV commands, it maps the OS address
52 family to the IETF address families */
56 unsigned long IETFFamily
;
60 struct AFMap AFMap
[] = {{AF_INET
,1},{}};
62 struct AFMap AFMap
[] = {{AF_INET
,1},{AF_INET6
,2},{}};
65 unsigned long TimeOut
= 120;
67 string
FtpMethod::FailFile
;
68 int FtpMethod::FailFd
= -1;
69 time_t FtpMethod::FailTime
= 0;
71 // FTPConn::FTPConn - Constructor /*{{{*/
72 // ---------------------------------------------------------------------
74 FTPConn::FTPConn(URI Srv
) : Len(0), ServerFd(-1), DataFd(-1),
75 DataListenFd(-1), ServerName(Srv
),
76 ForceExtended(false), TryPassive(true)
78 Debug
= _config
->FindB("Debug::Acquire::Ftp",false);
83 // FTPConn::~FTPConn - Destructor /*{{{*/
84 // ---------------------------------------------------------------------
91 // FTPConn::Close - Close down the connection /*{{{*/
92 // ---------------------------------------------------------------------
93 /* Just tear down the socket and data socket */
104 freeaddrinfo(PasvAddr
);
108 // FTPConn::Open - Open a new connection /*{{{*/
109 // ---------------------------------------------------------------------
110 /* Connect to the server using a non-blocking connection and perform a
112 bool FTPConn::Open(pkgAcqMethod
*Owner
)
114 // Use the already open connection if possible.
120 // Determine the proxy setting
121 string SpecificProxy
= _config
->Find("Acquire::ftp::Proxy::" + ServerName
.Host
);
122 if (!SpecificProxy
.empty())
124 if (SpecificProxy
== "DIRECT")
127 Proxy
= SpecificProxy
;
131 string DefProxy
= _config
->Find("Acquire::ftp::Proxy");
132 if (!DefProxy
.empty())
138 char* result
= getenv("ftp_proxy");
139 Proxy
= result
? result
: "";
143 // Parse no_proxy, a , separated list of domains
144 if (getenv("no_proxy") != 0)
146 if (CheckDomainList(ServerName
.Host
,getenv("no_proxy")) == true)
150 // Determine what host and port to use based on the proxy settings
153 if (Proxy
.empty() == true)
155 if (ServerName
.Port
!= 0)
156 Port
= ServerName
.Port
;
157 Host
= ServerName
.Host
;
166 /* Connect to the remote server. Since FTP is connection oriented we
167 want to make sure we get a new server every time we reconnect */
169 if (Connect(Host
,Port
,"ftp",21,ServerFd
,TimeOut
,Owner
) == false)
172 // Login must be before getpeername otherwise dante won't work.
173 Owner
->Status(_("Logging in"));
176 // Get the remote server's address
177 PeerAddrLen
= sizeof(PeerAddr
);
178 if (getpeername(ServerFd
,(sockaddr
*)&PeerAddr
,&PeerAddrLen
) != 0)
179 return _error
->Errno("getpeername",_("Unable to determine the peer name"));
181 // Get the local machine's address
182 ServerAddrLen
= sizeof(ServerAddr
);
183 if (getsockname(ServerFd
,(sockaddr
*)&ServerAddr
,&ServerAddrLen
) != 0)
184 return _error
->Errno("getsockname",_("Unable to determine the local name"));
189 // FTPConn::Login - Login to the remote server /*{{{*/
190 // ---------------------------------------------------------------------
191 /* This performs both normal login and proxy login using a simples script
192 stored in the config file. */
193 bool FTPConn::Login()
198 // Setup the variables needed for authentication
199 string User
= "anonymous";
200 string Pass
= "apt_get_ftp_2.1@debian.linux.user";
202 // Fill in the user/pass
203 if (ServerName
.User
.empty() == false)
204 User
= ServerName
.User
;
205 if (ServerName
.Password
.empty() == false)
206 Pass
= ServerName
.Password
;
208 // Perform simple login
209 if (Proxy
.empty() == true)
211 // Read the initial response
212 if (ReadResp(Tag
,Msg
) == false)
215 return _error
->Error(_("The server refused the connection and said: %s"),Msg
.c_str());
218 if (WriteMsg(Tag
,Msg
,"USER %s",User
.c_str()) == false)
221 return _error
->Error(_("USER failed, server said: %s"),Msg
.c_str());
223 if (Tag
== 331) { // 331 User name okay, need password.
225 if (WriteMsg(Tag
,Msg
,"PASS %s",Pass
.c_str()) == false)
228 return _error
->Error(_("PASS failed, server said: %s"),Msg
.c_str());
231 // Enter passive mode
232 if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true)
233 TryPassive
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true);
235 TryPassive
= _config
->FindB("Acquire::FTP::Passive",true);
239 // Read the initial response
240 if (ReadResp(Tag
,Msg
) == false)
243 return _error
->Error(_("The server refused the connection and said: %s"),Msg
.c_str());
245 // Perform proxy script execution
246 Configuration::Item
const *Opts
= _config
->Tree("Acquire::ftp::ProxyLogin");
247 if (Opts
== 0 || Opts
->Child
== 0)
248 return _error
->Error(_("A proxy server was specified but no login "
249 "script, Acquire::ftp::ProxyLogin is empty."));
252 // Iterate over the entire login script
253 for (; Opts
!= 0; Opts
= Opts
->Next
)
255 if (Opts
->Value
.empty() == true)
258 // Substitute the variables into the command
260 if (ServerName
.Port
!= 0)
261 sprintf(SitePort
,"%u",ServerName
.Port
);
263 strcpy(SitePort
,"21");
264 string Tmp
= Opts
->Value
;
265 Tmp
= SubstVar(Tmp
,"$(PROXY_USER)",Proxy
.User
);
266 Tmp
= SubstVar(Tmp
,"$(PROXY_PASS)",Proxy
.Password
);
267 Tmp
= SubstVar(Tmp
,"$(SITE_USER)",User
);
268 Tmp
= SubstVar(Tmp
,"$(SITE_PASS)",Pass
);
269 Tmp
= SubstVar(Tmp
,"$(SITE_PORT)",SitePort
);
270 Tmp
= SubstVar(Tmp
,"$(SITE)",ServerName
.Host
);
273 if (WriteMsg(Tag
,Msg
,"%s",Tmp
.c_str()) == false)
276 return _error
->Error(_("Login script command '%s' failed, server said: %s"),Tmp
.c_str(),Msg
.c_str());
279 // Enter passive mode
281 if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true)
282 TryPassive
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true);
285 if (_config
->Exists("Acquire::FTP::Proxy::Passive") == true)
286 TryPassive
= _config
->FindB("Acquire::FTP::Proxy::Passive",true);
288 TryPassive
= _config
->FindB("Acquire::FTP::Passive",true);
292 // Force the use of extended commands
293 if (_config
->Exists("Acquire::FTP::ForceExtended::" + ServerName
.Host
) == true)
294 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended::" + ServerName
.Host
,true);
296 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended",false);
299 if (WriteMsg(Tag
,Msg
,"TYPE I") == false)
302 return _error
->Error(_("TYPE failed, server said: %s"),Msg
.c_str());
307 // FTPConn::ReadLine - Read a line from the server /*{{{*/
308 // ---------------------------------------------------------------------
309 /* This performs a very simple buffered read. */
310 bool FTPConn::ReadLine(string
&Text
)
316 while (Len
< sizeof(Buffer
))
318 // Scan the buffer for a new line
319 for (unsigned int I
= 0; I
!= Len
; I
++)
321 // Escape some special chars
326 if (Buffer
[I
] != '\n')
330 Text
= string(Buffer
,I
);
331 memmove(Buffer
,Buffer
+I
,Len
- I
);
336 // Wait for some data..
337 if (WaitFd(ServerFd
,false,TimeOut
) == false)
340 return _error
->Error(_("Connection timeout"));
344 int Res
= read(ServerFd
,Buffer
+ Len
,sizeof(Buffer
) - Len
);
346 _error
->Error(_("Server closed the connection"));
349 _error
->Errno("read",_("Read error"));
356 return _error
->Error(_("A response overflowed the buffer."));
359 // FTPConn::ReadResp - Read a full response from the server /*{{{*/
360 // ---------------------------------------------------------------------
361 /* This reads a reply code from the server, it handles both p */
362 bool FTPConn::ReadResp(unsigned int &Ret
,string
&Text
)
364 // Grab the first line of the response
366 if (ReadLine(Msg
) == false)
371 Ret
= strtol(Msg
.c_str(),&End
,10);
372 if (End
- Msg
.c_str() != 3)
373 return _error
->Error(_("Protocol corruption"));
376 Text
= Msg
.c_str()+4;
380 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
385 return _error
->Error(_("Protocol corruption"));
387 /* Okay, here we do the continued message trick. This is foolish, but
388 proftpd follows the protocol as specified and wu-ftpd doesn't, so
389 we filter. I wonder how many clients break if you use proftpd and
390 put a '- in the 3rd spot in the message? */
392 strncpy(Leader
,Msg
.c_str(),3);
394 while (ReadLine(Msg
) == true)
396 // Short, it must be using RFC continuation..
397 if (Msg
.length() < 4)
404 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == ' ')
406 Text
+= Msg
.c_str()+4;
410 // This message has the wu-ftpd style reply code prefixed
411 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == '-')
413 Text
+= Msg
.c_str()+4;
417 // Must be RFC style prefixing
421 if (Debug
== true && _error
->PendingError() == false)
422 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
424 return !_error
->PendingError();
427 // FTPConn::WriteMsg - Send a message to the server /*{{{*/
428 // ---------------------------------------------------------------------
429 /* Simple printf like function.. */
430 bool FTPConn::WriteMsg(unsigned int &Ret
,string
&Text
,const char *Fmt
,...)
435 // sprintf the description
437 vsnprintf(S
,sizeof(S
) - 4,Fmt
,args
);
441 cerr
<< "-> '" << QuoteString(S
,"") << "'" << endl
;
444 unsigned long Len
= strlen(S
);
445 unsigned long Start
= 0;
448 if (WaitFd(ServerFd
,true,TimeOut
) == false)
451 return _error
->Error(_("Connection timeout"));
454 int Res
= write(ServerFd
,S
+ Start
,Len
);
457 _error
->Errno("write",_("Write error"));
466 return ReadResp(Ret
,Text
);
469 // FTPConn::GoPasv - Enter Passive mode /*{{{*/
470 // ---------------------------------------------------------------------
471 /* Try to enter passive mode, the return code does not indicate if passive
472 mode could or could not be established, only if there was a fatal error.
473 We have to enter passive mode every time we make a data connection :| */
474 bool FTPConn::GoPasv()
476 /* The PASV command only works on IPv4 sockets, even though it could
477 in theory suppory IPv6 via an all zeros reply */
478 if (((struct sockaddr
*)&PeerAddr
)->sa_family
!= AF_INET
||
479 ForceExtended
== true)
483 freeaddrinfo(PasvAddr
);
486 // Try to enable pasv mode
489 if (WriteMsg(Tag
,Msg
,"PASV") == false)
492 // Unsupported function
493 string::size_type Pos
= Msg
.find('(');
494 if (Tag
>= 400 || Pos
== string::npos
)
498 unsigned a0
,a1
,a2
,a3
,p0
,p1
;
499 if (sscanf(Msg
.c_str() + Pos
,"(%u,%u,%u,%u,%u,%u)",&a0
,&a1
,&a2
,&a3
,&p0
,&p1
) != 6)
502 /* Some evil servers return 0 to mean their addr. We can actually speak
503 to these servers natively using IPv6 */
504 if (a0
== 0 && a1
== 0 && a2
== 0 && a3
== 0)
506 // Get the IP in text form
507 char Name
[NI_MAXHOST
];
508 char Service
[NI_MAXSERV
];
509 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
510 Name
,sizeof(Name
),Service
,sizeof(Service
),
511 NI_NUMERICHOST
|NI_NUMERICSERV
);
513 struct addrinfo Hints
;
514 memset(&Hints
,0,sizeof(Hints
));
515 Hints
.ai_socktype
= SOCK_STREAM
;
516 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
517 Hints
.ai_flags
|= AI_NUMERICHOST
;
519 // Get a new passive address.
521 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
522 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
527 struct addrinfo Hints
;
528 memset(&Hints
,0,sizeof(Hints
));
529 Hints
.ai_socktype
= SOCK_STREAM
;
530 Hints
.ai_family
= AF_INET
;
531 Hints
.ai_flags
|= AI_NUMERICHOST
;
533 // Get a new passive address.
535 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
537 snprintf(Name
,sizeof(Name
),"%u.%u.%u.%u",a0
,a1
,a2
,a3
);
538 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
543 // FTPConn::ExtGoPasv - Enter Extended Passive mode /*{{{*/
544 // ---------------------------------------------------------------------
545 /* Try to enter extended passive mode. See GoPasv above and RFC 2428 */
546 bool FTPConn::ExtGoPasv()
549 freeaddrinfo(PasvAddr
);
552 // Try to enable pasv mode
555 if (WriteMsg(Tag
,Msg
,"EPSV") == false)
558 // Unsupported function
559 string::size_type Pos
= Msg
.find('(');
560 if (Tag
>= 400 || Pos
== string::npos
)
564 string::const_iterator List
[4];
567 for (string::const_iterator I
= Msg
.begin() + Pos
; I
< Msg
.end(); ++I
)
579 unsigned long Proto
= 0;
580 unsigned long Port
= 0;
582 IP
= string(List
[1]+1,List
[2]);
583 Port
= atoi(string(List
[2]+1,List
[3]).c_str());
584 if (IP
.empty() == false)
585 Proto
= atoi(string(List
[0]+1,List
[1]).c_str());
590 // String version of the port
592 snprintf(PStr
,sizeof(PStr
),"%lu",Port
);
594 // Get the IP in text form
595 struct addrinfo Hints
;
596 memset(&Hints
,0,sizeof(Hints
));
597 Hints
.ai_socktype
= SOCK_STREAM
;
598 Hints
.ai_flags
|= AI_NUMERICHOST
;
600 /* The RFC defined case, connect to the old IP/protocol using the
602 if (IP
.empty() == true)
604 // Get the IP in text form
605 char Name
[NI_MAXHOST
];
606 char Service
[NI_MAXSERV
];
607 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
608 Name
,sizeof(Name
),Service
,sizeof(Service
),
609 NI_NUMERICHOST
|NI_NUMERICSERV
);
611 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
617 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
618 if (AFMap
[J
].IETFFamily
== Proto
)
619 Hints
.ai_family
= AFMap
[J
].Family
;
620 if (Hints
.ai_family
== 0)
624 // Get a new passive address.
626 if ((Res
= getaddrinfo(IP
.c_str(),PStr
,&Hints
,&PasvAddr
)) != 0)
632 // FTPConn::Size - Return the size of a file /*{{{*/
633 // ---------------------------------------------------------------------
634 /* Grab the file size from the server, 0 means no size or empty file */
635 bool FTPConn::Size(const char *Path
,unsigned long long &Size
)
641 if (WriteMsg(Tag
,Msg
,"SIZE %s",Path
) == false)
645 Size
= strtoull(Msg
.c_str(),&End
,10);
646 if (Tag
>= 400 || End
== Msg
.c_str())
651 // FTPConn::ModTime - Return the modification time of the file /*{{{*/
652 // ---------------------------------------------------------------------
653 /* Like Size no error is returned if the command is not supported. If the
654 command fails then time is set to the current time of day to fool
656 bool FTPConn::ModTime(const char *Path
, time_t &Time
)
660 // Query the mod time
663 if (WriteMsg(Tag
,Msg
,"MDTM %s",Path
) == false)
665 if (Tag
>= 400 || Msg
.empty() == true || isdigit(Msg
[0]) == 0)
669 return FTPMDTMStrToTime(Msg
.c_str(), Time
);
672 // FTPConn::CreateDataFd - Get a data connection /*{{{*/
673 // ---------------------------------------------------------------------
674 /* Create the data connection. Call FinalizeDataFd after this though.. */
675 bool FTPConn::CreateDataFd()
680 // Attempt to enter passive mode.
681 if (TryPassive
== true)
683 if (GoPasv() == false)
686 // Oops, didn't work out, don't bother trying again.
695 if ((DataFd
= socket(PasvAddr
->ai_family
,PasvAddr
->ai_socktype
,
696 PasvAddr
->ai_protocol
)) < 0)
697 return _error
->Errno("socket",_("Could not create a socket"));
699 // Connect to the server
700 SetNonBlock(DataFd
,true);
701 if (connect(DataFd
,PasvAddr
->ai_addr
,PasvAddr
->ai_addrlen
) < 0 &&
702 errno
!= EINPROGRESS
)
703 return _error
->Errno("socket",_("Could not create a socket"));
705 /* This implements a timeout for connect by opening the connection
707 if (WaitFd(DataFd
,true,TimeOut
) == false)
708 return _error
->Error(_("Could not connect data socket, connection timed out"));
710 unsigned int Len
= sizeof(Err
);
711 if (getsockopt(DataFd
,SOL_SOCKET
,SO_ERROR
,&Err
,&Len
) != 0)
712 return _error
->Errno("getsockopt",_("Failed"));
714 return _error
->Error(_("Could not connect passive socket."));
723 // Get the information for a listening socket.
724 struct addrinfo
*BindAddr
= 0;
725 struct addrinfo Hints
;
726 memset(&Hints
,0,sizeof(Hints
));
727 Hints
.ai_socktype
= SOCK_STREAM
;
728 Hints
.ai_flags
|= AI_PASSIVE
;
729 Hints
.ai_family
= ((struct sockaddr
*)&ServerAddr
)->sa_family
;
731 if ((Res
= getaddrinfo(0,"0",&Hints
,&BindAddr
)) != 0)
732 return _error
->Error(_("getaddrinfo was unable to get a listening socket"));
734 // Construct the socket
735 if ((DataListenFd
= socket(BindAddr
->ai_family
,BindAddr
->ai_socktype
,
736 BindAddr
->ai_protocol
)) < 0)
738 freeaddrinfo(BindAddr
);
739 return _error
->Errno("socket",_("Could not create a socket"));
743 if (bind(DataListenFd
,BindAddr
->ai_addr
,BindAddr
->ai_addrlen
) < 0)
745 freeaddrinfo(BindAddr
);
746 return _error
->Errno("bind",_("Could not bind a socket"));
748 freeaddrinfo(BindAddr
);
749 if (listen(DataListenFd
,1) < 0)
750 return _error
->Errno("listen",_("Could not listen on the socket"));
751 SetNonBlock(DataListenFd
,true);
753 // Determine the name to send to the remote
754 struct sockaddr_storage Addr
;
755 socklen_t AddrLen
= sizeof(Addr
);
756 if (getsockname(DataListenFd
,(sockaddr
*)&Addr
,&AddrLen
) < 0)
757 return _error
->Errno("getsockname",_("Could not determine the socket's name"));
760 // Reverse the address. We need the server address and the data port.
761 char Name
[NI_MAXHOST
];
762 char Service
[NI_MAXSERV
];
763 char Service2
[NI_MAXSERV
];
764 getnameinfo((struct sockaddr
*)&Addr
,AddrLen
,
765 Name
,sizeof(Name
),Service
,sizeof(Service
),
766 NI_NUMERICHOST
|NI_NUMERICSERV
);
767 getnameinfo((struct sockaddr
*)&ServerAddr
,ServerAddrLen
,
768 Name
,sizeof(Name
),Service2
,sizeof(Service2
),
769 NI_NUMERICHOST
|NI_NUMERICSERV
);
771 // Send off an IPv4 address in the old port format
772 if (((struct sockaddr
*)&Addr
)->sa_family
== AF_INET
&&
773 ForceExtended
== false)
775 // Convert the dots in the quad into commas
776 for (char *I
= Name
; *I
!= 0; I
++)
779 unsigned long Port
= atoi(Service
);
781 // Send the port command
784 if (WriteMsg(Tag
,Msg
,"PORT %s,%d,%d",
786 (int)(Port
>> 8) & 0xff, (int)(Port
& 0xff)) == false)
789 return _error
->Error(_("Unable to send PORT command"));
793 // Construct an EPRT command
795 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
796 if (AFMap
[J
].Family
== ((struct sockaddr
*)&Addr
)->sa_family
)
797 Proto
= AFMap
[J
].IETFFamily
;
799 return _error
->Error(_("Unknown address family %u (AF_*)"),
800 ((struct sockaddr
*)&Addr
)->sa_family
);
802 // Send the EPRT command
805 if (WriteMsg(Tag
,Msg
,"EPRT |%u|%s|%s|",Proto
,Name
,Service
) == false)
808 return _error
->Error(_("EPRT failed, server said: %s"),Msg
.c_str());
812 // FTPConn::Finalize - Complete the Data connection /*{{{*/
813 // ---------------------------------------------------------------------
814 /* If the connection is in port mode this waits for the other end to hook
816 bool FTPConn::Finalize()
818 // Passive mode? Do nothing
822 // Close any old socket..
826 // Wait for someone to connect..
827 if (WaitFd(DataListenFd
,false,TimeOut
) == false)
828 return _error
->Error(_("Data socket connect timed out"));
830 // Accept the connection
831 struct sockaddr_in Addr
;
832 socklen_t Len
= sizeof(Addr
);
833 DataFd
= accept(DataListenFd
,(struct sockaddr
*)&Addr
,&Len
);
835 return _error
->Errno("accept",_("Unable to accept connection"));
843 // FTPConn::Get - Get a file /*{{{*/
844 // ---------------------------------------------------------------------
845 /* This opens a data connection, sends REST and RETR and then
846 transfers the file over. */
847 bool FTPConn::Get(const char *Path
,FileFd
&To
,unsigned long long Resume
,
848 Hashes
&Hash
,bool &Missing
)
851 if (CreateDataFd() == false)
858 if (WriteMsg(Tag
,Msg
,"REST %u",Resume
) == false)
864 if (To
.Truncate(Resume
) == false)
867 if (To
.Seek(0) == false)
872 if (Hash
.AddFD(To
,Resume
) == false)
874 _error
->Errno("read",_("Problem hashing file"));
879 // Send the get command
880 if (WriteMsg(Tag
,Msg
,"RETR %s",Path
) == false)
887 return _error
->Error(_("Unable to fetch file, server said '%s'"),Msg
.c_str());
890 // Finish off the data connection
891 if (Finalize() == false)
895 unsigned char Buffer
[4096];
898 // Wait for some data..
899 if (WaitFd(DataFd
,false,TimeOut
) == false)
902 return _error
->Error(_("Data socket timed out"));
906 int Res
= read(DataFd
,Buffer
,sizeof(Buffer
));
916 Hash
.Add(Buffer
,Res
);
917 if (To
.Write(Buffer
,Res
) == false)
928 // Read the closing message from the server
929 if (ReadResp(Tag
,Msg
) == false)
932 return _error
->Error(_("Data transfer failed, server said '%s'"),Msg
.c_str());
937 // FtpMethod::FtpMethod - Constructor /*{{{*/
938 // ---------------------------------------------------------------------
940 FtpMethod::FtpMethod() : pkgAcqMethod("1.0",SendConfig
)
942 signal(SIGTERM
,SigTerm
);
943 signal(SIGINT
,SigTerm
);
949 // FtpMethod::SigTerm - Handle a fatal signal /*{{{*/
950 // ---------------------------------------------------------------------
951 /* This closes and timestamps the open file. This is neccessary to get
952 resume behavoir on user abort */
953 void FtpMethod::SigTerm(int)
961 UBuf
.actime
= FailTime
;
962 UBuf
.modtime
= FailTime
;
963 utime(FailFile
.c_str(),&UBuf
);
968 // FtpMethod::Configuration - Handle a configuration message /*{{{*/
969 // ---------------------------------------------------------------------
970 /* We stash the desired pipeline depth */
971 bool FtpMethod::Configuration(string Message
)
973 if (pkgAcqMethod::Configuration(Message
) == false)
976 TimeOut
= _config
->FindI("Acquire::Ftp::Timeout",TimeOut
);
980 // FtpMethod::Fetch - Fetch a file /*{{{*/
981 // ---------------------------------------------------------------------
982 /* Fetch a single file, called by the base class.. */
983 bool FtpMethod::Fetch(FetchItem
*Itm
)
986 const char *File
= Get
.Path
.c_str();
988 Res
.Filename
= Itm
->DestFile
;
991 maybe_add_auth (Get
, _config
->FindFile("Dir::Etc::netrc"));
993 // Connect to the server
994 if (Server
== 0 || Server
->Comp(Get
) == false)
997 Server
= new FTPConn(Get
);
1000 // Could not connect is a transient error..
1001 if (Server
->Open(this) == false)
1008 // Get the files information
1010 unsigned long long Size
;
1011 if (Server
->Size(File
,Size
) == false ||
1012 Server
->ModTime(File
,FailTime
) == false)
1019 // See if it is an IMS hit
1020 if (Itm
->LastModified
== FailTime
)
1028 // See if the file exists
1030 if (stat(Itm
->DestFile
.c_str(),&Buf
) == 0)
1032 if (Size
== (unsigned long long)Buf
.st_size
&& FailTime
== Buf
.st_mtime
)
1034 Res
.Size
= Buf
.st_size
;
1035 Res
.LastModified
= Buf
.st_mtime
;
1036 Res
.ResumePoint
= Buf
.st_size
;
1042 if (FailTime
== Buf
.st_mtime
&& Size
> (unsigned long long)Buf
.st_size
)
1043 Res
.ResumePoint
= Buf
.st_size
;
1049 FileFd
Fd(Itm
->DestFile
,FileFd::WriteAny
);
1050 if (_error
->PendingError() == true)
1055 FailFile
= Itm
->DestFile
;
1056 FailFile
.c_str(); // Make sure we dont do a malloc in the signal handler
1060 if (Server
->Get(File
,Fd
,Res
.ResumePoint
,Hash
,Missing
) == false)
1065 struct utimbuf UBuf
;
1066 UBuf
.actime
= FailTime
;
1067 UBuf
.modtime
= FailTime
;
1068 utime(FailFile
.c_str(),&UBuf
);
1070 // If the file is missing we hard fail and delete the destfile
1071 // otherwise transient fail
1072 if (Missing
== true) {
1073 unlink(FailFile
.c_str());
1080 Res
.Size
= Fd
.Size();
1083 Res
.LastModified
= FailTime
;
1084 Res
.TakeHashes(Hash
);
1087 struct utimbuf UBuf
;
1088 UBuf
.actime
= FailTime
;
1089 UBuf
.modtime
= FailTime
;
1090 utime(Queue
->DestFile
.c_str(),&UBuf
);
1099 int main(int argc
,const char *argv
[])
1101 setlocale(LC_ALL
, "");
1103 /* See if we should be come the http client - we do this for http
1105 if (getenv("ftp_proxy") != 0)
1107 URI Proxy
= string(getenv("ftp_proxy"));
1109 // Run the HTTP method
1110 if (Proxy
.Access
== "http")
1112 // Copy over the environment setting
1114 snprintf(S
,sizeof(S
),"http_proxy=%s",getenv("ftp_proxy"));
1116 putenv((char *)"no_proxy=");
1118 // Run the http method
1119 string Path
= flNotFile(argv
[0]) + "http";
1120 execl(Path
.c_str(),Path
.c_str(),(char *)NULL
);
1121 cerr
<< _("Unable to invoke ") << Path
<< endl
;