]>
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
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 if (ServerName
.Port
!= 0)
271 std::string SitePort
;
272 strprintf(SitePort
, "%u", ServerName
.Port
);
273 Tmp
= SubstVar(Tmp
,"$(SITE_PORT)", SitePort
);
276 Tmp
= SubstVar(Tmp
,"$(SITE_PORT)", "21");
277 Tmp
= SubstVar(Tmp
,"$(SITE)",ServerName
.Host
);
280 if (WriteMsg(Tag
,Msg
,"%s",Tmp
.c_str()) == false)
283 return _error
->Error(_("Login script command '%s' failed, server said: %s"),Tmp
.c_str(),Msg
.c_str());
286 // Enter passive mode
288 if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true)
289 TryPassive
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true);
292 if (_config
->Exists("Acquire::FTP::Proxy::Passive") == true)
293 TryPassive
= _config
->FindB("Acquire::FTP::Proxy::Passive",true);
295 TryPassive
= _config
->FindB("Acquire::FTP::Passive",true);
299 // Force the use of extended commands
300 if (_config
->Exists("Acquire::FTP::ForceExtended::" + ServerName
.Host
) == true)
301 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended::" + ServerName
.Host
,true);
303 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended",false);
306 if (WriteMsg(Tag
,Msg
,"TYPE I") == false)
309 return _error
->Error(_("TYPE failed, server said: %s"),Msg
.c_str());
314 // FTPConn::ReadLine - Read a line from the server /*{{{*/
315 // ---------------------------------------------------------------------
316 /* This performs a very simple buffered read. */
317 bool FTPConn::ReadLine(string
&Text
)
323 while (Len
< sizeof(Buffer
))
325 // Scan the buffer for a new line
326 for (unsigned int I
= 0; I
!= Len
; I
++)
328 // Escape some special chars
333 if (Buffer
[I
] != '\n')
337 Text
= string(Buffer
,I
);
338 memmove(Buffer
,Buffer
+I
,Len
- I
);
343 // Wait for some data..
344 if (WaitFd(ServerFd
,false,TimeOut
) == false)
347 return _error
->Error(_("Connection timeout"));
351 int Res
= read(ServerFd
,Buffer
+ Len
,sizeof(Buffer
) - Len
);
353 _error
->Error(_("Server closed the connection"));
356 _error
->Errno("read",_("Read error"));
363 return _error
->Error(_("A response overflowed the buffer."));
366 // FTPConn::ReadResp - Read a full response from the server /*{{{*/
367 // ---------------------------------------------------------------------
368 /* This reads a reply code from the server, it handles both p */
369 bool FTPConn::ReadResp(unsigned int &Ret
,string
&Text
)
371 // Grab the first line of the response
373 if (ReadLine(Msg
) == false)
378 Ret
= strtol(Msg
.c_str(),&End
,10);
379 if (End
- Msg
.c_str() != 3)
380 return _error
->Error(_("Protocol corruption"));
383 Text
= Msg
.c_str()+4;
387 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
392 return _error
->Error(_("Protocol corruption"));
394 /* Okay, here we do the continued message trick. This is foolish, but
395 proftpd follows the protocol as specified and wu-ftpd doesn't, so
396 we filter. I wonder how many clients break if you use proftpd and
397 put a '- in the 3rd spot in the message? */
399 strncpy(Leader
,Msg
.c_str(),3);
401 while (ReadLine(Msg
) == true)
403 // Short, it must be using RFC continuation..
404 if (Msg
.length() < 4)
411 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == ' ')
413 Text
+= Msg
.c_str()+4;
417 // This message has the wu-ftpd style reply code prefixed
418 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == '-')
420 Text
+= Msg
.c_str()+4;
424 // Must be RFC style prefixing
428 if (Debug
== true && _error
->PendingError() == false)
429 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
431 return !_error
->PendingError();
434 // FTPConn::WriteMsg - Send a message to the server /*{{{*/
435 // ---------------------------------------------------------------------
436 /* Simple printf like function.. */
437 bool FTPConn::WriteMsg(unsigned int &Ret
,string
&Text
,const char *Fmt
,...)
442 // sprintf the description
444 vsnprintf(S
,sizeof(S
) - 4,Fmt
,args
);
449 cerr
<< "-> '" << QuoteString(S
,"") << "'" << endl
;
452 unsigned long Len
= strlen(S
);
453 unsigned long Start
= 0;
456 if (WaitFd(ServerFd
,true,TimeOut
) == false)
459 return _error
->Error(_("Connection timeout"));
462 int Res
= write(ServerFd
,S
+ Start
,Len
);
465 _error
->Errno("write",_("Write error"));
474 return ReadResp(Ret
,Text
);
477 // FTPConn::GoPasv - Enter Passive mode /*{{{*/
478 // ---------------------------------------------------------------------
479 /* Try to enter passive mode, the return code does not indicate if passive
480 mode could or could not be established, only if there was a fatal error.
481 We have to enter passive mode every time we make a data connection :| */
482 bool FTPConn::GoPasv()
484 /* The PASV command only works on IPv4 sockets, even though it could
485 in theory suppory IPv6 via an all zeros reply */
486 if (((struct sockaddr
*)&PeerAddr
)->sa_family
!= AF_INET
||
487 ForceExtended
== true)
491 freeaddrinfo(PasvAddr
);
494 // Try to enable pasv mode
497 if (WriteMsg(Tag
,Msg
,"PASV") == false)
500 // Unsupported function
501 string::size_type Pos
= Msg
.find('(');
502 if (Tag
>= 400 || Pos
== string::npos
)
506 unsigned a0
,a1
,a2
,a3
,p0
,p1
;
507 if (sscanf(Msg
.c_str() + Pos
,"(%u,%u,%u,%u,%u,%u)",&a0
,&a1
,&a2
,&a3
,&p0
,&p1
) != 6)
510 /* Some evil servers return 0 to mean their addr. We can actually speak
511 to these servers natively using IPv6 */
512 if (a0
== 0 && a1
== 0 && a2
== 0 && a3
== 0)
514 // Get the IP in text form
515 char Name
[NI_MAXHOST
];
516 char Service
[NI_MAXSERV
];
517 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
518 Name
,sizeof(Name
),Service
,sizeof(Service
),
519 NI_NUMERICHOST
|NI_NUMERICSERV
);
521 struct addrinfo Hints
;
522 memset(&Hints
,0,sizeof(Hints
));
523 Hints
.ai_socktype
= SOCK_STREAM
;
524 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
525 Hints
.ai_flags
|= AI_NUMERICHOST
;
527 // Get a new passive address.
529 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
530 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
535 struct addrinfo Hints
;
536 memset(&Hints
,0,sizeof(Hints
));
537 Hints
.ai_socktype
= SOCK_STREAM
;
538 Hints
.ai_family
= AF_INET
;
539 Hints
.ai_flags
|= AI_NUMERICHOST
;
541 // Get a new passive address.
543 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
545 snprintf(Name
,sizeof(Name
),"%u.%u.%u.%u",a0
,a1
,a2
,a3
);
546 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
551 // FTPConn::ExtGoPasv - Enter Extended Passive mode /*{{{*/
552 // ---------------------------------------------------------------------
553 /* Try to enter extended passive mode. See GoPasv above and RFC 2428 */
554 bool FTPConn::ExtGoPasv()
557 freeaddrinfo(PasvAddr
);
560 // Try to enable pasv mode
563 if (WriteMsg(Tag
,Msg
,"EPSV") == false)
566 // Unsupported function
567 string::size_type Pos
= Msg
.find('(');
568 if (Tag
>= 400 || Pos
== string::npos
)
572 string::const_iterator List
[4];
575 for (string::const_iterator I
= Msg
.begin() + Pos
; I
< Msg
.end(); ++I
)
587 unsigned long Proto
= 0;
588 unsigned long Port
= 0;
590 IP
= string(List
[1]+1,List
[2]);
591 Port
= atoi(string(List
[2]+1,List
[3]).c_str());
592 if (IP
.empty() == false)
593 Proto
= atoi(string(List
[0]+1,List
[1]).c_str());
598 // String version of the port
600 snprintf(PStr
,sizeof(PStr
),"%lu",Port
);
602 // Get the IP in text form
603 struct addrinfo Hints
;
604 memset(&Hints
,0,sizeof(Hints
));
605 Hints
.ai_socktype
= SOCK_STREAM
;
606 Hints
.ai_flags
|= AI_NUMERICHOST
;
608 /* The RFC defined case, connect to the old IP/protocol using the
610 if (IP
.empty() == true)
612 // Get the IP in text form
613 char Name
[NI_MAXHOST
];
614 char Service
[NI_MAXSERV
];
615 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
616 Name
,sizeof(Name
),Service
,sizeof(Service
),
617 NI_NUMERICHOST
|NI_NUMERICSERV
);
619 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
625 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
626 if (AFMap
[J
].IETFFamily
== Proto
)
627 Hints
.ai_family
= AFMap
[J
].Family
;
628 if (Hints
.ai_family
== 0)
632 // Get a new passive address.
633 if (getaddrinfo(IP
.c_str(),PStr
,&Hints
,&PasvAddr
) != 0)
639 // FTPConn::Size - Return the size of a file /*{{{*/
640 // ---------------------------------------------------------------------
641 /* Grab the file size from the server, 0 means no size or empty file */
642 bool FTPConn::Size(const char *Path
,unsigned long long &Size
)
648 if (WriteMsg(Tag
,Msg
,"SIZE %s",Path
) == false)
652 Size
= strtoull(Msg
.c_str(),&End
,10);
653 if (Tag
>= 400 || End
== Msg
.c_str())
658 // FTPConn::ModTime - Return the modification time of the file /*{{{*/
659 // ---------------------------------------------------------------------
660 /* Like Size no error is returned if the command is not supported. If the
661 command fails then time is set to the current time of day to fool
663 bool FTPConn::ModTime(const char *Path
, time_t &Time
)
667 // Query the mod time
670 if (WriteMsg(Tag
,Msg
,"MDTM %s",Path
) == false)
672 if (Tag
>= 400 || Msg
.empty() == true || isdigit(Msg
[0]) == 0)
676 return FTPMDTMStrToTime(Msg
.c_str(), Time
);
679 // FTPConn::CreateDataFd - Get a data connection /*{{{*/
680 // ---------------------------------------------------------------------
681 /* Create the data connection. Call FinalizeDataFd after this though.. */
682 bool FTPConn::CreateDataFd()
687 // Attempt to enter passive mode.
688 if (TryPassive
== true)
690 if (GoPasv() == false)
693 // Oops, didn't work out, don't bother trying again.
702 if ((DataFd
= socket(PasvAddr
->ai_family
,PasvAddr
->ai_socktype
,
703 PasvAddr
->ai_protocol
)) < 0)
704 return _error
->Errno("socket",_("Could not create a socket"));
706 // Connect to the server
707 SetNonBlock(DataFd
,true);
708 if (connect(DataFd
,PasvAddr
->ai_addr
,PasvAddr
->ai_addrlen
) < 0 &&
709 errno
!= EINPROGRESS
)
710 return _error
->Errno("socket",_("Could not create a socket"));
712 /* This implements a timeout for connect by opening the connection
714 if (WaitFd(DataFd
,true,TimeOut
) == false)
715 return _error
->Error(_("Could not connect data socket, connection timed out"));
717 unsigned int Len
= sizeof(Err
);
718 if (getsockopt(DataFd
,SOL_SOCKET
,SO_ERROR
,&Err
,&Len
) != 0)
719 return _error
->Errno("getsockopt",_("Failed"));
721 return _error
->Error(_("Could not connect passive socket."));
730 // Get the information for a listening socket.
731 struct addrinfo
*BindAddr
= NULL
;
732 struct addrinfo Hints
;
733 memset(&Hints
,0,sizeof(Hints
));
734 Hints
.ai_socktype
= SOCK_STREAM
;
735 Hints
.ai_flags
|= AI_PASSIVE
;
736 Hints
.ai_family
= ((struct sockaddr
*)&ServerAddr
)->sa_family
;
737 if (getaddrinfo(0,"0",&Hints
,&BindAddr
) != 0 || BindAddr
== NULL
)
738 return _error
->Error(_("getaddrinfo was unable to get a listening socket"));
740 // Construct the socket
741 if ((DataListenFd
= socket(BindAddr
->ai_family
,BindAddr
->ai_socktype
,
742 BindAddr
->ai_protocol
)) < 0)
744 freeaddrinfo(BindAddr
);
745 return _error
->Errno("socket",_("Could not create a socket"));
749 if (bind(DataListenFd
,BindAddr
->ai_addr
,BindAddr
->ai_addrlen
) < 0)
751 freeaddrinfo(BindAddr
);
752 return _error
->Errno("bind",_("Could not bind a socket"));
754 freeaddrinfo(BindAddr
);
755 if (listen(DataListenFd
,1) < 0)
756 return _error
->Errno("listen",_("Could not listen on the socket"));
757 SetNonBlock(DataListenFd
,true);
759 // Determine the name to send to the remote
760 struct sockaddr_storage Addr
;
761 socklen_t AddrLen
= sizeof(Addr
);
762 if (getsockname(DataListenFd
,(sockaddr
*)&Addr
,&AddrLen
) < 0)
763 return _error
->Errno("getsockname",_("Could not determine the socket's name"));
766 // Reverse the address. We need the server address and the data port.
767 char Name
[NI_MAXHOST
];
768 char Service
[NI_MAXSERV
];
769 char Service2
[NI_MAXSERV
];
770 getnameinfo((struct sockaddr
*)&Addr
,AddrLen
,
771 Name
,sizeof(Name
),Service
,sizeof(Service
),
772 NI_NUMERICHOST
|NI_NUMERICSERV
);
773 getnameinfo((struct sockaddr
*)&ServerAddr
,ServerAddrLen
,
774 Name
,sizeof(Name
),Service2
,sizeof(Service2
),
775 NI_NUMERICHOST
|NI_NUMERICSERV
);
777 // Send off an IPv4 address in the old port format
778 if (((struct sockaddr
*)&Addr
)->sa_family
== AF_INET
&&
779 ForceExtended
== false)
781 // Convert the dots in the quad into commas
782 for (char *I
= Name
; *I
!= 0; I
++)
785 unsigned long Port
= atoi(Service
);
787 // Send the port command
790 if (WriteMsg(Tag
,Msg
,"PORT %s,%d,%d",
792 (int)(Port
>> 8) & 0xff, (int)(Port
& 0xff)) == false)
795 return _error
->Error(_("Unable to send PORT command"));
799 // Construct an EPRT command
801 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
802 if (AFMap
[J
].Family
== ((struct sockaddr
*)&Addr
)->sa_family
)
803 Proto
= AFMap
[J
].IETFFamily
;
805 return _error
->Error(_("Unknown address family %u (AF_*)"),
806 ((struct sockaddr
*)&Addr
)->sa_family
);
808 // Send the EPRT command
811 if (WriteMsg(Tag
,Msg
,"EPRT |%u|%s|%s|",Proto
,Name
,Service
) == false)
814 return _error
->Error(_("EPRT failed, server said: %s"),Msg
.c_str());
818 // FTPConn::Finalize - Complete the Data connection /*{{{*/
819 // ---------------------------------------------------------------------
820 /* If the connection is in port mode this waits for the other end to hook
822 bool FTPConn::Finalize()
824 // Passive mode? Do nothing
828 // Close any old socket..
832 // Wait for someone to connect..
833 if (WaitFd(DataListenFd
,false,TimeOut
) == false)
834 return _error
->Error(_("Data socket connect timed out"));
836 // Accept the connection
837 struct sockaddr_in Addr
;
838 socklen_t Len
= sizeof(Addr
);
839 DataFd
= accept(DataListenFd
,(struct sockaddr
*)&Addr
,&Len
);
841 return _error
->Errno("accept",_("Unable to accept connection"));
849 // FTPConn::Get - Get a file /*{{{*/
850 // ---------------------------------------------------------------------
851 /* This opens a data connection, sends REST and RETR and then
852 transfers the file over. */
853 bool FTPConn::Get(const char *Path
,FileFd
&To
,unsigned long long Resume
,
854 Hashes
&Hash
,bool &Missing
, unsigned long long MaximumSize
,
858 if (CreateDataFd() == false)
865 if (WriteMsg(Tag
,Msg
,"REST %u",Resume
) == false)
871 if (To
.Truncate(Resume
) == false)
874 if (To
.Seek(0) == false)
879 if (Hash
.AddFD(To
,Resume
) == false)
881 _error
->Errno("read",_("Problem hashing file"));
886 // Send the get command
887 if (WriteMsg(Tag
,Msg
,"RETR %s",Path
) == false)
894 return _error
->Error(_("Unable to fetch file, server said '%s'"),Msg
.c_str());
897 // Finish off the data connection
898 if (Finalize() == false)
902 unsigned char Buffer
[4096];
905 // Wait for some data..
906 if (WaitFd(DataFd
,false,TimeOut
) == false)
909 return _error
->Error(_("Data socket timed out"));
913 int Res
= read(DataFd
,Buffer
,sizeof(Buffer
));
923 Hash
.Add(Buffer
,Res
);
924 if (To
.Write(Buffer
,Res
) == false)
930 if (MaximumSize
> 0 && To
.Tell() > MaximumSize
)
932 Owner
->SetFailReason("MaximumSizeExceeded");
933 return _error
->Error("Writing more data than expected (%llu > %llu)",
934 To
.Tell(), MaximumSize
);
942 // Read the closing message from the server
943 if (ReadResp(Tag
,Msg
) == false)
946 return _error
->Error(_("Data transfer failed, server said '%s'"),Msg
.c_str());
951 // FtpMethod::FtpMethod - Constructor /*{{{*/
952 // ---------------------------------------------------------------------
954 FtpMethod::FtpMethod() : pkgAcqMethod("1.0",SendConfig
)
956 signal(SIGTERM
,SigTerm
);
957 signal(SIGINT
,SigTerm
);
963 // FtpMethod::SigTerm - Handle a fatal signal /*{{{*/
964 // ---------------------------------------------------------------------
965 /* This closes and timestamps the open file. This is necessary to get
966 resume behavoir on user abort */
967 void FtpMethod::SigTerm(int)
973 struct timeval times
[2];
974 times
[0].tv_sec
= FailTime
;
975 times
[1].tv_sec
= FailTime
;
976 times
[0].tv_usec
= times
[1].tv_usec
= 0;
977 utimes(FailFile
.c_str(), times
);
984 // FtpMethod::Configuration - Handle a configuration message /*{{{*/
985 // ---------------------------------------------------------------------
986 /* We stash the desired pipeline depth */
987 bool FtpMethod::Configuration(string Message
)
989 if (pkgAcqMethod::Configuration(Message
) == false)
992 TimeOut
= _config
->FindI("Acquire::Ftp::Timeout",TimeOut
);
994 // no more active ftp, sorry
1000 // FtpMethod::Fetch - Fetch a file /*{{{*/
1001 // ---------------------------------------------------------------------
1002 /* Fetch a single file, called by the base class.. */
1003 bool FtpMethod::Fetch(FetchItem
*Itm
)
1006 const char *File
= Get
.Path
.c_str();
1008 Res
.Filename
= Itm
->DestFile
;
1011 maybe_add_auth (Get
, _config
->FindFile("Dir::Etc::netrc"));
1013 // Connect to the server
1014 if (Server
== 0 || Server
->Comp(Get
) == false)
1017 Server
= new FTPConn(Get
);
1020 // Could not connect is a transient error..
1021 if (Server
->Open(this) == false)
1028 // Get the files information
1030 unsigned long long Size
;
1031 if (Server
->Size(File
,Size
) == false ||
1032 Server
->ModTime(File
,FailTime
) == false)
1039 // See if it is an IMS hit
1040 if (Itm
->LastModified
== FailTime
)
1048 // See if the file exists
1050 if (stat(Itm
->DestFile
.c_str(),&Buf
) == 0)
1052 if (Size
== (unsigned long long)Buf
.st_size
&& FailTime
== Buf
.st_mtime
)
1054 Res
.Size
= Buf
.st_size
;
1055 Res
.LastModified
= Buf
.st_mtime
;
1056 Res
.ResumePoint
= Buf
.st_size
;
1062 if (FailTime
== Buf
.st_mtime
&& Size
> (unsigned long long)Buf
.st_size
)
1063 Res
.ResumePoint
= Buf
.st_size
;
1069 FileFd
Fd(Itm
->DestFile
,FileFd::WriteAny
);
1070 if (_error
->PendingError() == true)
1075 FailFile
= Itm
->DestFile
;
1076 FailFile
.c_str(); // Make sure we dont do a malloc in the signal handler
1080 if (Server
->Get(File
,Fd
,Res
.ResumePoint
,Hash
,Missing
,Itm
->MaximumSize
,this) == false)
1085 struct timeval times
[2];
1086 times
[0].tv_sec
= FailTime
;
1087 times
[1].tv_sec
= FailTime
;
1088 times
[0].tv_usec
= times
[1].tv_usec
= 0;
1089 utimes(FailFile
.c_str(), times
);
1091 // If the file is missing we hard fail and delete the destfile
1092 // otherwise transient fail
1093 if (Missing
== true) {
1094 unlink(FailFile
.c_str());
1101 Res
.Size
= Fd
.Size();
1104 struct timeval times
[2];
1105 times
[0].tv_sec
= FailTime
;
1106 times
[1].tv_sec
= FailTime
;
1107 times
[0].tv_usec
= times
[1].tv_usec
= 0;
1108 utimes(Fd
.Name().c_str(), times
);
1112 Res
.LastModified
= FailTime
;
1113 Res
.TakeHashes(Hash
);
1121 int main(int, const char *argv
[])
1123 setlocale(LC_ALL
, "");
1125 /* See if we should be come the http client - we do this for http
1127 if (getenv("ftp_proxy") != 0)
1129 URI Proxy
= string(getenv("ftp_proxy"));
1131 // Run the HTTP method
1132 if (Proxy
.Access
== "http")
1134 // Copy over the environment setting
1136 snprintf(S
,sizeof(S
),"http_proxy=%s",getenv("ftp_proxy"));
1138 putenv((char *)"no_proxy=");
1140 // Run the http method
1141 string Path
= flNotFile(argv
[0]) + "http";
1142 execl(Path
.c_str(),Path
.c_str(),(char *)NULL
);
1143 cerr
<< _("Unable to invoke ") << Path
<< endl
;