]>
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
);
442 cerr
<< "-> '" << QuoteString(S
,"") << "'" << endl
;
445 unsigned long Len
= strlen(S
);
446 unsigned long Start
= 0;
449 if (WaitFd(ServerFd
,true,TimeOut
) == false)
452 return _error
->Error(_("Connection timeout"));
455 int Res
= write(ServerFd
,S
+ Start
,Len
);
458 _error
->Errno("write",_("Write error"));
467 return ReadResp(Ret
,Text
);
470 // FTPConn::GoPasv - Enter Passive mode /*{{{*/
471 // ---------------------------------------------------------------------
472 /* Try to enter passive mode, the return code does not indicate if passive
473 mode could or could not be established, only if there was a fatal error.
474 We have to enter passive mode every time we make a data connection :| */
475 bool FTPConn::GoPasv()
477 /* The PASV command only works on IPv4 sockets, even though it could
478 in theory suppory IPv6 via an all zeros reply */
479 if (((struct sockaddr
*)&PeerAddr
)->sa_family
!= AF_INET
||
480 ForceExtended
== true)
484 freeaddrinfo(PasvAddr
);
487 // Try to enable pasv mode
490 if (WriteMsg(Tag
,Msg
,"PASV") == false)
493 // Unsupported function
494 string::size_type Pos
= Msg
.find('(');
495 if (Tag
>= 400 || Pos
== string::npos
)
499 unsigned a0
,a1
,a2
,a3
,p0
,p1
;
500 if (sscanf(Msg
.c_str() + Pos
,"(%u,%u,%u,%u,%u,%u)",&a0
,&a1
,&a2
,&a3
,&p0
,&p1
) != 6)
503 /* Some evil servers return 0 to mean their addr. We can actually speak
504 to these servers natively using IPv6 */
505 if (a0
== 0 && a1
== 0 && a2
== 0 && a3
== 0)
507 // Get the IP in text form
508 char Name
[NI_MAXHOST
];
509 char Service
[NI_MAXSERV
];
510 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
511 Name
,sizeof(Name
),Service
,sizeof(Service
),
512 NI_NUMERICHOST
|NI_NUMERICSERV
);
514 struct addrinfo Hints
;
515 memset(&Hints
,0,sizeof(Hints
));
516 Hints
.ai_socktype
= SOCK_STREAM
;
517 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
518 Hints
.ai_flags
|= AI_NUMERICHOST
;
520 // Get a new passive address.
522 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
523 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
528 struct addrinfo Hints
;
529 memset(&Hints
,0,sizeof(Hints
));
530 Hints
.ai_socktype
= SOCK_STREAM
;
531 Hints
.ai_family
= AF_INET
;
532 Hints
.ai_flags
|= AI_NUMERICHOST
;
534 // Get a new passive address.
536 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
538 snprintf(Name
,sizeof(Name
),"%u.%u.%u.%u",a0
,a1
,a2
,a3
);
539 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
544 // FTPConn::ExtGoPasv - Enter Extended Passive mode /*{{{*/
545 // ---------------------------------------------------------------------
546 /* Try to enter extended passive mode. See GoPasv above and RFC 2428 */
547 bool FTPConn::ExtGoPasv()
550 freeaddrinfo(PasvAddr
);
553 // Try to enable pasv mode
556 if (WriteMsg(Tag
,Msg
,"EPSV") == false)
559 // Unsupported function
560 string::size_type Pos
= Msg
.find('(');
561 if (Tag
>= 400 || Pos
== string::npos
)
565 string::const_iterator List
[4];
568 for (string::const_iterator I
= Msg
.begin() + Pos
; I
< Msg
.end(); ++I
)
580 unsigned long Proto
= 0;
581 unsigned long Port
= 0;
583 IP
= string(List
[1]+1,List
[2]);
584 Port
= atoi(string(List
[2]+1,List
[3]).c_str());
585 if (IP
.empty() == false)
586 Proto
= atoi(string(List
[0]+1,List
[1]).c_str());
591 // String version of the port
593 snprintf(PStr
,sizeof(PStr
),"%lu",Port
);
595 // Get the IP in text form
596 struct addrinfo Hints
;
597 memset(&Hints
,0,sizeof(Hints
));
598 Hints
.ai_socktype
= SOCK_STREAM
;
599 Hints
.ai_flags
|= AI_NUMERICHOST
;
601 /* The RFC defined case, connect to the old IP/protocol using the
603 if (IP
.empty() == true)
605 // Get the IP in text form
606 char Name
[NI_MAXHOST
];
607 char Service
[NI_MAXSERV
];
608 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
609 Name
,sizeof(Name
),Service
,sizeof(Service
),
610 NI_NUMERICHOST
|NI_NUMERICSERV
);
612 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
618 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
619 if (AFMap
[J
].IETFFamily
== Proto
)
620 Hints
.ai_family
= AFMap
[J
].Family
;
621 if (Hints
.ai_family
== 0)
625 // Get a new passive address.
626 if (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
= NULL
;
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
;
730 if (getaddrinfo(0,"0",&Hints
,&BindAddr
) != 0 || BindAddr
== NULL
)
731 return _error
->Error(_("getaddrinfo was unable to get a listening socket"));
733 // Construct the socket
734 if ((DataListenFd
= socket(BindAddr
->ai_family
,BindAddr
->ai_socktype
,
735 BindAddr
->ai_protocol
)) < 0)
737 freeaddrinfo(BindAddr
);
738 return _error
->Errno("socket",_("Could not create a socket"));
742 if (bind(DataListenFd
,BindAddr
->ai_addr
,BindAddr
->ai_addrlen
) < 0)
744 freeaddrinfo(BindAddr
);
745 return _error
->Errno("bind",_("Could not bind a socket"));
747 freeaddrinfo(BindAddr
);
748 if (listen(DataListenFd
,1) < 0)
749 return _error
->Errno("listen",_("Could not listen on the socket"));
750 SetNonBlock(DataListenFd
,true);
752 // Determine the name to send to the remote
753 struct sockaddr_storage Addr
;
754 socklen_t AddrLen
= sizeof(Addr
);
755 if (getsockname(DataListenFd
,(sockaddr
*)&Addr
,&AddrLen
) < 0)
756 return _error
->Errno("getsockname",_("Could not determine the socket's name"));
759 // Reverse the address. We need the server address and the data port.
760 char Name
[NI_MAXHOST
];
761 char Service
[NI_MAXSERV
];
762 char Service2
[NI_MAXSERV
];
763 getnameinfo((struct sockaddr
*)&Addr
,AddrLen
,
764 Name
,sizeof(Name
),Service
,sizeof(Service
),
765 NI_NUMERICHOST
|NI_NUMERICSERV
);
766 getnameinfo((struct sockaddr
*)&ServerAddr
,ServerAddrLen
,
767 Name
,sizeof(Name
),Service2
,sizeof(Service2
),
768 NI_NUMERICHOST
|NI_NUMERICSERV
);
770 // Send off an IPv4 address in the old port format
771 if (((struct sockaddr
*)&Addr
)->sa_family
== AF_INET
&&
772 ForceExtended
== false)
774 // Convert the dots in the quad into commas
775 for (char *I
= Name
; *I
!= 0; I
++)
778 unsigned long Port
= atoi(Service
);
780 // Send the port command
783 if (WriteMsg(Tag
,Msg
,"PORT %s,%d,%d",
785 (int)(Port
>> 8) & 0xff, (int)(Port
& 0xff)) == false)
788 return _error
->Error(_("Unable to send PORT command"));
792 // Construct an EPRT command
794 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
795 if (AFMap
[J
].Family
== ((struct sockaddr
*)&Addr
)->sa_family
)
796 Proto
= AFMap
[J
].IETFFamily
;
798 return _error
->Error(_("Unknown address family %u (AF_*)"),
799 ((struct sockaddr
*)&Addr
)->sa_family
);
801 // Send the EPRT command
804 if (WriteMsg(Tag
,Msg
,"EPRT |%u|%s|%s|",Proto
,Name
,Service
) == false)
807 return _error
->Error(_("EPRT failed, server said: %s"),Msg
.c_str());
811 // FTPConn::Finalize - Complete the Data connection /*{{{*/
812 // ---------------------------------------------------------------------
813 /* If the connection is in port mode this waits for the other end to hook
815 bool FTPConn::Finalize()
817 // Passive mode? Do nothing
821 // Close any old socket..
825 // Wait for someone to connect..
826 if (WaitFd(DataListenFd
,false,TimeOut
) == false)
827 return _error
->Error(_("Data socket connect timed out"));
829 // Accept the connection
830 struct sockaddr_in Addr
;
831 socklen_t Len
= sizeof(Addr
);
832 DataFd
= accept(DataListenFd
,(struct sockaddr
*)&Addr
,&Len
);
834 return _error
->Errno("accept",_("Unable to accept connection"));
842 // FTPConn::Get - Get a file /*{{{*/
843 // ---------------------------------------------------------------------
844 /* This opens a data connection, sends REST and RETR and then
845 transfers the file over. */
846 bool FTPConn::Get(const char *Path
,FileFd
&To
,unsigned long long Resume
,
847 Hashes
&Hash
,bool &Missing
)
850 if (CreateDataFd() == false)
857 if (WriteMsg(Tag
,Msg
,"REST %u",Resume
) == false)
863 if (To
.Truncate(Resume
) == false)
866 if (To
.Seek(0) == false)
871 if (Hash
.AddFD(To
,Resume
) == false)
873 _error
->Errno("read",_("Problem hashing file"));
878 // Send the get command
879 if (WriteMsg(Tag
,Msg
,"RETR %s",Path
) == false)
886 return _error
->Error(_("Unable to fetch file, server said '%s'"),Msg
.c_str());
889 // Finish off the data connection
890 if (Finalize() == false)
894 unsigned char Buffer
[4096];
897 // Wait for some data..
898 if (WaitFd(DataFd
,false,TimeOut
) == false)
901 return _error
->Error(_("Data socket timed out"));
905 int Res
= read(DataFd
,Buffer
,sizeof(Buffer
));
915 Hash
.Add(Buffer
,Res
);
916 if (To
.Write(Buffer
,Res
) == false)
927 // Read the closing message from the server
928 if (ReadResp(Tag
,Msg
) == false)
931 return _error
->Error(_("Data transfer failed, server said '%s'"),Msg
.c_str());
936 // FtpMethod::FtpMethod - Constructor /*{{{*/
937 // ---------------------------------------------------------------------
939 FtpMethod::FtpMethod() : pkgAcqMethod("1.0",SendConfig
)
941 signal(SIGTERM
,SigTerm
);
942 signal(SIGINT
,SigTerm
);
948 // FtpMethod::SigTerm - Handle a fatal signal /*{{{*/
949 // ---------------------------------------------------------------------
950 /* This closes and timestamps the open file. This is neccessary to get
951 resume behavoir on user abort */
952 void FtpMethod::SigTerm(int)
960 UBuf
.actime
= FailTime
;
961 UBuf
.modtime
= FailTime
;
962 utime(FailFile
.c_str(),&UBuf
);
967 // FtpMethod::Configuration - Handle a configuration message /*{{{*/
968 // ---------------------------------------------------------------------
969 /* We stash the desired pipeline depth */
970 bool FtpMethod::Configuration(string Message
)
972 if (pkgAcqMethod::Configuration(Message
) == false)
975 TimeOut
= _config
->FindI("Acquire::Ftp::Timeout",TimeOut
);
979 // FtpMethod::Fetch - Fetch a file /*{{{*/
980 // ---------------------------------------------------------------------
981 /* Fetch a single file, called by the base class.. */
982 bool FtpMethod::Fetch(FetchItem
*Itm
)
985 const char *File
= Get
.Path
.c_str();
987 Res
.Filename
= Itm
->DestFile
;
990 maybe_add_auth (Get
, _config
->FindFile("Dir::Etc::netrc"));
992 // Connect to the server
993 if (Server
== 0 || Server
->Comp(Get
) == false)
996 Server
= new FTPConn(Get
);
999 // Could not connect is a transient error..
1000 if (Server
->Open(this) == false)
1007 // Get the files information
1009 unsigned long long Size
;
1010 if (Server
->Size(File
,Size
) == false ||
1011 Server
->ModTime(File
,FailTime
) == false)
1018 // See if it is an IMS hit
1019 if (Itm
->LastModified
== FailTime
)
1027 // See if the file exists
1029 if (stat(Itm
->DestFile
.c_str(),&Buf
) == 0)
1031 if (Size
== (unsigned long long)Buf
.st_size
&& FailTime
== Buf
.st_mtime
)
1033 Res
.Size
= Buf
.st_size
;
1034 Res
.LastModified
= Buf
.st_mtime
;
1035 Res
.ResumePoint
= Buf
.st_size
;
1041 if (FailTime
== Buf
.st_mtime
&& Size
> (unsigned long long)Buf
.st_size
)
1042 Res
.ResumePoint
= Buf
.st_size
;
1048 FileFd
Fd(Itm
->DestFile
,FileFd::WriteAny
);
1049 if (_error
->PendingError() == true)
1054 FailFile
= Itm
->DestFile
;
1055 FailFile
.c_str(); // Make sure we dont do a malloc in the signal handler
1059 if (Server
->Get(File
,Fd
,Res
.ResumePoint
,Hash
,Missing
) == false)
1064 struct utimbuf UBuf
;
1065 UBuf
.actime
= FailTime
;
1066 UBuf
.modtime
= FailTime
;
1067 utime(FailFile
.c_str(),&UBuf
);
1069 // If the file is missing we hard fail and delete the destfile
1070 // otherwise transient fail
1071 if (Missing
== true) {
1072 unlink(FailFile
.c_str());
1079 Res
.Size
= Fd
.Size();
1082 Res
.LastModified
= FailTime
;
1083 Res
.TakeHashes(Hash
);
1086 struct utimbuf UBuf
;
1087 UBuf
.actime
= FailTime
;
1088 UBuf
.modtime
= FailTime
;
1089 utime(Queue
->DestFile
.c_str(),&UBuf
);
1098 int main(int argc
,const char *argv
[])
1100 setlocale(LC_ALL
, "");
1102 /* See if we should be come the http client - we do this for http
1104 if (getenv("ftp_proxy") != 0)
1106 URI Proxy
= string(getenv("ftp_proxy"));
1108 // Run the HTTP method
1109 if (Proxy
.Access
== "http")
1111 // Copy over the environment setting
1113 snprintf(S
,sizeof(S
),"http_proxy=%s",getenv("ftp_proxy"));
1115 putenv((char *)"no_proxy=");
1117 // Run the http method
1118 string Path
= flNotFile(argv
[0]) + "http";
1119 execl(Path
.c_str(),Path
.c_str(),(char *)NULL
);
1120 cerr
<< _("Unable to invoke ") << Path
<< endl
;