]>
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 Acquire Method - This is the FTP acquire 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>
26 #include <apt-pkg/strutl.h>
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
45 #include "rfc2553emu.h"
54 /* This table is for the EPRT and EPSV commands, it maps the OS address
55 family to the IETF address families */
59 unsigned long IETFFamily
;
63 struct AFMap AFMap
[] = {{AF_INET
,1},{0, 0}};
65 struct AFMap AFMap
[] = {{AF_INET
,1},{AF_INET6
,2},{0, 0}};
68 unsigned long TimeOut
= 120;
70 string
FtpMethod::FailFile
;
71 int FtpMethod::FailFd
= -1;
72 time_t FtpMethod::FailTime
= 0;
74 // FTPConn::FTPConn - Constructor /*{{{*/
75 // ---------------------------------------------------------------------
77 FTPConn::FTPConn(URI Srv
) : Len(0), ServerFd(-1), DataFd(-1),
78 DataListenFd(-1), ServerName(Srv
),
79 ForceExtended(false), TryPassive(true),
80 PeerAddrLen(0), ServerAddrLen(0)
82 Debug
= _config
->FindB("Debug::Acquire::Ftp",false);
87 // FTPConn::~FTPConn - Destructor /*{{{*/
88 // ---------------------------------------------------------------------
95 // FTPConn::Close - Close down the connection /*{{{*/
96 // ---------------------------------------------------------------------
97 /* Just tear down the socket and data socket */
108 freeaddrinfo(PasvAddr
);
112 // FTPConn::Open - Open a new connection /*{{{*/
113 // ---------------------------------------------------------------------
114 /* Connect to the server using a non-blocking connection and perform a
116 bool FTPConn::Open(pkgAcqMethod
*Owner
)
118 // Use the already open connection if possible.
124 // Determine the proxy setting
125 string SpecificProxy
= _config
->Find("Acquire::ftp::Proxy::" + ServerName
.Host
);
126 if (!SpecificProxy
.empty())
128 if (SpecificProxy
== "DIRECT")
131 Proxy
= SpecificProxy
;
135 string DefProxy
= _config
->Find("Acquire::ftp::Proxy");
136 if (!DefProxy
.empty())
142 char* result
= getenv("ftp_proxy");
143 Proxy
= result
? result
: "";
147 // Parse no_proxy, a , separated list of domains
148 if (getenv("no_proxy") != 0)
150 if (CheckDomainList(ServerName
.Host
,getenv("no_proxy")) == true)
154 // Determine what host and port to use based on the proxy settings
157 if (Proxy
.empty() == true)
159 if (ServerName
.Port
!= 0)
160 Port
= ServerName
.Port
;
161 Host
= ServerName
.Host
;
170 /* Connect to the remote server. Since FTP is connection oriented we
171 want to make sure we get a new server every time we reconnect */
173 if (Connect(Host
,Port
,"ftp",21,ServerFd
,TimeOut
,Owner
) == false)
176 // Login must be before getpeername otherwise dante won't work.
177 Owner
->Status(_("Logging in"));
180 // Get the remote server's address
181 PeerAddrLen
= sizeof(PeerAddr
);
182 if (getpeername(ServerFd
,(sockaddr
*)&PeerAddr
,&PeerAddrLen
) != 0)
183 return _error
->Errno("getpeername",_("Unable to determine the peer name"));
185 // Get the local machine's address
186 ServerAddrLen
= sizeof(ServerAddr
);
187 if (getsockname(ServerFd
,(sockaddr
*)&ServerAddr
,&ServerAddrLen
) != 0)
188 return _error
->Errno("getsockname",_("Unable to determine the local name"));
193 // FTPConn::Login - Login to the remote server /*{{{*/
194 // ---------------------------------------------------------------------
195 /* This performs both normal login and proxy login using a simples script
196 stored in the config file. */
197 bool FTPConn::Login()
202 // Setup the variables needed for authentication
203 string User
= "anonymous";
204 string Pass
= "apt_get_ftp_2.1@debian.linux.user";
206 // Fill in the user/pass
207 if (ServerName
.User
.empty() == false)
208 User
= ServerName
.User
;
209 if (ServerName
.Password
.empty() == false)
210 Pass
= ServerName
.Password
;
212 // Perform simple login
213 if (Proxy
.empty() == true)
215 // Read the initial response
216 if (ReadResp(Tag
,Msg
) == false)
219 return _error
->Error(_("The server refused the connection and said: %s"),Msg
.c_str());
222 if (WriteMsg(Tag
,Msg
,"USER %s",User
.c_str()) == false)
225 return _error
->Error(_("USER failed, server said: %s"),Msg
.c_str());
227 if (Tag
== 331) { // 331 User name okay, need password.
229 if (WriteMsg(Tag
,Msg
,"PASS %s",Pass
.c_str()) == false)
232 return _error
->Error(_("PASS failed, server said: %s"),Msg
.c_str());
235 // Enter passive mode
236 if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true)
237 TryPassive
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true);
239 TryPassive
= _config
->FindB("Acquire::FTP::Passive",true);
243 // Read the initial response
244 if (ReadResp(Tag
,Msg
) == false)
247 return _error
->Error(_("The server refused the connection and said: %s"),Msg
.c_str());
249 // Perform proxy script execution
250 Configuration::Item
const *Opts
= _config
->Tree("Acquire::ftp::ProxyLogin");
251 if (Opts
== 0 || Opts
->Child
== 0)
252 return _error
->Error(_("A proxy server was specified but no login "
253 "script, Acquire::ftp::ProxyLogin is empty."));
256 // Iterate over the entire login script
257 for (; Opts
!= 0; Opts
= Opts
->Next
)
259 if (Opts
->Value
.empty() == true)
262 // Substitute the variables into the command
263 string Tmp
= Opts
->Value
;
264 Tmp
= SubstVar(Tmp
,"$(PROXY_USER)",Proxy
.User
);
265 Tmp
= SubstVar(Tmp
,"$(PROXY_PASS)",Proxy
.Password
);
266 Tmp
= SubstVar(Tmp
,"$(SITE_USER)",User
);
267 Tmp
= SubstVar(Tmp
,"$(SITE_PASS)",Pass
);
268 if (ServerName
.Port
!= 0)
270 std::string SitePort
;
271 strprintf(SitePort
, "%u", ServerName
.Port
);
272 Tmp
= SubstVar(Tmp
,"$(SITE_PORT)", SitePort
);
275 Tmp
= SubstVar(Tmp
,"$(SITE_PORT)", "21");
276 Tmp
= SubstVar(Tmp
,"$(SITE)",ServerName
.Host
);
279 if (WriteMsg(Tag
,Msg
,"%s",Tmp
.c_str()) == false)
282 return _error
->Error(_("Login script command '%s' failed, server said: %s"),Tmp
.c_str(),Msg
.c_str());
285 // Enter passive mode
287 if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true)
288 TryPassive
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true);
291 if (_config
->Exists("Acquire::FTP::Proxy::Passive") == true)
292 TryPassive
= _config
->FindB("Acquire::FTP::Proxy::Passive",true);
294 TryPassive
= _config
->FindB("Acquire::FTP::Passive",true);
298 // Force the use of extended commands
299 if (_config
->Exists("Acquire::FTP::ForceExtended::" + ServerName
.Host
) == true)
300 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended::" + ServerName
.Host
,true);
302 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended",false);
305 if (WriteMsg(Tag
,Msg
,"TYPE I") == false)
308 return _error
->Error(_("TYPE failed, server said: %s"),Msg
.c_str());
313 // FTPConn::ReadLine - Read a line from the server /*{{{*/
314 // ---------------------------------------------------------------------
315 /* This performs a very simple buffered read. */
316 bool FTPConn::ReadLine(string
&Text
)
322 while (Len
< sizeof(Buffer
))
324 // Scan the buffer for a new line
325 for (unsigned int I
= 0; I
!= Len
; I
++)
327 // Escape some special chars
332 if (Buffer
[I
] != '\n')
336 Text
= string(Buffer
,I
);
337 memmove(Buffer
,Buffer
+I
,Len
- I
);
342 // Wait for some data..
343 if (WaitFd(ServerFd
,false,TimeOut
) == false)
346 return _error
->Error(_("Connection timeout"));
350 int Res
= read(ServerFd
,Buffer
+ Len
,sizeof(Buffer
) - Len
);
352 _error
->Error(_("Server closed the connection"));
355 _error
->Errno("read",_("Read error"));
362 return _error
->Error(_("A response overflowed the buffer."));
365 // FTPConn::ReadResp - Read a full response from the server /*{{{*/
366 // ---------------------------------------------------------------------
367 /* This reads a reply code from the server, it handles both p */
368 bool FTPConn::ReadResp(unsigned int &Ret
,string
&Text
)
370 // Grab the first line of the response
372 if (ReadLine(Msg
) == false)
377 Ret
= strtol(Msg
.c_str(),&End
,10);
378 if (End
- Msg
.c_str() != 3)
379 return _error
->Error(_("Protocol corruption"));
382 Text
= Msg
.c_str()+4;
386 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
391 return _error
->Error(_("Protocol corruption"));
393 /* Okay, here we do the continued message trick. This is foolish, but
394 proftpd follows the protocol as specified and wu-ftpd doesn't, so
395 we filter. I wonder how many clients break if you use proftpd and
396 put a '- in the 3rd spot in the message? */
398 strncpy(Leader
,Msg
.c_str(),3);
400 while (ReadLine(Msg
) == true)
402 // Short, it must be using RFC continuation..
403 if (Msg
.length() < 4)
410 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == ' ')
412 Text
+= Msg
.c_str()+4;
416 // This message has the wu-ftpd style reply code prefixed
417 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == '-')
419 Text
+= Msg
.c_str()+4;
423 // Must be RFC style prefixing
427 if (Debug
== true && _error
->PendingError() == false)
428 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
430 return !_error
->PendingError();
433 // FTPConn::WriteMsg - Send a message to the server /*{{{*/
434 // ---------------------------------------------------------------------
435 /* Simple printf like function.. */
436 bool FTPConn::WriteMsg(unsigned int &Ret
,string
&Text
,const char *Fmt
,...)
441 // sprintf the description
443 vsnprintf(S
,sizeof(S
) - 4,Fmt
,args
);
448 cerr
<< "-> '" << QuoteString(S
,"") << "'" << endl
;
451 unsigned long Len
= strlen(S
);
452 unsigned long Start
= 0;
455 if (WaitFd(ServerFd
,true,TimeOut
) == false)
458 return _error
->Error(_("Connection timeout"));
461 int Res
= write(ServerFd
,S
+ Start
,Len
);
464 _error
->Errno("write",_("Write error"));
473 return ReadResp(Ret
,Text
);
476 // FTPConn::GoPasv - Enter Passive mode /*{{{*/
477 // ---------------------------------------------------------------------
478 /* Try to enter passive mode, the return code does not indicate if passive
479 mode could or could not be established, only if there was a fatal error.
480 We have to enter passive mode every time we make a data connection :| */
481 bool FTPConn::GoPasv()
483 /* The PASV command only works on IPv4 sockets, even though it could
484 in theory suppory IPv6 via an all zeros reply */
485 if (((struct sockaddr
*)&PeerAddr
)->sa_family
!= AF_INET
||
486 ForceExtended
== true)
490 freeaddrinfo(PasvAddr
);
493 // Try to enable pasv mode
496 if (WriteMsg(Tag
,Msg
,"PASV") == false)
499 // Unsupported function
500 string::size_type Pos
= Msg
.find('(');
501 if (Tag
>= 400 || Pos
== string::npos
)
505 unsigned a0
,a1
,a2
,a3
,p0
,p1
;
506 if (sscanf(Msg
.c_str() + Pos
,"(%u,%u,%u,%u,%u,%u)",&a0
,&a1
,&a2
,&a3
,&p0
,&p1
) != 6)
509 /* Some evil servers return 0 to mean their addr. We can actually speak
510 to these servers natively using IPv6 */
511 if (a0
== 0 && a1
== 0 && a2
== 0 && a3
== 0)
513 // Get the IP in text form
514 char Name
[NI_MAXHOST
];
515 char Service
[NI_MAXSERV
];
516 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
517 Name
,sizeof(Name
),Service
,sizeof(Service
),
518 NI_NUMERICHOST
|NI_NUMERICSERV
);
520 struct addrinfo Hints
;
521 memset(&Hints
,0,sizeof(Hints
));
522 Hints
.ai_socktype
= SOCK_STREAM
;
523 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
524 Hints
.ai_flags
|= AI_NUMERICHOST
;
526 // Get a new passive address.
528 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
529 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
534 struct addrinfo Hints
;
535 memset(&Hints
,0,sizeof(Hints
));
536 Hints
.ai_socktype
= SOCK_STREAM
;
537 Hints
.ai_family
= AF_INET
;
538 Hints
.ai_flags
|= AI_NUMERICHOST
;
540 // Get a new passive address.
542 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
544 snprintf(Name
,sizeof(Name
),"%u.%u.%u.%u",a0
,a1
,a2
,a3
);
545 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
550 // FTPConn::ExtGoPasv - Enter Extended Passive mode /*{{{*/
551 // ---------------------------------------------------------------------
552 /* Try to enter extended passive mode. See GoPasv above and RFC 2428 */
553 bool FTPConn::ExtGoPasv()
556 freeaddrinfo(PasvAddr
);
559 // Try to enable pasv mode
562 if (WriteMsg(Tag
,Msg
,"EPSV") == false)
565 // Unsupported function
566 string::size_type Pos
= Msg
.find('(');
567 if (Tag
>= 400 || Pos
== string::npos
)
571 string::const_iterator List
[4];
574 for (string::const_iterator I
= Msg
.begin() + Pos
; I
< Msg
.end(); ++I
)
586 unsigned long Proto
= 0;
587 unsigned long Port
= 0;
589 IP
= string(List
[1]+1,List
[2]);
590 Port
= atoi(string(List
[2]+1,List
[3]).c_str());
591 if (IP
.empty() == false)
592 Proto
= atoi(string(List
[0]+1,List
[1]).c_str());
597 // String version of the port
599 snprintf(PStr
,sizeof(PStr
),"%lu",Port
);
601 // Get the IP in text form
602 struct addrinfo Hints
;
603 memset(&Hints
,0,sizeof(Hints
));
604 Hints
.ai_socktype
= SOCK_STREAM
;
605 Hints
.ai_flags
|= AI_NUMERICHOST
;
607 /* The RFC defined case, connect to the old IP/protocol using the
609 if (IP
.empty() == true)
611 // Get the IP in text form
612 char Name
[NI_MAXHOST
];
613 char Service
[NI_MAXSERV
];
614 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
615 Name
,sizeof(Name
),Service
,sizeof(Service
),
616 NI_NUMERICHOST
|NI_NUMERICSERV
);
618 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
624 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
625 if (AFMap
[J
].IETFFamily
== Proto
)
626 Hints
.ai_family
= AFMap
[J
].Family
;
627 if (Hints
.ai_family
== 0)
631 // Get a new passive address.
632 if (getaddrinfo(IP
.c_str(),PStr
,&Hints
,&PasvAddr
) != 0)
638 // FTPConn::Size - Return the size of a file /*{{{*/
639 // ---------------------------------------------------------------------
640 /* Grab the file size from the server, 0 means no size or empty file */
641 bool FTPConn::Size(const char *Path
,unsigned long long &Size
)
647 if (WriteMsg(Tag
,Msg
,"SIZE %s",Path
) == false)
651 Size
= strtoull(Msg
.c_str(),&End
,10);
652 if (Tag
>= 400 || End
== Msg
.c_str())
657 // FTPConn::ModTime - Return the modification time of the file /*{{{*/
658 // ---------------------------------------------------------------------
659 /* Like Size no error is returned if the command is not supported. If the
660 command fails then time is set to the current time of day to fool
662 bool FTPConn::ModTime(const char *Path
, time_t &Time
)
666 // Query the mod time
669 if (WriteMsg(Tag
,Msg
,"MDTM %s",Path
) == false)
671 if (Tag
>= 400 || Msg
.empty() == true || isdigit(Msg
[0]) == 0)
675 return FTPMDTMStrToTime(Msg
.c_str(), Time
);
678 // FTPConn::CreateDataFd - Get a data connection /*{{{*/
679 // ---------------------------------------------------------------------
680 /* Create the data connection. Call FinalizeDataFd after this though.. */
681 bool FTPConn::CreateDataFd()
686 // Attempt to enter passive mode.
687 if (TryPassive
== true)
689 if (GoPasv() == false)
692 // Oops, didn't work out, don't bother trying again.
701 if ((DataFd
= socket(PasvAddr
->ai_family
,PasvAddr
->ai_socktype
,
702 PasvAddr
->ai_protocol
)) < 0)
703 return _error
->Errno("socket",_("Could not create a socket"));
705 // Connect to the server
706 SetNonBlock(DataFd
,true);
707 if (connect(DataFd
,PasvAddr
->ai_addr
,PasvAddr
->ai_addrlen
) < 0 &&
708 errno
!= EINPROGRESS
)
709 return _error
->Errno("socket",_("Could not create a socket"));
711 /* This implements a timeout for connect by opening the connection
713 if (WaitFd(DataFd
,true,TimeOut
) == false)
714 return _error
->Error(_("Could not connect data socket, connection timed out"));
716 unsigned int Len
= sizeof(Err
);
717 if (getsockopt(DataFd
,SOL_SOCKET
,SO_ERROR
,&Err
,&Len
) != 0)
718 return _error
->Errno("getsockopt",_("Failed"));
720 return _error
->Error(_("Could not connect passive socket."));
729 // Get the information for a listening socket.
730 struct addrinfo
*BindAddr
= NULL
;
731 struct addrinfo Hints
;
732 memset(&Hints
,0,sizeof(Hints
));
733 Hints
.ai_socktype
= SOCK_STREAM
;
734 Hints
.ai_flags
|= AI_PASSIVE
;
735 Hints
.ai_family
= ((struct sockaddr
*)&ServerAddr
)->sa_family
;
736 if (getaddrinfo(0,"0",&Hints
,&BindAddr
) != 0 || BindAddr
== NULL
)
737 return _error
->Error(_("getaddrinfo was unable to get a listening socket"));
739 // Construct the socket
740 if ((DataListenFd
= socket(BindAddr
->ai_family
,BindAddr
->ai_socktype
,
741 BindAddr
->ai_protocol
)) < 0)
743 freeaddrinfo(BindAddr
);
744 return _error
->Errno("socket",_("Could not create a socket"));
748 if (::bind(DataListenFd
,BindAddr
->ai_addr
,BindAddr
->ai_addrlen
) < 0)
750 freeaddrinfo(BindAddr
);
751 return _error
->Errno("bind",_("Could not bind a socket"));
753 freeaddrinfo(BindAddr
);
754 if (listen(DataListenFd
,1) < 0)
755 return _error
->Errno("listen",_("Could not listen on the socket"));
756 SetNonBlock(DataListenFd
,true);
758 // Determine the name to send to the remote
759 struct sockaddr_storage Addr
;
760 socklen_t AddrLen
= sizeof(Addr
);
761 if (getsockname(DataListenFd
,(sockaddr
*)&Addr
,&AddrLen
) < 0)
762 return _error
->Errno("getsockname",_("Could not determine the socket's name"));
765 // Reverse the address. We need the server address and the data port.
766 char Name
[NI_MAXHOST
];
767 char Service
[NI_MAXSERV
];
768 char Service2
[NI_MAXSERV
];
769 getnameinfo((struct sockaddr
*)&Addr
,AddrLen
,
770 Name
,sizeof(Name
),Service
,sizeof(Service
),
771 NI_NUMERICHOST
|NI_NUMERICSERV
);
772 getnameinfo((struct sockaddr
*)&ServerAddr
,ServerAddrLen
,
773 Name
,sizeof(Name
),Service2
,sizeof(Service2
),
774 NI_NUMERICHOST
|NI_NUMERICSERV
);
776 // Send off an IPv4 address in the old port format
777 if (((struct sockaddr
*)&Addr
)->sa_family
== AF_INET
&&
778 ForceExtended
== false)
780 // Convert the dots in the quad into commas
781 for (char *I
= Name
; *I
!= 0; I
++)
784 unsigned long Port
= atoi(Service
);
786 // Send the port command
789 if (WriteMsg(Tag
,Msg
,"PORT %s,%d,%d",
791 (int)(Port
>> 8) & 0xff, (int)(Port
& 0xff)) == false)
794 return _error
->Error(_("Unable to send PORT command"));
798 // Construct an EPRT command
800 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
801 if (AFMap
[J
].Family
== ((struct sockaddr
*)&Addr
)->sa_family
)
802 Proto
= AFMap
[J
].IETFFamily
;
804 return _error
->Error(_("Unknown address family %u (AF_*)"),
805 ((struct sockaddr
*)&Addr
)->sa_family
);
807 // Send the EPRT command
810 if (WriteMsg(Tag
,Msg
,"EPRT |%u|%s|%s|",Proto
,Name
,Service
) == false)
813 return _error
->Error(_("EPRT failed, server said: %s"),Msg
.c_str());
817 // FTPConn::Finalize - Complete the Data connection /*{{{*/
818 // ---------------------------------------------------------------------
819 /* If the connection is in port mode this waits for the other end to hook
821 bool FTPConn::Finalize()
823 // Passive mode? Do nothing
827 // Close any old socket..
831 // Wait for someone to connect..
832 if (WaitFd(DataListenFd
,false,TimeOut
) == false)
833 return _error
->Error(_("Data socket connect timed out"));
835 // Accept the connection
836 struct sockaddr_in Addr
;
837 socklen_t Len
= sizeof(Addr
);
838 DataFd
= accept(DataListenFd
,(struct sockaddr
*)&Addr
,&Len
);
840 return _error
->Errno("accept",_("Unable to accept connection"));
848 // FTPConn::Get - Get a file /*{{{*/
849 // ---------------------------------------------------------------------
850 /* This opens a data connection, sends REST and RETR and then
851 transfers the file over. */
852 bool FTPConn::Get(const char *Path
,FileFd
&To
,unsigned long long Resume
,
853 Hashes
&Hash
,bool &Missing
, unsigned long long MaximumSize
,
857 if (CreateDataFd() == false)
864 if (WriteMsg(Tag
,Msg
,"REST %u",Resume
) == false)
870 if (To
.Truncate(Resume
) == false)
873 if (To
.Seek(0) == false)
878 if (Hash
.AddFD(To
,Resume
) == false)
880 _error
->Errno("read",_("Problem hashing file"));
885 // Send the get command
886 if (WriteMsg(Tag
,Msg
,"RETR %s",Path
) == false)
893 return _error
->Error(_("Unable to fetch file, server said '%s'"),Msg
.c_str());
896 // Finish off the data connection
897 if (Finalize() == false)
901 unsigned char Buffer
[4096];
904 // Wait for some data..
905 if (WaitFd(DataFd
,false,TimeOut
) == false)
908 return _error
->Error(_("Data socket timed out"));
912 int Res
= read(DataFd
,Buffer
,sizeof(Buffer
));
922 Hash
.Add(Buffer
,Res
);
923 if (To
.Write(Buffer
,Res
) == false)
929 if (MaximumSize
> 0 && To
.Tell() > MaximumSize
)
931 Owner
->SetFailReason("MaximumSizeExceeded");
932 return _error
->Error("Writing more data than expected (%llu > %llu)",
933 To
.Tell(), MaximumSize
);
941 // Read the closing message from the server
942 if (ReadResp(Tag
,Msg
) == false)
945 return _error
->Error(_("Data transfer failed, server said '%s'"),Msg
.c_str());
950 // FtpMethod::FtpMethod - Constructor /*{{{*/
951 // ---------------------------------------------------------------------
953 FtpMethod::FtpMethod() : aptMethod("ftp","1.0",SendConfig
)
955 signal(SIGTERM
,SigTerm
);
956 signal(SIGINT
,SigTerm
);
962 // FtpMethod::SigTerm - Handle a fatal signal /*{{{*/
963 // ---------------------------------------------------------------------
964 /* This closes and timestamps the open file. This is necessary to get
965 resume behavoir on user abort */
966 void FtpMethod::SigTerm(int)
972 struct timeval times
[2];
973 times
[0].tv_sec
= FailTime
;
974 times
[1].tv_sec
= FailTime
;
975 times
[0].tv_usec
= times
[1].tv_usec
= 0;
976 utimes(FailFile
.c_str(), times
);
983 // FtpMethod::Configuration - Handle a configuration message /*{{{*/
984 // ---------------------------------------------------------------------
985 /* We stash the desired pipeline depth */
986 bool FtpMethod::Configuration(string Message
)
988 if (aptMethod::Configuration(Message
) == false)
991 TimeOut
= _config
->FindI("Acquire::Ftp::Timeout",TimeOut
);
996 // FtpMethod::Fetch - Fetch a file /*{{{*/
997 // ---------------------------------------------------------------------
998 /* Fetch a single file, called by the base class.. */
999 bool FtpMethod::Fetch(FetchItem
*Itm
)
1002 const char *File
= Get
.Path
.c_str();
1004 Res
.Filename
= Itm
->DestFile
;
1007 maybe_add_auth (Get
, _config
->FindFile("Dir::Etc::netrc"));
1009 // Connect to the server
1010 if (Server
== 0 || Server
->Comp(Get
) == false)
1013 Server
= new FTPConn(Get
);
1016 // Could not connect is a transient error..
1017 if (Server
->Open(this) == false)
1024 // Get the files information
1026 unsigned long long Size
;
1027 if (Server
->Size(File
,Size
) == false ||
1028 Server
->ModTime(File
,FailTime
) == false)
1035 // See if it is an IMS hit
1036 if (Itm
->LastModified
== FailTime
)
1044 // See if the file exists
1046 if (stat(Itm
->DestFile
.c_str(),&Buf
) == 0)
1048 if (Size
== (unsigned long long)Buf
.st_size
&& FailTime
== Buf
.st_mtime
)
1050 Res
.Size
= Buf
.st_size
;
1051 Res
.LastModified
= Buf
.st_mtime
;
1052 Res
.ResumePoint
= Buf
.st_size
;
1058 if (FailTime
== Buf
.st_mtime
&& Size
> (unsigned long long)Buf
.st_size
)
1059 Res
.ResumePoint
= Buf
.st_size
;
1063 Hashes
Hash(Itm
->ExpectedHashes
);
1065 FileFd
Fd(Itm
->DestFile
,FileFd::WriteAny
);
1066 if (_error
->PendingError() == true)
1071 FailFile
= Itm
->DestFile
;
1072 FailFile
.c_str(); // Make sure we don't do a malloc in the signal handler
1076 if (Server
->Get(File
,Fd
,Res
.ResumePoint
,Hash
,Missing
,Itm
->MaximumSize
,this) == false)
1081 struct timeval times
[2];
1082 times
[0].tv_sec
= FailTime
;
1083 times
[1].tv_sec
= FailTime
;
1084 times
[0].tv_usec
= times
[1].tv_usec
= 0;
1085 utimes(FailFile
.c_str(), times
);
1087 // If the file is missing we hard fail and delete the destfile
1088 // otherwise transient fail
1089 if (Missing
== true) {
1090 RemoveFile("ftp", FailFile
);
1097 Res
.Size
= Fd
.Size();
1100 struct timeval times
[2];
1101 times
[0].tv_sec
= FailTime
;
1102 times
[1].tv_sec
= FailTime
;
1103 times
[0].tv_usec
= times
[1].tv_usec
= 0;
1104 utimes(Fd
.Name().c_str(), times
);
1108 Res
.LastModified
= FailTime
;
1109 Res
.TakeHashes(Hash
);
1117 int main(int, const char *argv
[])
1119 setlocale(LC_ALL
, "");
1121 /* See if we should be come the http client - we do this for http
1123 if (getenv("ftp_proxy") != 0)
1125 URI Proxy
= string(getenv("ftp_proxy"));
1127 // Run the HTTP method
1128 if (Proxy
.Access
== "http")
1130 // Copy over the environment setting
1132 snprintf(S
,sizeof(S
),"http_proxy=%s",getenv("ftp_proxy"));
1134 putenv((char *)"no_proxy=");
1136 // Run the http method
1137 string Path
= flNotFile(argv
[0]) + "http";
1138 execl(Path
.c_str(),Path
.c_str(),(char *)NULL
);
1139 cerr
<< _("Unable to invoke ") << Path
<< endl
;