]>
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)
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
264 if (ServerName
.Port
!= 0)
265 sprintf(SitePort
,"%u",ServerName
.Port
);
267 strcpy(SitePort
,"21");
268 string Tmp
= Opts
->Value
;
269 Tmp
= SubstVar(Tmp
,"$(PROXY_USER)",Proxy
.User
);
270 Tmp
= SubstVar(Tmp
,"$(PROXY_PASS)",Proxy
.Password
);
271 Tmp
= SubstVar(Tmp
,"$(SITE_USER)",User
);
272 Tmp
= SubstVar(Tmp
,"$(SITE_PASS)",Pass
);
273 Tmp
= SubstVar(Tmp
,"$(SITE_PORT)",SitePort
);
274 Tmp
= SubstVar(Tmp
,"$(SITE)",ServerName
.Host
);
277 if (WriteMsg(Tag
,Msg
,"%s",Tmp
.c_str()) == false)
280 return _error
->Error(_("Login script command '%s' failed, server said: %s"),Tmp
.c_str(),Msg
.c_str());
283 // Enter passive mode
285 if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true)
286 TryPassive
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true);
289 if (_config
->Exists("Acquire::FTP::Proxy::Passive") == true)
290 TryPassive
= _config
->FindB("Acquire::FTP::Proxy::Passive",true);
292 TryPassive
= _config
->FindB("Acquire::FTP::Passive",true);
296 // Force the use of extended commands
297 if (_config
->Exists("Acquire::FTP::ForceExtended::" + ServerName
.Host
) == true)
298 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended::" + ServerName
.Host
,true);
300 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended",false);
303 if (WriteMsg(Tag
,Msg
,"TYPE I") == false)
306 return _error
->Error(_("TYPE failed, server said: %s"),Msg
.c_str());
311 // FTPConn::ReadLine - Read a line from the server /*{{{*/
312 // ---------------------------------------------------------------------
313 /* This performs a very simple buffered read. */
314 bool FTPConn::ReadLine(string
&Text
)
320 while (Len
< sizeof(Buffer
))
322 // Scan the buffer for a new line
323 for (unsigned int I
= 0; I
!= Len
; I
++)
325 // Escape some special chars
330 if (Buffer
[I
] != '\n')
334 Text
= string(Buffer
,I
);
335 memmove(Buffer
,Buffer
+I
,Len
- I
);
340 // Wait for some data..
341 if (WaitFd(ServerFd
,false,TimeOut
) == false)
344 return _error
->Error(_("Connection timeout"));
348 int Res
= read(ServerFd
,Buffer
+ Len
,sizeof(Buffer
) - Len
);
350 _error
->Error(_("Server closed the connection"));
353 _error
->Errno("read",_("Read error"));
360 return _error
->Error(_("A response overflowed the buffer."));
363 // FTPConn::ReadResp - Read a full response from the server /*{{{*/
364 // ---------------------------------------------------------------------
365 /* This reads a reply code from the server, it handles both p */
366 bool FTPConn::ReadResp(unsigned int &Ret
,string
&Text
)
368 // Grab the first line of the response
370 if (ReadLine(Msg
) == false)
375 Ret
= strtol(Msg
.c_str(),&End
,10);
376 if (End
- Msg
.c_str() != 3)
377 return _error
->Error(_("Protocol corruption"));
380 Text
= Msg
.c_str()+4;
384 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
389 return _error
->Error(_("Protocol corruption"));
391 /* Okay, here we do the continued message trick. This is foolish, but
392 proftpd follows the protocol as specified and wu-ftpd doesn't, so
393 we filter. I wonder how many clients break if you use proftpd and
394 put a '- in the 3rd spot in the message? */
396 strncpy(Leader
,Msg
.c_str(),3);
398 while (ReadLine(Msg
) == true)
400 // Short, it must be using RFC continuation..
401 if (Msg
.length() < 4)
408 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == ' ')
410 Text
+= Msg
.c_str()+4;
414 // This message has the wu-ftpd style reply code prefixed
415 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == '-')
417 Text
+= Msg
.c_str()+4;
421 // Must be RFC style prefixing
425 if (Debug
== true && _error
->PendingError() == false)
426 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
428 return !_error
->PendingError();
431 // FTPConn::WriteMsg - Send a message to the server /*{{{*/
432 // ---------------------------------------------------------------------
433 /* Simple printf like function.. */
434 bool FTPConn::WriteMsg(unsigned int &Ret
,string
&Text
,const char *Fmt
,...)
439 // sprintf the description
441 vsnprintf(S
,sizeof(S
) - 4,Fmt
,args
);
446 cerr
<< "-> '" << QuoteString(S
,"") << "'" << endl
;
449 unsigned long Len
= strlen(S
);
450 unsigned long Start
= 0;
453 if (WaitFd(ServerFd
,true,TimeOut
) == false)
456 return _error
->Error(_("Connection timeout"));
459 int Res
= write(ServerFd
,S
+ Start
,Len
);
462 _error
->Errno("write",_("Write error"));
471 return ReadResp(Ret
,Text
);
474 // FTPConn::GoPasv - Enter Passive mode /*{{{*/
475 // ---------------------------------------------------------------------
476 /* Try to enter passive mode, the return code does not indicate if passive
477 mode could or could not be established, only if there was a fatal error.
478 We have to enter passive mode every time we make a data connection :| */
479 bool FTPConn::GoPasv()
481 /* The PASV command only works on IPv4 sockets, even though it could
482 in theory suppory IPv6 via an all zeros reply */
483 if (((struct sockaddr
*)&PeerAddr
)->sa_family
!= AF_INET
||
484 ForceExtended
== true)
488 freeaddrinfo(PasvAddr
);
491 // Try to enable pasv mode
494 if (WriteMsg(Tag
,Msg
,"PASV") == false)
497 // Unsupported function
498 string::size_type Pos
= Msg
.find('(');
499 if (Tag
>= 400 || Pos
== string::npos
)
503 unsigned a0
,a1
,a2
,a3
,p0
,p1
;
504 if (sscanf(Msg
.c_str() + Pos
,"(%u,%u,%u,%u,%u,%u)",&a0
,&a1
,&a2
,&a3
,&p0
,&p1
) != 6)
507 /* Some evil servers return 0 to mean their addr. We can actually speak
508 to these servers natively using IPv6 */
509 if (a0
== 0 && a1
== 0 && a2
== 0 && a3
== 0)
511 // Get the IP in text form
512 char Name
[NI_MAXHOST
];
513 char Service
[NI_MAXSERV
];
514 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
515 Name
,sizeof(Name
),Service
,sizeof(Service
),
516 NI_NUMERICHOST
|NI_NUMERICSERV
);
518 struct addrinfo Hints
;
519 memset(&Hints
,0,sizeof(Hints
));
520 Hints
.ai_socktype
= SOCK_STREAM
;
521 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
522 Hints
.ai_flags
|= AI_NUMERICHOST
;
524 // Get a new passive address.
526 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
527 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
532 struct addrinfo Hints
;
533 memset(&Hints
,0,sizeof(Hints
));
534 Hints
.ai_socktype
= SOCK_STREAM
;
535 Hints
.ai_family
= AF_INET
;
536 Hints
.ai_flags
|= AI_NUMERICHOST
;
538 // Get a new passive address.
540 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
542 snprintf(Name
,sizeof(Name
),"%u.%u.%u.%u",a0
,a1
,a2
,a3
);
543 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
548 // FTPConn::ExtGoPasv - Enter Extended Passive mode /*{{{*/
549 // ---------------------------------------------------------------------
550 /* Try to enter extended passive mode. See GoPasv above and RFC 2428 */
551 bool FTPConn::ExtGoPasv()
554 freeaddrinfo(PasvAddr
);
557 // Try to enable pasv mode
560 if (WriteMsg(Tag
,Msg
,"EPSV") == false)
563 // Unsupported function
564 string::size_type Pos
= Msg
.find('(');
565 if (Tag
>= 400 || Pos
== string::npos
)
569 string::const_iterator List
[4];
572 for (string::const_iterator I
= Msg
.begin() + Pos
; I
< Msg
.end(); ++I
)
584 unsigned long Proto
= 0;
585 unsigned long Port
= 0;
587 IP
= string(List
[1]+1,List
[2]);
588 Port
= atoi(string(List
[2]+1,List
[3]).c_str());
589 if (IP
.empty() == false)
590 Proto
= atoi(string(List
[0]+1,List
[1]).c_str());
595 // String version of the port
597 snprintf(PStr
,sizeof(PStr
),"%lu",Port
);
599 // Get the IP in text form
600 struct addrinfo Hints
;
601 memset(&Hints
,0,sizeof(Hints
));
602 Hints
.ai_socktype
= SOCK_STREAM
;
603 Hints
.ai_flags
|= AI_NUMERICHOST
;
605 /* The RFC defined case, connect to the old IP/protocol using the
607 if (IP
.empty() == true)
609 // Get the IP in text form
610 char Name
[NI_MAXHOST
];
611 char Service
[NI_MAXSERV
];
612 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
613 Name
,sizeof(Name
),Service
,sizeof(Service
),
614 NI_NUMERICHOST
|NI_NUMERICSERV
);
616 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
622 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
623 if (AFMap
[J
].IETFFamily
== Proto
)
624 Hints
.ai_family
= AFMap
[J
].Family
;
625 if (Hints
.ai_family
== 0)
629 // Get a new passive address.
630 if (getaddrinfo(IP
.c_str(),PStr
,&Hints
,&PasvAddr
) != 0)
636 // FTPConn::Size - Return the size of a file /*{{{*/
637 // ---------------------------------------------------------------------
638 /* Grab the file size from the server, 0 means no size or empty file */
639 bool FTPConn::Size(const char *Path
,unsigned long long &Size
)
645 if (WriteMsg(Tag
,Msg
,"SIZE %s",Path
) == false)
649 Size
= strtoull(Msg
.c_str(),&End
,10);
650 if (Tag
>= 400 || End
== Msg
.c_str())
655 // FTPConn::ModTime - Return the modification time of the file /*{{{*/
656 // ---------------------------------------------------------------------
657 /* Like Size no error is returned if the command is not supported. If the
658 command fails then time is set to the current time of day to fool
660 bool FTPConn::ModTime(const char *Path
, time_t &Time
)
664 // Query the mod time
667 if (WriteMsg(Tag
,Msg
,"MDTM %s",Path
) == false)
669 if (Tag
>= 400 || Msg
.empty() == true || isdigit(Msg
[0]) == 0)
673 return FTPMDTMStrToTime(Msg
.c_str(), Time
);
676 // FTPConn::CreateDataFd - Get a data connection /*{{{*/
677 // ---------------------------------------------------------------------
678 /* Create the data connection. Call FinalizeDataFd after this though.. */
679 bool FTPConn::CreateDataFd()
684 // Attempt to enter passive mode.
685 if (TryPassive
== true)
687 if (GoPasv() == false)
690 // Oops, didn't work out, don't bother trying again.
699 if ((DataFd
= socket(PasvAddr
->ai_family
,PasvAddr
->ai_socktype
,
700 PasvAddr
->ai_protocol
)) < 0)
701 return _error
->Errno("socket",_("Could not create a socket"));
703 // Connect to the server
704 SetNonBlock(DataFd
,true);
705 if (connect(DataFd
,PasvAddr
->ai_addr
,PasvAddr
->ai_addrlen
) < 0 &&
706 errno
!= EINPROGRESS
)
707 return _error
->Errno("socket",_("Could not create a socket"));
709 /* This implements a timeout for connect by opening the connection
711 if (WaitFd(DataFd
,true,TimeOut
) == false)
712 return _error
->Error(_("Could not connect data socket, connection timed out"));
714 unsigned int Len
= sizeof(Err
);
715 if (getsockopt(DataFd
,SOL_SOCKET
,SO_ERROR
,&Err
,&Len
) != 0)
716 return _error
->Errno("getsockopt",_("Failed"));
718 return _error
->Error(_("Could not connect passive socket."));
727 // Get the information for a listening socket.
728 struct addrinfo
*BindAddr
= NULL
;
729 struct addrinfo Hints
;
730 memset(&Hints
,0,sizeof(Hints
));
731 Hints
.ai_socktype
= SOCK_STREAM
;
732 Hints
.ai_flags
|= AI_PASSIVE
;
733 Hints
.ai_family
= ((struct sockaddr
*)&ServerAddr
)->sa_family
;
734 if (getaddrinfo(0,"0",&Hints
,&BindAddr
) != 0 || BindAddr
== NULL
)
735 return _error
->Error(_("getaddrinfo was unable to get a listening socket"));
737 // Construct the socket
738 if ((DataListenFd
= socket(BindAddr
->ai_family
,BindAddr
->ai_socktype
,
739 BindAddr
->ai_protocol
)) < 0)
741 freeaddrinfo(BindAddr
);
742 return _error
->Errno("socket",_("Could not create a socket"));
746 if (bind(DataListenFd
,BindAddr
->ai_addr
,BindAddr
->ai_addrlen
) < 0)
748 freeaddrinfo(BindAddr
);
749 return _error
->Errno("bind",_("Could not bind a socket"));
751 freeaddrinfo(BindAddr
);
752 if (listen(DataListenFd
,1) < 0)
753 return _error
->Errno("listen",_("Could not listen on the socket"));
754 SetNonBlock(DataListenFd
,true);
756 // Determine the name to send to the remote
757 struct sockaddr_storage Addr
;
758 socklen_t AddrLen
= sizeof(Addr
);
759 if (getsockname(DataListenFd
,(sockaddr
*)&Addr
,&AddrLen
) < 0)
760 return _error
->Errno("getsockname",_("Could not determine the socket's name"));
763 // Reverse the address. We need the server address and the data port.
764 char Name
[NI_MAXHOST
];
765 char Service
[NI_MAXSERV
];
766 char Service2
[NI_MAXSERV
];
767 getnameinfo((struct sockaddr
*)&Addr
,AddrLen
,
768 Name
,sizeof(Name
),Service
,sizeof(Service
),
769 NI_NUMERICHOST
|NI_NUMERICSERV
);
770 getnameinfo((struct sockaddr
*)&ServerAddr
,ServerAddrLen
,
771 Name
,sizeof(Name
),Service2
,sizeof(Service2
),
772 NI_NUMERICHOST
|NI_NUMERICSERV
);
774 // Send off an IPv4 address in the old port format
775 if (((struct sockaddr
*)&Addr
)->sa_family
== AF_INET
&&
776 ForceExtended
== false)
778 // Convert the dots in the quad into commas
779 for (char *I
= Name
; *I
!= 0; I
++)
782 unsigned long Port
= atoi(Service
);
784 // Send the port command
787 if (WriteMsg(Tag
,Msg
,"PORT %s,%d,%d",
789 (int)(Port
>> 8) & 0xff, (int)(Port
& 0xff)) == false)
792 return _error
->Error(_("Unable to send PORT command"));
796 // Construct an EPRT command
798 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
799 if (AFMap
[J
].Family
== ((struct sockaddr
*)&Addr
)->sa_family
)
800 Proto
= AFMap
[J
].IETFFamily
;
802 return _error
->Error(_("Unknown address family %u (AF_*)"),
803 ((struct sockaddr
*)&Addr
)->sa_family
);
805 // Send the EPRT command
808 if (WriteMsg(Tag
,Msg
,"EPRT |%u|%s|%s|",Proto
,Name
,Service
) == false)
811 return _error
->Error(_("EPRT failed, server said: %s"),Msg
.c_str());
815 // FTPConn::Finalize - Complete the Data connection /*{{{*/
816 // ---------------------------------------------------------------------
817 /* If the connection is in port mode this waits for the other end to hook
819 bool FTPConn::Finalize()
821 // Passive mode? Do nothing
825 // Close any old socket..
829 // Wait for someone to connect..
830 if (WaitFd(DataListenFd
,false,TimeOut
) == false)
831 return _error
->Error(_("Data socket connect timed out"));
833 // Accept the connection
834 struct sockaddr_in Addr
;
835 socklen_t Len
= sizeof(Addr
);
836 DataFd
= accept(DataListenFd
,(struct sockaddr
*)&Addr
,&Len
);
838 return _error
->Errno("accept",_("Unable to accept connection"));
846 // FTPConn::Get - Get a file /*{{{*/
847 // ---------------------------------------------------------------------
848 /* This opens a data connection, sends REST and RETR and then
849 transfers the file over. */
850 bool FTPConn::Get(const char *Path
,FileFd
&To
,unsigned long long Resume
,
851 Hashes
&Hash
,bool &Missing
)
854 if (CreateDataFd() == false)
861 if (WriteMsg(Tag
,Msg
,"REST %u",Resume
) == false)
867 if (To
.Truncate(Resume
) == false)
870 if (To
.Seek(0) == false)
875 if (Hash
.AddFD(To
,Resume
) == false)
877 _error
->Errno("read",_("Problem hashing file"));
882 // Send the get command
883 if (WriteMsg(Tag
,Msg
,"RETR %s",Path
) == false)
890 return _error
->Error(_("Unable to fetch file, server said '%s'"),Msg
.c_str());
893 // Finish off the data connection
894 if (Finalize() == false)
898 unsigned char Buffer
[4096];
901 // Wait for some data..
902 if (WaitFd(DataFd
,false,TimeOut
) == false)
905 return _error
->Error(_("Data socket timed out"));
909 int Res
= read(DataFd
,Buffer
,sizeof(Buffer
));
919 Hash
.Add(Buffer
,Res
);
920 if (To
.Write(Buffer
,Res
) == false)
931 // Read the closing message from the server
932 if (ReadResp(Tag
,Msg
) == false)
935 return _error
->Error(_("Data transfer failed, server said '%s'"),Msg
.c_str());
940 // FtpMethod::FtpMethod - Constructor /*{{{*/
941 // ---------------------------------------------------------------------
943 FtpMethod::FtpMethod() : pkgAcqMethod("1.0",SendConfig
)
945 signal(SIGTERM
,SigTerm
);
946 signal(SIGINT
,SigTerm
);
952 // FtpMethod::SigTerm - Handle a fatal signal /*{{{*/
953 // ---------------------------------------------------------------------
954 /* This closes and timestamps the open file. This is necessary to get
955 resume behavoir on user abort */
956 void FtpMethod::SigTerm(int)
962 struct timeval times
[2];
963 times
[0].tv_sec
= FailTime
;
964 times
[1].tv_sec
= FailTime
;
965 times
[0].tv_usec
= times
[1].tv_usec
= 0;
966 utimes(FailFile
.c_str(), times
);
973 // FtpMethod::Configuration - Handle a configuration message /*{{{*/
974 // ---------------------------------------------------------------------
975 /* We stash the desired pipeline depth */
976 bool FtpMethod::Configuration(string Message
)
978 if (pkgAcqMethod::Configuration(Message
) == false)
981 TimeOut
= _config
->FindI("Acquire::Ftp::Timeout",TimeOut
);
985 // FtpMethod::Fetch - Fetch a file /*{{{*/
986 // ---------------------------------------------------------------------
987 /* Fetch a single file, called by the base class.. */
988 bool FtpMethod::Fetch(FetchItem
*Itm
)
991 const char *File
= Get
.Path
.c_str();
993 Res
.Filename
= Itm
->DestFile
;
996 maybe_add_auth (Get
, _config
->FindFile("Dir::Etc::netrc"));
998 // Connect to the server
999 if (Server
== 0 || Server
->Comp(Get
) == false)
1002 Server
= new FTPConn(Get
);
1005 // Could not connect is a transient error..
1006 if (Server
->Open(this) == false)
1013 // Get the files information
1015 unsigned long long Size
;
1016 if (Server
->Size(File
,Size
) == false ||
1017 Server
->ModTime(File
,FailTime
) == false)
1024 // See if it is an IMS hit
1025 if (Itm
->LastModified
== FailTime
)
1033 // See if the file exists
1035 if (stat(Itm
->DestFile
.c_str(),&Buf
) == 0)
1037 if (Size
== (unsigned long long)Buf
.st_size
&& FailTime
== Buf
.st_mtime
)
1039 Res
.Size
= Buf
.st_size
;
1040 Res
.LastModified
= Buf
.st_mtime
;
1041 Res
.ResumePoint
= Buf
.st_size
;
1047 if (FailTime
== Buf
.st_mtime
&& Size
> (unsigned long long)Buf
.st_size
)
1048 Res
.ResumePoint
= Buf
.st_size
;
1054 FileFd
Fd(Itm
->DestFile
,FileFd::WriteAny
);
1055 if (_error
->PendingError() == true)
1060 FailFile
= Itm
->DestFile
;
1061 FailFile
.c_str(); // Make sure we dont do a malloc in the signal handler
1065 if (Server
->Get(File
,Fd
,Res
.ResumePoint
,Hash
,Missing
) == false)
1070 struct timeval times
[2];
1071 times
[0].tv_sec
= FailTime
;
1072 times
[1].tv_sec
= FailTime
;
1073 times
[0].tv_usec
= times
[1].tv_usec
= 0;
1074 utimes(FailFile
.c_str(), times
);
1076 // If the file is missing we hard fail and delete the destfile
1077 // otherwise transient fail
1078 if (Missing
== true) {
1079 unlink(FailFile
.c_str());
1086 Res
.Size
= Fd
.Size();
1089 struct timeval times
[2];
1090 times
[0].tv_sec
= FailTime
;
1091 times
[1].tv_sec
= FailTime
;
1092 times
[0].tv_usec
= times
[1].tv_usec
= 0;
1093 utimes(Fd
.Name().c_str(), times
);
1097 Res
.LastModified
= FailTime
;
1098 Res
.TakeHashes(Hash
);
1106 int main(int, const char *argv
[])
1108 setlocale(LC_ALL
, "");
1110 /* See if we should be come the http client - we do this for http
1112 if (getenv("ftp_proxy") != 0)
1114 URI Proxy
= string(getenv("ftp_proxy"));
1116 // Run the HTTP method
1117 if (Proxy
.Access
== "http")
1119 // Copy over the environment setting
1121 snprintf(S
,sizeof(S
),"http_proxy=%s",getenv("ftp_proxy"));
1123 putenv((char *)"no_proxy=");
1125 // Run the http method
1126 string Path
= flNotFile(argv
[0]) + "http";
1127 execl(Path
.c_str(),Path
.c_str(),(char *)NULL
);
1128 cerr
<< _("Unable to invoke ") << Path
<< endl
;