]>
git.saurik.com Git - apt.git/blob - methods/ftp.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: ftp.cc,v 1.30 2003/02/10 07:34:41 doogie Exp $
4 /* ######################################################################
6 FTP Aquire Method - This is the FTP aquire method for APT.
8 This is a very simple implementation that does not try to optimize
9 at all. Commands are sent syncronously with the FTP server (as the
10 rfc recommends, but it is not really necessary..) and no tricks are
11 done to speed things along.
13 RFC 2428 describes the IPv6 FTP behavior
15 ##################################################################### */
17 // Include Files /*{{{*/
19 #include <apt-pkg/fileutl.h>
20 #include <apt-pkg/acquire-method.h>
21 #include <apt-pkg/error.h>
22 #include <apt-pkg/hashes.h>
35 #include <netinet/in.h>
36 #include <sys/socket.h>
37 #include <arpa/inet.h>
40 #include "rfc2553emu.h"
47 /* This table is for the EPRT and EPSV commands, it maps the OS address
48 family to the IETF address families */
52 unsigned long IETFFamily
;
56 struct AFMap AFMap
[] = {{AF_INET
,1},{}};
58 struct AFMap AFMap
[] = {{AF_INET
,1},{AF_INET6
,2},{}};
61 unsigned long TimeOut
= 120;
63 string
FtpMethod::FailFile
;
64 int FtpMethod::FailFd
= -1;
65 time_t FtpMethod::FailTime
= 0;
67 // FTPConn::FTPConn - Constructor /*{{{*/
68 // ---------------------------------------------------------------------
70 FTPConn::FTPConn(URI Srv
) : Len(0), ServerFd(-1), DataFd(-1),
71 DataListenFd(-1), ServerName(Srv
)
73 Debug
= _config
->FindB("Debug::Acquire::Ftp",false);
77 // FTPConn::~FTPConn - Destructor /*{{{*/
78 // ---------------------------------------------------------------------
85 // FTPConn::Close - Close down the connection /*{{{*/
86 // ---------------------------------------------------------------------
87 /* Just tear down the socket and data socket */
98 freeaddrinfo(PasvAddr
);
102 // FTPConn::Open - Open a new connection /*{{{*/
103 // ---------------------------------------------------------------------
104 /* Connect to the server using a non-blocking connection and perform a
106 bool FTPConn::Open(pkgAcqMethod
*Owner
)
108 // Use the already open connection if possible.
114 // Determine the proxy setting
115 if (getenv("ftp_proxy") == 0)
117 string DefProxy
= _config
->Find("Acquire::ftp::Proxy");
118 string SpecificProxy
= _config
->Find("Acquire::ftp::Proxy::" + ServerName
.Host
);
119 if (SpecificProxy
.empty() == false)
121 if (SpecificProxy
== "DIRECT")
124 Proxy
= SpecificProxy
;
130 Proxy
= getenv("ftp_proxy");
132 // Parse no_proxy, a , separated list of domains
133 if (getenv("no_proxy") != 0)
135 if (CheckDomainList(ServerName
.Host
,getenv("no_proxy")) == true)
139 // Determine what host and port to use based on the proxy settings
142 if (Proxy
.empty() == true)
144 if (ServerName
.Port
!= 0)
145 Port
= ServerName
.Port
;
146 Host
= ServerName
.Host
;
155 /* Connect to the remote server. Since FTP is connection oriented we
156 want to make sure we get a new server every time we reconnect */
158 if (Connect(Host
,Port
,"ftp",21,ServerFd
,TimeOut
,Owner
) == false)
161 // Login must be before getpeername otherwise dante won't work.
162 Owner
->Status(_("Logging in"));
165 // Get the remote server's address
166 PeerAddrLen
= sizeof(PeerAddr
);
167 if (getpeername(ServerFd
,(sockaddr
*)&PeerAddr
,&PeerAddrLen
) != 0)
168 return _error
->Errno("getpeername",_("Unable to determine the peer name"));
170 // Get the local machine's address
171 ServerAddrLen
= sizeof(ServerAddr
);
172 if (getsockname(ServerFd
,(sockaddr
*)&ServerAddr
,&ServerAddrLen
) != 0)
173 return _error
->Errno("getsockname",_("Unable to determine the local name"));
178 // FTPConn::Login - Login to the remote server /*{{{*/
179 // ---------------------------------------------------------------------
180 /* This performs both normal login and proxy login using a simples script
181 stored in the config file. */
182 bool FTPConn::Login()
187 // Setup the variables needed for authentication
188 string User
= "anonymous";
189 string Pass
= "apt_get_ftp_2.1@debian.linux.user";
191 // Fill in the user/pass
192 if (ServerName
.User
.empty() == false)
193 User
= ServerName
.User
;
194 if (ServerName
.Password
.empty() == false)
195 Pass
= ServerName
.Password
;
197 // Perform simple login
198 if (Proxy
.empty() == true)
200 // Read the initial response
201 if (ReadResp(Tag
,Msg
) == false)
204 return _error
->Error(_("Server refused our connection and said: %s"),Msg
.c_str());
207 if (WriteMsg(Tag
,Msg
,"USER %s",User
.c_str()) == false)
210 return _error
->Error(_("USER failed, server said: %s"),Msg
.c_str());
213 if (WriteMsg(Tag
,Msg
,"PASS %s",Pass
.c_str()) == false)
216 return _error
->Error(_("PASS failed, server said: %s"),Msg
.c_str());
218 // Enter passive mode
219 if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true)
220 TryPassive
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true);
222 TryPassive
= _config
->FindB("Acquire::FTP::Passive",true);
226 // Read the initial response
227 if (ReadResp(Tag
,Msg
) == false)
230 return _error
->Error(_("Server refused our connection and said: %s"),Msg
.c_str());
232 // Perform proxy script execution
233 Configuration::Item
const *Opts
= _config
->Tree("Acquire::ftp::ProxyLogin");
234 if (Opts
== 0 || Opts
->Child
== 0)
235 return _error
->Error(_("A proxy server was specified but no login "
236 "script, Acquire::ftp::ProxyLogin is empty."));
239 // Iterate over the entire login script
240 for (; Opts
!= 0; Opts
= Opts
->Next
)
242 if (Opts
->Value
.empty() == true)
245 // Substitute the variables into the command
247 if (ServerName
.Port
!= 0)
248 sprintf(SitePort
,"%u",ServerName
.Port
);
250 strcpy(SitePort
,"21");
251 string Tmp
= Opts
->Value
;
252 Tmp
= SubstVar(Tmp
,"$(PROXY_USER)",Proxy
.User
);
253 Tmp
= SubstVar(Tmp
,"$(PROXY_PASS)",Proxy
.Password
);
254 Tmp
= SubstVar(Tmp
,"$(SITE_USER)",User
);
255 Tmp
= SubstVar(Tmp
,"$(SITE_PASS)",Pass
);
256 Tmp
= SubstVar(Tmp
,"$(SITE_PORT)",SitePort
);
257 Tmp
= SubstVar(Tmp
,"$(SITE)",ServerName
.Host
);
260 if (WriteMsg(Tag
,Msg
,"%s",Tmp
.c_str()) == false)
263 return _error
->Error(_("Login script command '%s' failed, server said: %s"),Tmp
.c_str(),Msg
.c_str());
266 // Enter passive mode
268 if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true)
269 TryPassive
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true);
272 if (_config
->Exists("Acquire::FTP::Proxy::Passive") == true)
273 TryPassive
= _config
->FindB("Acquire::FTP::Proxy::Passive",true);
275 TryPassive
= _config
->FindB("Acquire::FTP::Passive",true);
279 // Force the use of extended commands
280 if (_config
->Exists("Acquire::FTP::ForceExtended::" + ServerName
.Host
) == true)
281 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended::" + ServerName
.Host
,true);
283 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended",false);
286 if (WriteMsg(Tag
,Msg
,"TYPE I") == false)
289 return _error
->Error(_("TYPE failed, server said: %s"),Msg
.c_str());
294 // FTPConn::ReadLine - Read a line from the server /*{{{*/
295 // ---------------------------------------------------------------------
296 /* This performs a very simple buffered read. */
297 bool FTPConn::ReadLine(string
&Text
)
303 while (Len
< sizeof(Buffer
))
305 // Scan the buffer for a new line
306 for (unsigned int I
= 0; I
!= Len
; I
++)
308 // Escape some special chars
313 if (Buffer
[I
] != '\n')
317 Text
= string(Buffer
,I
);
318 memmove(Buffer
,Buffer
+I
,Len
- I
);
323 // Wait for some data..
324 if (WaitFd(ServerFd
,false,TimeOut
) == false)
327 return _error
->Error(_("Connection timeout"));
331 int Res
= read(ServerFd
,Buffer
+ Len
,sizeof(Buffer
) - Len
);
333 _error
->Error(_("Server closed the connection"));
336 _error
->Errno("read",_("Read error"));
343 return _error
->Error(_("A response overflowed the buffer."));
346 // FTPConn::ReadResp - Read a full response from the server /*{{{*/
347 // ---------------------------------------------------------------------
348 /* This reads a reply code from the server, it handles both p */
349 bool FTPConn::ReadResp(unsigned int &Ret
,string
&Text
)
351 // Grab the first line of the response
353 if (ReadLine(Msg
) == false)
358 Ret
= strtol(Msg
.c_str(),&End
,10);
359 if (End
- Msg
.c_str() != 3)
360 return _error
->Error(_("Protocol corruption"));
363 Text
= Msg
.c_str()+4;
367 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
372 return _error
->Error(_("Protocol corruption"));
374 /* Okay, here we do the continued message trick. This is foolish, but
375 proftpd follows the protocol as specified and wu-ftpd doesn't, so
376 we filter. I wonder how many clients break if you use proftpd and
377 put a '- in the 3rd spot in the message? */
379 strncpy(Leader
,Msg
.c_str(),3);
381 while (ReadLine(Msg
) == true)
383 // Short, it must be using RFC continuation..
384 if (Msg
.length() < 4)
391 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == ' ')
393 Text
+= Msg
.c_str()+4;
397 // This message has the wu-ftpd style reply code prefixed
398 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == '-')
400 Text
+= Msg
.c_str()+4;
404 // Must be RFC style prefixing
408 if (Debug
== true && _error
->PendingError() == false)
409 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
411 return !_error
->PendingError();
414 // FTPConn::WriteMsg - Send a message to the server /*{{{*/
415 // ---------------------------------------------------------------------
416 /* Simple printf like function.. */
417 bool FTPConn::WriteMsg(unsigned int &Ret
,string
&Text
,const char *Fmt
,...)
422 // sprintf the description
424 vsnprintf(S
,sizeof(S
) - 4,Fmt
,args
);
428 cerr
<< "-> '" << QuoteString(S
,"") << "'" << endl
;
431 unsigned long Len
= strlen(S
);
432 unsigned long Start
= 0;
435 if (WaitFd(ServerFd
,true,TimeOut
) == false)
438 return _error
->Error(_("Connection timeout"));
441 int Res
= write(ServerFd
,S
+ Start
,Len
);
444 _error
->Errno("write",_("Write Error"));
453 return ReadResp(Ret
,Text
);
456 // FTPConn::GoPasv - Enter Passive mode /*{{{*/
457 // ---------------------------------------------------------------------
458 /* Try to enter passive mode, the return code does not indicate if passive
459 mode could or could not be established, only if there was a fatal error.
460 We have to enter passive mode every time we make a data connection :| */
461 bool FTPConn::GoPasv()
463 /* The PASV command only works on IPv4 sockets, even though it could
464 in theory suppory IPv6 via an all zeros reply */
465 if (((struct sockaddr
*)&PeerAddr
)->sa_family
!= AF_INET
||
466 ForceExtended
== true)
470 freeaddrinfo(PasvAddr
);
473 // Try to enable pasv mode
476 if (WriteMsg(Tag
,Msg
,"PASV") == false)
479 // Unsupported function
480 string::size_type Pos
= Msg
.find('(');
481 if (Tag
>= 400 || Pos
== string::npos
)
485 unsigned a0
,a1
,a2
,a3
,p0
,p1
;
486 if (sscanf(Msg
.c_str() + Pos
,"(%u,%u,%u,%u,%u,%u)",&a0
,&a1
,&a2
,&a3
,&p0
,&p1
) != 6)
489 /* Some evil servers return 0 to mean their addr. We can actually speak
490 to these servers natively using IPv6 */
491 if (a0
== 0 && a1
== 0 && a2
== 0 && a3
== 0)
493 // Get the IP in text form
494 char Name
[NI_MAXHOST
];
495 char Service
[NI_MAXSERV
];
496 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
497 Name
,sizeof(Name
),Service
,sizeof(Service
),
498 NI_NUMERICHOST
|NI_NUMERICSERV
);
500 struct addrinfo Hints
;
501 memset(&Hints
,0,sizeof(Hints
));
502 Hints
.ai_socktype
= SOCK_STREAM
;
503 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
504 Hints
.ai_flags
|= AI_NUMERICHOST
;
506 // Get a new passive address.
508 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
509 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
514 struct addrinfo Hints
;
515 memset(&Hints
,0,sizeof(Hints
));
516 Hints
.ai_socktype
= SOCK_STREAM
;
517 Hints
.ai_family
= AF_INET
;
518 Hints
.ai_flags
|= AI_NUMERICHOST
;
520 // Get a new passive address.
522 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
524 snprintf(Name
,sizeof(Name
),"%u.%u.%u.%u",a0
,a1
,a2
,a3
);
525 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
530 // FTPConn::ExtGoPasv - Enter Extended Passive mode /*{{{*/
531 // ---------------------------------------------------------------------
532 /* Try to enter extended passive mode. See GoPasv above and RFC 2428 */
533 bool FTPConn::ExtGoPasv()
536 freeaddrinfo(PasvAddr
);
539 // Try to enable pasv mode
542 if (WriteMsg(Tag
,Msg
,"EPSV") == false)
545 // Unsupported function
546 string::size_type Pos
= Msg
.find('(');
547 if (Tag
>= 400 || Pos
== string::npos
)
551 string::const_iterator List
[4];
554 for (string::const_iterator I
= Msg
.begin() + Pos
; I
< Msg
.end(); I
++)
566 unsigned long Proto
= 0;
567 unsigned long Port
= 0;
569 IP
= string(List
[1]+1,List
[2]);
570 Port
= atoi(string(List
[2]+1,List
[3]).c_str());
571 if (IP
.empty() == false)
572 Proto
= atoi(string(List
[0]+1,List
[1]).c_str());
577 // String version of the port
579 snprintf(PStr
,sizeof(PStr
),"%lu",Port
);
581 // Get the IP in text form
582 struct addrinfo Hints
;
583 memset(&Hints
,0,sizeof(Hints
));
584 Hints
.ai_socktype
= SOCK_STREAM
;
585 Hints
.ai_flags
|= AI_NUMERICHOST
;
587 /* The RFC defined case, connect to the old IP/protocol using the
589 if (IP
.empty() == true)
591 // Get the IP in text form
592 char Name
[NI_MAXHOST
];
593 char Service
[NI_MAXSERV
];
594 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
595 Name
,sizeof(Name
),Service
,sizeof(Service
),
596 NI_NUMERICHOST
|NI_NUMERICSERV
);
598 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
604 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
605 if (AFMap
[J
].IETFFamily
== Proto
)
606 Hints
.ai_family
= AFMap
[J
].Family
;
607 if (Hints
.ai_family
== 0)
611 // Get a new passive address.
613 if ((Res
= getaddrinfo(IP
.c_str(),PStr
,&Hints
,&PasvAddr
)) != 0)
619 // FTPConn::Size - Return the size of a file /*{{{*/
620 // ---------------------------------------------------------------------
621 /* Grab the file size from the server, 0 means no size or empty file */
622 bool FTPConn::Size(const char *Path
,unsigned long &Size
)
628 if (WriteMsg(Tag
,Msg
,"SIZE %s",Path
) == false)
632 Size
= strtol(Msg
.c_str(),&End
,10);
633 if (Tag
>= 400 || End
== Msg
.c_str())
638 // FTPConn::ModTime - Return the modification time of the file /*{{{*/
639 // ---------------------------------------------------------------------
640 /* Like Size no error is returned if the command is not supported. If the
641 command fails then time is set to the current time of day to fool
643 bool FTPConn::ModTime(const char *Path
, time_t &Time
)
647 // Query the mod time
650 if (WriteMsg(Tag
,Msg
,"MDTM %s",Path
) == false)
652 if (Tag
>= 400 || Msg
.empty() == true || isdigit(Msg
[0]) == 0)
660 // FTPConn::CreateDataFd - Get a data connection /*{{{*/
661 // ---------------------------------------------------------------------
662 /* Create the data connection. Call FinalizeDataFd after this though.. */
663 bool FTPConn::CreateDataFd()
668 // Attempt to enter passive mode.
669 if (TryPassive
== true)
671 if (GoPasv() == false)
674 // Oops, didn't work out, don't bother trying again.
683 if ((DataFd
= socket(PasvAddr
->ai_family
,PasvAddr
->ai_socktype
,
684 PasvAddr
->ai_protocol
)) < 0)
685 return _error
->Errno("socket",_("Could not create a socket"));
687 // Connect to the server
688 SetNonBlock(DataFd
,true);
689 if (connect(DataFd
,PasvAddr
->ai_addr
,PasvAddr
->ai_addrlen
) < 0 &&
690 errno
!= EINPROGRESS
)
691 return _error
->Errno("socket",_("Could not create a socket"));
693 /* This implements a timeout for connect by opening the connection
695 if (WaitFd(DataFd
,true,TimeOut
) == false)
696 return _error
->Error(_("Could not connect data socket, connection timed out"));
698 unsigned int Len
= sizeof(Err
);
699 if (getsockopt(DataFd
,SOL_SOCKET
,SO_ERROR
,&Err
,&Len
) != 0)
700 return _error
->Errno("getsockopt",_("Failed"));
702 return _error
->Error(_("Could not connect passive socket."));
711 // Get the information for a listening socket.
712 struct addrinfo
*BindAddr
= 0;
713 struct addrinfo Hints
;
714 memset(&Hints
,0,sizeof(Hints
));
715 Hints
.ai_socktype
= SOCK_STREAM
;
716 Hints
.ai_flags
|= AI_PASSIVE
;
717 Hints
.ai_family
= ((struct sockaddr
*)&ServerAddr
)->sa_family
;
719 if ((Res
= getaddrinfo(0,"0",&Hints
,&BindAddr
)) != 0)
720 return _error
->Error(_("getaddrinfo was unable to get a listening socket"));
722 // Construct the socket
723 if ((DataListenFd
= socket(BindAddr
->ai_family
,BindAddr
->ai_socktype
,
724 BindAddr
->ai_protocol
)) < 0)
726 freeaddrinfo(BindAddr
);
727 return _error
->Errno("socket",_("Could not create a socket"));
731 if (bind(DataListenFd
,BindAddr
->ai_addr
,BindAddr
->ai_addrlen
) < 0)
733 freeaddrinfo(BindAddr
);
734 return _error
->Errno("bind",_("Could not bind a socket"));
736 freeaddrinfo(BindAddr
);
737 if (listen(DataListenFd
,1) < 0)
738 return _error
->Errno("listen",_("Could not listen on the socket"));
739 SetNonBlock(DataListenFd
,true);
741 // Determine the name to send to the remote
742 struct sockaddr_storage Addr
;
743 socklen_t AddrLen
= sizeof(Addr
);
744 if (getsockname(DataListenFd
,(sockaddr
*)&Addr
,&AddrLen
) < 0)
745 return _error
->Errno("getsockname",_("Could not determine the socket's name"));
748 // Reverse the address. We need the server address and the data port.
749 char Name
[NI_MAXHOST
];
750 char Service
[NI_MAXSERV
];
751 char Service2
[NI_MAXSERV
];
752 getnameinfo((struct sockaddr
*)&Addr
,AddrLen
,
753 Name
,sizeof(Name
),Service
,sizeof(Service
),
754 NI_NUMERICHOST
|NI_NUMERICSERV
);
755 getnameinfo((struct sockaddr
*)&ServerAddr
,ServerAddrLen
,
756 Name
,sizeof(Name
),Service2
,sizeof(Service2
),
757 NI_NUMERICHOST
|NI_NUMERICSERV
);
759 // Send off an IPv4 address in the old port format
760 if (((struct sockaddr
*)&Addr
)->sa_family
== AF_INET
&&
761 ForceExtended
== false)
763 // Convert the dots in the quad into commas
764 for (char *I
= Name
; *I
!= 0; I
++)
767 unsigned long Port
= atoi(Service
);
769 // Send the port command
772 if (WriteMsg(Tag
,Msg
,"PORT %s,%d,%d",
774 (int)(Port
>> 8) & 0xff, (int)(Port
& 0xff)) == false)
777 return _error
->Error(_("Unable to send PORT command"));
781 // Construct an EPRT command
783 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
784 if (AFMap
[J
].Family
== ((struct sockaddr
*)&Addr
)->sa_family
)
785 Proto
= AFMap
[J
].IETFFamily
;
787 return _error
->Error(_("Unknown address family %u (AF_*)"),
788 ((struct sockaddr
*)&Addr
)->sa_family
);
790 // Send the EPRT command
793 if (WriteMsg(Tag
,Msg
,"EPRT |%u|%s|%s|",Proto
,Name
,Service
) == false)
796 return _error
->Error(_("EPRT failed, server said: %s"),Msg
.c_str());
800 // FTPConn::Finalize - Complete the Data connection /*{{{*/
801 // ---------------------------------------------------------------------
802 /* If the connection is in port mode this waits for the other end to hook
804 bool FTPConn::Finalize()
806 // Passive mode? Do nothing
810 // Close any old socket..
814 // Wait for someone to connect..
815 if (WaitFd(DataListenFd
,false,TimeOut
) == false)
816 return _error
->Error(_("Data socket connect timed out"));
818 // Accept the connection
819 struct sockaddr_in Addr
;
820 socklen_t Len
= sizeof(Addr
);
821 DataFd
= accept(DataListenFd
,(struct sockaddr
*)&Addr
,&Len
);
823 return _error
->Errno("accept",_("Unable to accept connection"));
831 // FTPConn::Get - Get a file /*{{{*/
832 // ---------------------------------------------------------------------
833 /* This opens a data connection, sends REST and RETR and then
834 transfers the file over. */
835 bool FTPConn::Get(const char *Path
,FileFd
&To
,unsigned long Resume
,
836 Hashes
&Hash
,bool &Missing
)
839 if (CreateDataFd() == false)
846 if (WriteMsg(Tag
,Msg
,"REST %u",Resume
) == false)
852 if (To
.Truncate(Resume
) == false)
855 if (To
.Seek(0) == false)
860 if (Hash
.AddFD(To
.Fd(),Resume
) == false)
862 _error
->Errno("read",_("Problem hashing file"));
867 // Send the get command
868 if (WriteMsg(Tag
,Msg
,"RETR %s",Path
) == false)
875 return _error
->Error(_("Unable to fetch file, server said '%s'"),Msg
.c_str());
878 // Finish off the data connection
879 if (Finalize() == false)
883 unsigned char Buffer
[4096];
886 // Wait for some data..
887 if (WaitFd(DataFd
,false,TimeOut
) == false)
890 return _error
->Error(_("Data socket timed out"));
894 int Res
= read(DataFd
,Buffer
,sizeof(Buffer
));
904 Hash
.Add(Buffer
,Res
);
905 if (To
.Write(Buffer
,Res
) == false)
916 // Read the closing message from the server
917 if (ReadResp(Tag
,Msg
) == false)
920 return _error
->Error(_("Data transfer failed, server said '%s'"),Msg
.c_str());
925 // FtpMethod::FtpMethod - Constructor /*{{{*/
926 // ---------------------------------------------------------------------
928 FtpMethod::FtpMethod() : pkgAcqMethod("1.0",SendConfig
)
930 signal(SIGTERM
,SigTerm
);
931 signal(SIGINT
,SigTerm
);
937 // FtpMethod::SigTerm - Handle a fatal signal /*{{{*/
938 // ---------------------------------------------------------------------
939 /* This closes and timestamps the open file. This is neccessary to get
940 resume behavoir on user abort */
941 void FtpMethod::SigTerm(int)
949 UBuf
.actime
= FailTime
;
950 UBuf
.modtime
= FailTime
;
951 utime(FailFile
.c_str(),&UBuf
);
956 // FtpMethod::Configuration - Handle a configuration message /*{{{*/
957 // ---------------------------------------------------------------------
958 /* We stash the desired pipeline depth */
959 bool FtpMethod::Configuration(string Message
)
961 if (pkgAcqMethod::Configuration(Message
) == false)
964 TimeOut
= _config
->FindI("Acquire::Ftp::Timeout",TimeOut
);
968 // FtpMethod::Fetch - Fetch a file /*{{{*/
969 // ---------------------------------------------------------------------
970 /* Fetch a single file, called by the base class.. */
971 bool FtpMethod::Fetch(FetchItem
*Itm
)
974 const char *File
= Get
.Path
.c_str();
976 Res
.Filename
= Itm
->DestFile
;
979 // Connect to the server
980 if (Server
== 0 || Server
->Comp(Get
) == false)
983 Server
= new FTPConn(Get
);
986 // Could not connect is a transient error..
987 if (Server
->Open(this) == false)
994 // Get the files information
997 if (Server
->Size(File
,Size
) == false ||
998 Server
->ModTime(File
,FailTime
) == false)
1005 // See if it is an IMS hit
1006 if (Itm
->LastModified
== FailTime
)
1014 // See if the file exists
1016 if (stat(Itm
->DestFile
.c_str(),&Buf
) == 0)
1018 if (Size
== (unsigned)Buf
.st_size
&& FailTime
== Buf
.st_mtime
)
1020 Res
.Size
= Buf
.st_size
;
1021 Res
.LastModified
= Buf
.st_mtime
;
1022 Res
.ResumePoint
= Buf
.st_size
;
1028 if (FailTime
== Buf
.st_mtime
&& Size
> (unsigned)Buf
.st_size
)
1029 Res
.ResumePoint
= Buf
.st_size
;
1035 FileFd
Fd(Itm
->DestFile
,FileFd::WriteAny
);
1036 if (_error
->PendingError() == true)
1041 FailFile
= Itm
->DestFile
;
1042 FailFile
.c_str(); // Make sure we dont do a malloc in the signal handler
1046 if (Server
->Get(File
,Fd
,Res
.ResumePoint
,Hash
,Missing
) == false)
1051 struct utimbuf UBuf
;
1052 UBuf
.actime
= FailTime
;
1053 UBuf
.modtime
= FailTime
;
1054 utime(FailFile
.c_str(),&UBuf
);
1056 // If the file is missing we hard fail otherwise transient fail
1057 if (Missing
== true)
1063 Res
.Size
= Fd
.Size();
1066 Res
.LastModified
= FailTime
;
1067 Res
.TakeHashes(Hash
);
1070 struct utimbuf UBuf
;
1071 UBuf
.actime
= FailTime
;
1072 UBuf
.modtime
= FailTime
;
1073 utime(Queue
->DestFile
.c_str(),&UBuf
);
1082 int main(int argc
,const char *argv
[])
1084 /* See if we should be come the http client - we do this for http
1086 if (getenv("ftp_proxy") != 0)
1088 URI Proxy
= string(getenv("ftp_proxy"));
1090 // Run the HTTP method
1091 if (Proxy
.Access
== "http")
1093 // Copy over the environment setting
1095 snprintf(S
,sizeof(S
),"http_proxy=%s",getenv("ftp_proxy"));
1097 putenv("no_proxy=");
1099 // Run the http method
1100 string Path
= flNotFile(argv
[0]) + "http";
1101 execl(Path
.c_str(),Path
.c_str(),0);
1102 cerr
<< _("Unable to invoke ") << Path
<< endl
;