]>
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 <sys/socket.h>
43 #include <arpa/inet.h>
46 #include "rfc2553emu.h"
55 /* This table is for the EPRT and EPSV commands, it maps the OS address
56 family to the IETF address families */
60 unsigned long IETFFamily
;
64 struct AFMap AFMap
[] = {{AF_INET
,1},{0, 0}};
66 struct AFMap AFMap
[] = {{AF_INET
,1},{AF_INET6
,2},{0, 0}};
69 unsigned long TimeOut
= 120;
71 string
FtpMethod::FailFile
;
72 int FtpMethod::FailFd
= -1;
73 time_t FtpMethod::FailTime
= 0;
75 // FTPConn::FTPConn - Constructor /*{{{*/
76 // ---------------------------------------------------------------------
78 FTPConn::FTPConn(URI Srv
) : Len(0), ServerFd(-1), DataFd(-1),
79 DataListenFd(-1), ServerName(Srv
),
80 ForceExtended(false), TryPassive(true),
81 PeerAddrLen(0), ServerAddrLen(0)
83 Debug
= _config
->FindB("Debug::Acquire::Ftp",false);
88 // FTPConn::~FTPConn - Destructor /*{{{*/
89 // ---------------------------------------------------------------------
96 // FTPConn::Close - Close down the connection /*{{{*/
97 // ---------------------------------------------------------------------
98 /* Just tear down the socket and data socket */
109 freeaddrinfo(PasvAddr
);
113 // FTPConn::Open - Open a new connection /*{{{*/
114 // ---------------------------------------------------------------------
115 /* Connect to the server using a non-blocking connection and perform a
117 bool FTPConn::Open(pkgAcqMethod
*Owner
)
119 // Use the already open connection if possible.
125 // Determine the proxy setting
126 string SpecificProxy
= _config
->Find("Acquire::ftp::Proxy::" + ServerName
.Host
);
127 if (!SpecificProxy
.empty())
129 if (SpecificProxy
== "DIRECT")
132 Proxy
= SpecificProxy
;
136 string DefProxy
= _config
->Find("Acquire::ftp::Proxy");
137 if (!DefProxy
.empty())
143 char* result
= getenv("ftp_proxy");
144 Proxy
= result
? result
: "";
148 // Parse no_proxy, a , separated list of domains
149 if (getenv("no_proxy") != 0)
151 if (CheckDomainList(ServerName
.Host
,getenv("no_proxy")) == true)
155 // Determine what host and port to use based on the proxy settings
158 if (Proxy
.empty() == true)
160 if (ServerName
.Port
!= 0)
161 Port
= ServerName
.Port
;
162 Host
= ServerName
.Host
;
171 /* Connect to the remote server. Since FTP is connection oriented we
172 want to make sure we get a new server every time we reconnect */
174 if (Connect(Host
,Port
,"ftp",21,ServerFd
,TimeOut
,Owner
) == false)
177 // Login must be before getpeername otherwise dante won't work.
178 Owner
->Status(_("Logging in"));
181 // Get the remote server's address
182 PeerAddrLen
= sizeof(PeerAddr
);
183 if (getpeername(ServerFd
,(sockaddr
*)&PeerAddr
,&PeerAddrLen
) != 0)
184 return _error
->Errno("getpeername",_("Unable to determine the peer name"));
186 // Get the local machine's address
187 ServerAddrLen
= sizeof(ServerAddr
);
188 if (getsockname(ServerFd
,(sockaddr
*)&ServerAddr
,&ServerAddrLen
) != 0)
189 return _error
->Errno("getsockname",_("Unable to determine the local name"));
194 // FTPConn::Login - Login to the remote server /*{{{*/
195 // ---------------------------------------------------------------------
196 /* This performs both normal login and proxy login using a simples script
197 stored in the config file. */
198 bool FTPConn::Login()
203 // Setup the variables needed for authentication
204 string User
= "anonymous";
205 string Pass
= "apt_get_ftp_2.1@debian.linux.user";
207 // Fill in the user/pass
208 if (ServerName
.User
.empty() == false)
209 User
= ServerName
.User
;
210 if (ServerName
.Password
.empty() == false)
211 Pass
= ServerName
.Password
;
213 // Perform simple login
214 if (Proxy
.empty() == true)
216 // Read the initial response
217 if (ReadResp(Tag
,Msg
) == false)
220 return _error
->Error(_("The server refused the connection and said: %s"),Msg
.c_str());
223 if (WriteMsg(Tag
,Msg
,"USER %s",User
.c_str()) == false)
226 return _error
->Error(_("USER failed, server said: %s"),Msg
.c_str());
228 if (Tag
== 331) { // 331 User name okay, need password.
230 if (WriteMsg(Tag
,Msg
,"PASS %s",Pass
.c_str()) == false)
233 return _error
->Error(_("PASS failed, server said: %s"),Msg
.c_str());
236 // Enter passive mode
237 if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true)
238 TryPassive
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true);
240 TryPassive
= _config
->FindB("Acquire::FTP::Passive",true);
244 // Read the initial response
245 if (ReadResp(Tag
,Msg
) == false)
248 return _error
->Error(_("The server refused the connection and said: %s"),Msg
.c_str());
250 // Perform proxy script execution
251 Configuration::Item
const *Opts
= _config
->Tree("Acquire::ftp::ProxyLogin");
252 if (Opts
== 0 || Opts
->Child
== 0)
253 return _error
->Error(_("A proxy server was specified but no login "
254 "script, Acquire::ftp::ProxyLogin is empty."));
257 // Iterate over the entire login script
258 for (; Opts
!= 0; Opts
= Opts
->Next
)
260 if (Opts
->Value
.empty() == true)
263 // Substitute the variables into the command
265 if (ServerName
.Port
!= 0)
266 sprintf(SitePort
,"%u",ServerName
.Port
);
268 strcpy(SitePort
,"21");
269 string Tmp
= Opts
->Value
;
270 Tmp
= SubstVar(Tmp
,"$(PROXY_USER)",Proxy
.User
);
271 Tmp
= SubstVar(Tmp
,"$(PROXY_PASS)",Proxy
.Password
);
272 Tmp
= SubstVar(Tmp
,"$(SITE_USER)",User
);
273 Tmp
= SubstVar(Tmp
,"$(SITE_PASS)",Pass
);
274 Tmp
= SubstVar(Tmp
,"$(SITE_PORT)",SitePort
);
275 Tmp
= SubstVar(Tmp
,"$(SITE)",ServerName
.Host
);
278 if (WriteMsg(Tag
,Msg
,"%s",Tmp
.c_str()) == false)
281 return _error
->Error(_("Login script command '%s' failed, server said: %s"),Tmp
.c_str(),Msg
.c_str());
284 // Enter passive mode
286 if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true)
287 TryPassive
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true);
290 if (_config
->Exists("Acquire::FTP::Proxy::Passive") == true)
291 TryPassive
= _config
->FindB("Acquire::FTP::Proxy::Passive",true);
293 TryPassive
= _config
->FindB("Acquire::FTP::Passive",true);
297 // Force the use of extended commands
298 if (_config
->Exists("Acquire::FTP::ForceExtended::" + ServerName
.Host
) == true)
299 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended::" + ServerName
.Host
,true);
301 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended",false);
304 if (WriteMsg(Tag
,Msg
,"TYPE I") == false)
307 return _error
->Error(_("TYPE failed, server said: %s"),Msg
.c_str());
312 // FTPConn::ReadLine - Read a line from the server /*{{{*/
313 // ---------------------------------------------------------------------
314 /* This performs a very simple buffered read. */
315 bool FTPConn::ReadLine(string
&Text
)
321 while (Len
< sizeof(Buffer
))
323 // Scan the buffer for a new line
324 for (unsigned int I
= 0; I
!= Len
; I
++)
326 // Escape some special chars
331 if (Buffer
[I
] != '\n')
335 Text
= string(Buffer
,I
);
336 memmove(Buffer
,Buffer
+I
,Len
- I
);
341 // Wait for some data..
342 if (WaitFd(ServerFd
,false,TimeOut
) == false)
345 return _error
->Error(_("Connection timeout"));
349 int Res
= read(ServerFd
,Buffer
+ Len
,sizeof(Buffer
) - Len
);
351 _error
->Error(_("Server closed the connection"));
354 _error
->Errno("read",_("Read error"));
361 return _error
->Error(_("A response overflowed the buffer."));
364 // FTPConn::ReadResp - Read a full response from the server /*{{{*/
365 // ---------------------------------------------------------------------
366 /* This reads a reply code from the server, it handles both p */
367 bool FTPConn::ReadResp(unsigned int &Ret
,string
&Text
)
369 // Grab the first line of the response
371 if (ReadLine(Msg
) == false)
376 Ret
= strtol(Msg
.c_str(),&End
,10);
377 if (End
- Msg
.c_str() != 3)
378 return _error
->Error(_("Protocol corruption"));
381 Text
= Msg
.c_str()+4;
385 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
390 return _error
->Error(_("Protocol corruption"));
392 /* Okay, here we do the continued message trick. This is foolish, but
393 proftpd follows the protocol as specified and wu-ftpd doesn't, so
394 we filter. I wonder how many clients break if you use proftpd and
395 put a '- in the 3rd spot in the message? */
397 strncpy(Leader
,Msg
.c_str(),3);
399 while (ReadLine(Msg
) == true)
401 // Short, it must be using RFC continuation..
402 if (Msg
.length() < 4)
409 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == ' ')
411 Text
+= Msg
.c_str()+4;
415 // This message has the wu-ftpd style reply code prefixed
416 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == '-')
418 Text
+= Msg
.c_str()+4;
422 // Must be RFC style prefixing
426 if (Debug
== true && _error
->PendingError() == false)
427 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
429 return !_error
->PendingError();
432 // FTPConn::WriteMsg - Send a message to the server /*{{{*/
433 // ---------------------------------------------------------------------
434 /* Simple printf like function.. */
435 bool FTPConn::WriteMsg(unsigned int &Ret
,string
&Text
,const char *Fmt
,...)
440 // sprintf the description
442 vsnprintf(S
,sizeof(S
) - 4,Fmt
,args
);
447 cerr
<< "-> '" << QuoteString(S
,"") << "'" << endl
;
450 unsigned long Len
= strlen(S
);
451 unsigned long Start
= 0;
454 if (WaitFd(ServerFd
,true,TimeOut
) == false)
457 return _error
->Error(_("Connection timeout"));
460 int Res
= write(ServerFd
,S
+ Start
,Len
);
463 _error
->Errno("write",_("Write error"));
472 return ReadResp(Ret
,Text
);
475 // FTPConn::GoPasv - Enter Passive mode /*{{{*/
476 // ---------------------------------------------------------------------
477 /* Try to enter passive mode, the return code does not indicate if passive
478 mode could or could not be established, only if there was a fatal error.
479 We have to enter passive mode every time we make a data connection :| */
480 bool FTPConn::GoPasv()
482 /* The PASV command only works on IPv4 sockets, even though it could
483 in theory suppory IPv6 via an all zeros reply */
484 if (((struct sockaddr
*)&PeerAddr
)->sa_family
!= AF_INET
||
485 ForceExtended
== true)
489 freeaddrinfo(PasvAddr
);
492 // Try to enable pasv mode
495 if (WriteMsg(Tag
,Msg
,"PASV") == false)
498 // Unsupported function
499 string::size_type Pos
= Msg
.find('(');
500 if (Tag
>= 400 || Pos
== string::npos
)
504 unsigned a0
,a1
,a2
,a3
,p0
,p1
;
505 if (sscanf(Msg
.c_str() + Pos
,"(%u,%u,%u,%u,%u,%u)",&a0
,&a1
,&a2
,&a3
,&p0
,&p1
) != 6)
508 /* Some evil servers return 0 to mean their addr. We can actually speak
509 to these servers natively using IPv6 */
510 if (a0
== 0 && a1
== 0 && a2
== 0 && a3
== 0)
512 // Get the IP in text form
513 char Name
[NI_MAXHOST
];
514 char Service
[NI_MAXSERV
];
515 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
516 Name
,sizeof(Name
),Service
,sizeof(Service
),
517 NI_NUMERICHOST
|NI_NUMERICSERV
);
519 struct addrinfo Hints
;
520 memset(&Hints
,0,sizeof(Hints
));
521 Hints
.ai_socktype
= SOCK_STREAM
;
522 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
523 Hints
.ai_flags
|= AI_NUMERICHOST
;
525 // Get a new passive address.
527 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
528 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
533 struct addrinfo Hints
;
534 memset(&Hints
,0,sizeof(Hints
));
535 Hints
.ai_socktype
= SOCK_STREAM
;
536 Hints
.ai_family
= AF_INET
;
537 Hints
.ai_flags
|= AI_NUMERICHOST
;
539 // Get a new passive address.
541 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
543 snprintf(Name
,sizeof(Name
),"%u.%u.%u.%u",a0
,a1
,a2
,a3
);
544 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
549 // FTPConn::ExtGoPasv - Enter Extended Passive mode /*{{{*/
550 // ---------------------------------------------------------------------
551 /* Try to enter extended passive mode. See GoPasv above and RFC 2428 */
552 bool FTPConn::ExtGoPasv()
555 freeaddrinfo(PasvAddr
);
558 // Try to enable pasv mode
561 if (WriteMsg(Tag
,Msg
,"EPSV") == false)
564 // Unsupported function
565 string::size_type Pos
= Msg
.find('(');
566 if (Tag
>= 400 || Pos
== string::npos
)
570 string::const_iterator List
[4];
573 for (string::const_iterator I
= Msg
.begin() + Pos
; I
< Msg
.end(); ++I
)
585 unsigned long Proto
= 0;
586 unsigned long Port
= 0;
588 IP
= string(List
[1]+1,List
[2]);
589 Port
= atoi(string(List
[2]+1,List
[3]).c_str());
590 if (IP
.empty() == false)
591 Proto
= atoi(string(List
[0]+1,List
[1]).c_str());
596 // String version of the port
598 snprintf(PStr
,sizeof(PStr
),"%lu",Port
);
600 // Get the IP in text form
601 struct addrinfo Hints
;
602 memset(&Hints
,0,sizeof(Hints
));
603 Hints
.ai_socktype
= SOCK_STREAM
;
604 Hints
.ai_flags
|= AI_NUMERICHOST
;
606 /* The RFC defined case, connect to the old IP/protocol using the
608 if (IP
.empty() == true)
610 // Get the IP in text form
611 char Name
[NI_MAXHOST
];
612 char Service
[NI_MAXSERV
];
613 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
614 Name
,sizeof(Name
),Service
,sizeof(Service
),
615 NI_NUMERICHOST
|NI_NUMERICSERV
);
617 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
623 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
624 if (AFMap
[J
].IETFFamily
== Proto
)
625 Hints
.ai_family
= AFMap
[J
].Family
;
626 if (Hints
.ai_family
== 0)
630 // Get a new passive address.
631 if (getaddrinfo(IP
.c_str(),PStr
,&Hints
,&PasvAddr
) != 0)
637 // FTPConn::Size - Return the size of a file /*{{{*/
638 // ---------------------------------------------------------------------
639 /* Grab the file size from the server, 0 means no size or empty file */
640 bool FTPConn::Size(const char *Path
,unsigned long long &Size
)
646 if (WriteMsg(Tag
,Msg
,"SIZE %s",Path
) == false)
650 Size
= strtoull(Msg
.c_str(),&End
,10);
651 if (Tag
>= 400 || End
== Msg
.c_str())
656 // FTPConn::ModTime - Return the modification time of the file /*{{{*/
657 // ---------------------------------------------------------------------
658 /* Like Size no error is returned if the command is not supported. If the
659 command fails then time is set to the current time of day to fool
661 bool FTPConn::ModTime(const char *Path
, time_t &Time
)
665 // Query the mod time
668 if (WriteMsg(Tag
,Msg
,"MDTM %s",Path
) == false)
670 if (Tag
>= 400 || Msg
.empty() == true || isdigit(Msg
[0]) == 0)
674 return FTPMDTMStrToTime(Msg
.c_str(), Time
);
677 // FTPConn::CreateDataFd - Get a data connection /*{{{*/
678 // ---------------------------------------------------------------------
679 /* Create the data connection. Call FinalizeDataFd after this though.. */
680 bool FTPConn::CreateDataFd()
685 // Attempt to enter passive mode.
686 if (TryPassive
== true)
688 if (GoPasv() == false)
691 // Oops, didn't work out, don't bother trying again.
700 if ((DataFd
= socket(PasvAddr
->ai_family
,PasvAddr
->ai_socktype
,
701 PasvAddr
->ai_protocol
)) < 0)
702 return _error
->Errno("socket",_("Could not create a socket"));
704 // Connect to the server
705 SetNonBlock(DataFd
,true);
706 if (connect(DataFd
,PasvAddr
->ai_addr
,PasvAddr
->ai_addrlen
) < 0 &&
707 errno
!= EINPROGRESS
)
708 return _error
->Errno("socket",_("Could not create a socket"));
710 /* This implements a timeout for connect by opening the connection
712 if (WaitFd(DataFd
,true,TimeOut
) == false)
713 return _error
->Error(_("Could not connect data socket, connection timed out"));
715 unsigned int Len
= sizeof(Err
);
716 if (getsockopt(DataFd
,SOL_SOCKET
,SO_ERROR
,&Err
,&Len
) != 0)
717 return _error
->Errno("getsockopt",_("Failed"));
719 return _error
->Error(_("Could not connect passive socket."));
728 // Get the information for a listening socket.
729 struct addrinfo
*BindAddr
= NULL
;
730 struct addrinfo Hints
;
731 memset(&Hints
,0,sizeof(Hints
));
732 Hints
.ai_socktype
= SOCK_STREAM
;
733 Hints
.ai_flags
|= AI_PASSIVE
;
734 Hints
.ai_family
= ((struct sockaddr
*)&ServerAddr
)->sa_family
;
735 if (getaddrinfo(0,"0",&Hints
,&BindAddr
) != 0 || BindAddr
== NULL
)
736 return _error
->Error(_("getaddrinfo was unable to get a listening socket"));
738 // Construct the socket
739 if ((DataListenFd
= socket(BindAddr
->ai_family
,BindAddr
->ai_socktype
,
740 BindAddr
->ai_protocol
)) < 0)
742 freeaddrinfo(BindAddr
);
743 return _error
->Errno("socket",_("Could not create a socket"));
747 if (bind(DataListenFd
,BindAddr
->ai_addr
,BindAddr
->ai_addrlen
) < 0)
749 freeaddrinfo(BindAddr
);
750 return _error
->Errno("bind",_("Could not bind a socket"));
752 freeaddrinfo(BindAddr
);
753 if (listen(DataListenFd
,1) < 0)
754 return _error
->Errno("listen",_("Could not listen on the socket"));
755 SetNonBlock(DataListenFd
,true);
757 // Determine the name to send to the remote
758 struct sockaddr_storage Addr
;
759 socklen_t AddrLen
= sizeof(Addr
);
760 if (getsockname(DataListenFd
,(sockaddr
*)&Addr
,&AddrLen
) < 0)
761 return _error
->Errno("getsockname",_("Could not determine the socket's name"));
764 // Reverse the address. We need the server address and the data port.
765 char Name
[NI_MAXHOST
];
766 char Service
[NI_MAXSERV
];
767 char Service2
[NI_MAXSERV
];
768 getnameinfo((struct sockaddr
*)&Addr
,AddrLen
,
769 Name
,sizeof(Name
),Service
,sizeof(Service
),
770 NI_NUMERICHOST
|NI_NUMERICSERV
);
771 getnameinfo((struct sockaddr
*)&ServerAddr
,ServerAddrLen
,
772 Name
,sizeof(Name
),Service2
,sizeof(Service2
),
773 NI_NUMERICHOST
|NI_NUMERICSERV
);
775 // Send off an IPv4 address in the old port format
776 if (((struct sockaddr
*)&Addr
)->sa_family
== AF_INET
&&
777 ForceExtended
== false)
779 // Convert the dots in the quad into commas
780 for (char *I
= Name
; *I
!= 0; I
++)
783 unsigned long Port
= atoi(Service
);
785 // Send the port command
788 if (WriteMsg(Tag
,Msg
,"PORT %s,%d,%d",
790 (int)(Port
>> 8) & 0xff, (int)(Port
& 0xff)) == false)
793 return _error
->Error(_("Unable to send PORT command"));
797 // Construct an EPRT command
799 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
800 if (AFMap
[J
].Family
== ((struct sockaddr
*)&Addr
)->sa_family
)
801 Proto
= AFMap
[J
].IETFFamily
;
803 return _error
->Error(_("Unknown address family %u (AF_*)"),
804 ((struct sockaddr
*)&Addr
)->sa_family
);
806 // Send the EPRT command
809 if (WriteMsg(Tag
,Msg
,"EPRT |%u|%s|%s|",Proto
,Name
,Service
) == false)
812 return _error
->Error(_("EPRT failed, server said: %s"),Msg
.c_str());
816 // FTPConn::Finalize - Complete the Data connection /*{{{*/
817 // ---------------------------------------------------------------------
818 /* If the connection is in port mode this waits for the other end to hook
820 bool FTPConn::Finalize()
822 // Passive mode? Do nothing
826 // Close any old socket..
830 // Wait for someone to connect..
831 if (WaitFd(DataListenFd
,false,TimeOut
) == false)
832 return _error
->Error(_("Data socket connect timed out"));
834 // Accept the connection
835 struct sockaddr_in Addr
;
836 socklen_t Len
= sizeof(Addr
);
837 DataFd
= accept(DataListenFd
,(struct sockaddr
*)&Addr
,&Len
);
839 return _error
->Errno("accept",_("Unable to accept connection"));
847 // FTPConn::Get - Get a file /*{{{*/
848 // ---------------------------------------------------------------------
849 /* This opens a data connection, sends REST and RETR and then
850 transfers the file over. */
851 bool FTPConn::Get(const char *Path
,FileFd
&To
,unsigned long long Resume
,
852 Hashes
&Hash
,bool &Missing
, unsigned long long MaximumSize
,
856 if (CreateDataFd() == false)
863 if (WriteMsg(Tag
,Msg
,"REST %u",Resume
) == false)
869 if (To
.Truncate(Resume
) == false)
872 if (To
.Seek(0) == false)
877 if (Hash
.AddFD(To
,Resume
) == false)
879 _error
->Errno("read",_("Problem hashing file"));
884 // Send the get command
885 if (WriteMsg(Tag
,Msg
,"RETR %s",Path
) == false)
892 return _error
->Error(_("Unable to fetch file, server said '%s'"),Msg
.c_str());
895 // Finish off the data connection
896 if (Finalize() == false)
900 unsigned char Buffer
[4096];
903 // Wait for some data..
904 if (WaitFd(DataFd
,false,TimeOut
) == false)
907 return _error
->Error(_("Data socket timed out"));
911 int Res
= read(DataFd
,Buffer
,sizeof(Buffer
));
921 Hash
.Add(Buffer
,Res
);
922 if (To
.Write(Buffer
,Res
) == false)
928 if (MaximumSize
> 0 && To
.Tell() > MaximumSize
)
930 Owner
->SetFailReason("MaximumSizeExceeded");
931 return _error
->Error("Writing more data than expected (%llu > %llu)",
932 To
.Tell(), MaximumSize
);
940 // Read the closing message from the server
941 if (ReadResp(Tag
,Msg
) == false)
944 return _error
->Error(_("Data transfer failed, server said '%s'"),Msg
.c_str());
949 // FtpMethod::FtpMethod - Constructor /*{{{*/
950 // ---------------------------------------------------------------------
952 FtpMethod::FtpMethod() : pkgAcqMethod("1.0",SendConfig
)
954 signal(SIGTERM
,SigTerm
);
955 signal(SIGINT
,SigTerm
);
961 // FtpMethod::SigTerm - Handle a fatal signal /*{{{*/
962 // ---------------------------------------------------------------------
963 /* This closes and timestamps the open file. This is necessary to get
964 resume behavoir on user abort */
965 void FtpMethod::SigTerm(int)
971 struct timeval times
[2];
972 times
[0].tv_sec
= FailTime
;
973 times
[1].tv_sec
= FailTime
;
974 times
[0].tv_usec
= times
[1].tv_usec
= 0;
975 utimes(FailFile
.c_str(), times
);
982 // FtpMethod::Configuration - Handle a configuration message /*{{{*/
983 // ---------------------------------------------------------------------
984 /* We stash the desired pipeline depth */
985 bool FtpMethod::Configuration(string Message
)
987 if (pkgAcqMethod::Configuration(Message
) == false)
990 TimeOut
= _config
->FindI("Acquire::Ftp::Timeout",TimeOut
);
992 // no more active ftp, sorry
998 // FtpMethod::Fetch - Fetch a file /*{{{*/
999 // ---------------------------------------------------------------------
1000 /* Fetch a single file, called by the base class.. */
1001 bool FtpMethod::Fetch(FetchItem
*Itm
)
1004 const char *File
= Get
.Path
.c_str();
1006 Res
.Filename
= Itm
->DestFile
;
1009 maybe_add_auth (Get
, _config
->FindFile("Dir::Etc::netrc"));
1011 // Connect to the server
1012 if (Server
== 0 || Server
->Comp(Get
) == false)
1015 Server
= new FTPConn(Get
);
1018 // Could not connect is a transient error..
1019 if (Server
->Open(this) == false)
1026 // Get the files information
1028 unsigned long long Size
;
1029 if (Server
->Size(File
,Size
) == false ||
1030 Server
->ModTime(File
,FailTime
) == false)
1037 // See if it is an IMS hit
1038 if (Itm
->LastModified
== FailTime
)
1046 // See if the file exists
1048 if (stat(Itm
->DestFile
.c_str(),&Buf
) == 0)
1050 if (Size
== (unsigned long long)Buf
.st_size
&& FailTime
== Buf
.st_mtime
)
1052 Res
.Size
= Buf
.st_size
;
1053 Res
.LastModified
= Buf
.st_mtime
;
1054 Res
.ResumePoint
= Buf
.st_size
;
1060 if (FailTime
== Buf
.st_mtime
&& Size
> (unsigned long long)Buf
.st_size
)
1061 Res
.ResumePoint
= Buf
.st_size
;
1067 FileFd
Fd(Itm
->DestFile
,FileFd::WriteAny
);
1068 if (_error
->PendingError() == true)
1073 FailFile
= Itm
->DestFile
;
1074 FailFile
.c_str(); // Make sure we dont do a malloc in the signal handler
1078 if (Server
->Get(File
,Fd
,Res
.ResumePoint
,Hash
,Missing
,Itm
->MaximumSize
,this) == false)
1083 struct timeval times
[2];
1084 times
[0].tv_sec
= FailTime
;
1085 times
[1].tv_sec
= FailTime
;
1086 times
[0].tv_usec
= times
[1].tv_usec
= 0;
1087 utimes(FailFile
.c_str(), times
);
1089 // If the file is missing we hard fail and delete the destfile
1090 // otherwise transient fail
1091 if (Missing
== true) {
1092 unlink(FailFile
.c_str());
1099 Res
.Size
= Fd
.Size();
1102 struct timeval times
[2];
1103 times
[0].tv_sec
= FailTime
;
1104 times
[1].tv_sec
= FailTime
;
1105 times
[0].tv_usec
= times
[1].tv_usec
= 0;
1106 utimes(Fd
.Name().c_str(), times
);
1110 Res
.LastModified
= FailTime
;
1111 Res
.TakeHashes(Hash
);
1119 int main(int, const char *argv
[])
1121 setlocale(LC_ALL
, "");
1123 /* See if we should be come the http client - we do this for http
1125 if (getenv("ftp_proxy") != 0)
1127 URI Proxy
= string(getenv("ftp_proxy"));
1129 // Run the HTTP method
1130 if (Proxy
.Access
== "http")
1132 // Copy over the environment setting
1134 snprintf(S
,sizeof(S
),"http_proxy=%s",getenv("ftp_proxy"));
1136 putenv((char *)"no_proxy=");
1138 // Run the http method
1139 string Path
= flNotFile(argv
[0]) + "http";
1140 execl(Path
.c_str(),Path
.c_str(),(char *)NULL
);
1141 cerr
<< _("Unable to invoke ") << Path
<< endl
;