]>
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 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 /*{{{*/
18 #include <apt-pkg/fileutl.h>
19 #include <apt-pkg/acquire-method.h>
20 #include <apt-pkg/error.h>
21 #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 string SpecificProxy
= _config
->Find("Acquire::ftp::Proxy::" + ServerName
.Host
);
116 if (!SpecificProxy
.empty())
118 if (SpecificProxy
== "DIRECT")
121 Proxy
= SpecificProxy
;
125 string DefProxy
= _config
->Find("Acquire::ftp::Proxy");
126 if (!DefProxy
.empty())
132 char* result
= getenv("ftp_proxy");
133 Proxy
= result
? result
: "";
137 // Parse no_proxy, a , separated list of domains
138 if (getenv("no_proxy") != 0)
140 if (CheckDomainList(ServerName
.Host
,getenv("no_proxy")) == true)
144 // Determine what host and port to use based on the proxy settings
147 if (Proxy
.empty() == true)
149 if (ServerName
.Port
!= 0)
150 Port
= ServerName
.Port
;
151 Host
= ServerName
.Host
;
160 /* Connect to the remote server. Since FTP is connection oriented we
161 want to make sure we get a new server every time we reconnect */
163 if (Connect(Host
,Port
,"ftp",21,ServerFd
,TimeOut
,Owner
) == false)
166 // Login must be before getpeername otherwise dante won't work.
167 Owner
->Status(_("Logging in"));
170 // Get the remote server's address
171 PeerAddrLen
= sizeof(PeerAddr
);
172 if (getpeername(ServerFd
,(sockaddr
*)&PeerAddr
,&PeerAddrLen
) != 0)
173 return _error
->Errno("getpeername",_("Unable to determine the peer name"));
175 // Get the local machine's address
176 ServerAddrLen
= sizeof(ServerAddr
);
177 if (getsockname(ServerFd
,(sockaddr
*)&ServerAddr
,&ServerAddrLen
) != 0)
178 return _error
->Errno("getsockname",_("Unable to determine the local name"));
183 // FTPConn::Login - Login to the remote server /*{{{*/
184 // ---------------------------------------------------------------------
185 /* This performs both normal login and proxy login using a simples script
186 stored in the config file. */
187 bool FTPConn::Login()
192 // Setup the variables needed for authentication
193 string User
= "anonymous";
194 string Pass
= "apt_get_ftp_2.1@debian.linux.user";
196 // Fill in the user/pass
197 if (ServerName
.User
.empty() == false)
198 User
= ServerName
.User
;
199 if (ServerName
.Password
.empty() == false)
200 Pass
= ServerName
.Password
;
202 // Perform simple login
203 if (Proxy
.empty() == true)
205 // Read the initial response
206 if (ReadResp(Tag
,Msg
) == false)
209 return _error
->Error(_("The server refused the connection and said: %s"),Msg
.c_str());
212 if (WriteMsg(Tag
,Msg
,"USER %s",User
.c_str()) == false)
215 return _error
->Error(_("USER failed, server said: %s"),Msg
.c_str());
217 if (Tag
== 331) { // 331 User name okay, need password.
219 if (WriteMsg(Tag
,Msg
,"PASS %s",Pass
.c_str()) == false)
222 return _error
->Error(_("PASS failed, server said: %s"),Msg
.c_str());
225 // Enter passive mode
226 if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true)
227 TryPassive
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true);
229 TryPassive
= _config
->FindB("Acquire::FTP::Passive",true);
233 // Read the initial response
234 if (ReadResp(Tag
,Msg
) == false)
237 return _error
->Error(_("The server refused the connection and said: %s"),Msg
.c_str());
239 // Perform proxy script execution
240 Configuration::Item
const *Opts
= _config
->Tree("Acquire::ftp::ProxyLogin");
241 if (Opts
== 0 || Opts
->Child
== 0)
242 return _error
->Error(_("A proxy server was specified but no login "
243 "script, Acquire::ftp::ProxyLogin is empty."));
246 // Iterate over the entire login script
247 for (; Opts
!= 0; Opts
= Opts
->Next
)
249 if (Opts
->Value
.empty() == true)
252 // Substitute the variables into the command
254 if (ServerName
.Port
!= 0)
255 sprintf(SitePort
,"%u",ServerName
.Port
);
257 strcpy(SitePort
,"21");
258 string Tmp
= Opts
->Value
;
259 Tmp
= SubstVar(Tmp
,"$(PROXY_USER)",Proxy
.User
);
260 Tmp
= SubstVar(Tmp
,"$(PROXY_PASS)",Proxy
.Password
);
261 Tmp
= SubstVar(Tmp
,"$(SITE_USER)",User
);
262 Tmp
= SubstVar(Tmp
,"$(SITE_PASS)",Pass
);
263 Tmp
= SubstVar(Tmp
,"$(SITE_PORT)",SitePort
);
264 Tmp
= SubstVar(Tmp
,"$(SITE)",ServerName
.Host
);
267 if (WriteMsg(Tag
,Msg
,"%s",Tmp
.c_str()) == false)
270 return _error
->Error(_("Login script command '%s' failed, server said: %s"),Tmp
.c_str(),Msg
.c_str());
273 // Enter passive mode
275 if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true)
276 TryPassive
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true);
279 if (_config
->Exists("Acquire::FTP::Proxy::Passive") == true)
280 TryPassive
= _config
->FindB("Acquire::FTP::Proxy::Passive",true);
282 TryPassive
= _config
->FindB("Acquire::FTP::Passive",true);
286 // Force the use of extended commands
287 if (_config
->Exists("Acquire::FTP::ForceExtended::" + ServerName
.Host
) == true)
288 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended::" + ServerName
.Host
,true);
290 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended",false);
293 if (WriteMsg(Tag
,Msg
,"TYPE I") == false)
296 return _error
->Error(_("TYPE failed, server said: %s"),Msg
.c_str());
301 // FTPConn::ReadLine - Read a line from the server /*{{{*/
302 // ---------------------------------------------------------------------
303 /* This performs a very simple buffered read. */
304 bool FTPConn::ReadLine(string
&Text
)
310 while (Len
< sizeof(Buffer
))
312 // Scan the buffer for a new line
313 for (unsigned int I
= 0; I
!= Len
; I
++)
315 // Escape some special chars
320 if (Buffer
[I
] != '\n')
324 Text
= string(Buffer
,I
);
325 memmove(Buffer
,Buffer
+I
,Len
- I
);
330 // Wait for some data..
331 if (WaitFd(ServerFd
,false,TimeOut
) == false)
334 return _error
->Error(_("Connection timeout"));
338 int Res
= read(ServerFd
,Buffer
+ Len
,sizeof(Buffer
) - Len
);
340 _error
->Error(_("Server closed the connection"));
343 _error
->Errno("read",_("Read error"));
350 return _error
->Error(_("A response overflowed the buffer."));
353 // FTPConn::ReadResp - Read a full response from the server /*{{{*/
354 // ---------------------------------------------------------------------
355 /* This reads a reply code from the server, it handles both p */
356 bool FTPConn::ReadResp(unsigned int &Ret
,string
&Text
)
358 // Grab the first line of the response
360 if (ReadLine(Msg
) == false)
365 Ret
= strtol(Msg
.c_str(),&End
,10);
366 if (End
- Msg
.c_str() != 3)
367 return _error
->Error(_("Protocol corruption"));
370 Text
= Msg
.c_str()+4;
374 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
379 return _error
->Error(_("Protocol corruption"));
381 /* Okay, here we do the continued message trick. This is foolish, but
382 proftpd follows the protocol as specified and wu-ftpd doesn't, so
383 we filter. I wonder how many clients break if you use proftpd and
384 put a '- in the 3rd spot in the message? */
386 strncpy(Leader
,Msg
.c_str(),3);
388 while (ReadLine(Msg
) == true)
390 // Short, it must be using RFC continuation..
391 if (Msg
.length() < 4)
398 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == ' ')
400 Text
+= Msg
.c_str()+4;
404 // This message has the wu-ftpd style reply code prefixed
405 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == '-')
407 Text
+= Msg
.c_str()+4;
411 // Must be RFC style prefixing
415 if (Debug
== true && _error
->PendingError() == false)
416 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
418 return !_error
->PendingError();
421 // FTPConn::WriteMsg - Send a message to the server /*{{{*/
422 // ---------------------------------------------------------------------
423 /* Simple printf like function.. */
424 bool FTPConn::WriteMsg(unsigned int &Ret
,string
&Text
,const char *Fmt
,...)
429 // sprintf the description
431 vsnprintf(S
,sizeof(S
) - 4,Fmt
,args
);
435 cerr
<< "-> '" << QuoteString(S
,"") << "'" << endl
;
438 unsigned long Len
= strlen(S
);
439 unsigned long Start
= 0;
442 if (WaitFd(ServerFd
,true,TimeOut
) == false)
445 return _error
->Error(_("Connection timeout"));
448 int Res
= write(ServerFd
,S
+ Start
,Len
);
451 _error
->Errno("write",_("Write error"));
460 return ReadResp(Ret
,Text
);
463 // FTPConn::GoPasv - Enter Passive mode /*{{{*/
464 // ---------------------------------------------------------------------
465 /* Try to enter passive mode, the return code does not indicate if passive
466 mode could or could not be established, only if there was a fatal error.
467 We have to enter passive mode every time we make a data connection :| */
468 bool FTPConn::GoPasv()
470 /* The PASV command only works on IPv4 sockets, even though it could
471 in theory suppory IPv6 via an all zeros reply */
472 if (((struct sockaddr
*)&PeerAddr
)->sa_family
!= AF_INET
||
473 ForceExtended
== true)
477 freeaddrinfo(PasvAddr
);
480 // Try to enable pasv mode
483 if (WriteMsg(Tag
,Msg
,"PASV") == false)
486 // Unsupported function
487 string::size_type Pos
= Msg
.find('(');
488 if (Tag
>= 400 || Pos
== string::npos
)
492 unsigned a0
,a1
,a2
,a3
,p0
,p1
;
493 if (sscanf(Msg
.c_str() + Pos
,"(%u,%u,%u,%u,%u,%u)",&a0
,&a1
,&a2
,&a3
,&p0
,&p1
) != 6)
496 /* Some evil servers return 0 to mean their addr. We can actually speak
497 to these servers natively using IPv6 */
498 if (a0
== 0 && a1
== 0 && a2
== 0 && a3
== 0)
500 // Get the IP in text form
501 char Name
[NI_MAXHOST
];
502 char Service
[NI_MAXSERV
];
503 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
504 Name
,sizeof(Name
),Service
,sizeof(Service
),
505 NI_NUMERICHOST
|NI_NUMERICSERV
);
507 struct addrinfo Hints
;
508 memset(&Hints
,0,sizeof(Hints
));
509 Hints
.ai_socktype
= SOCK_STREAM
;
510 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
511 Hints
.ai_flags
|= AI_NUMERICHOST
;
513 // Get a new passive address.
515 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
516 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
521 struct addrinfo Hints
;
522 memset(&Hints
,0,sizeof(Hints
));
523 Hints
.ai_socktype
= SOCK_STREAM
;
524 Hints
.ai_family
= AF_INET
;
525 Hints
.ai_flags
|= AI_NUMERICHOST
;
527 // Get a new passive address.
529 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
531 snprintf(Name
,sizeof(Name
),"%u.%u.%u.%u",a0
,a1
,a2
,a3
);
532 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
537 // FTPConn::ExtGoPasv - Enter Extended Passive mode /*{{{*/
538 // ---------------------------------------------------------------------
539 /* Try to enter extended passive mode. See GoPasv above and RFC 2428 */
540 bool FTPConn::ExtGoPasv()
543 freeaddrinfo(PasvAddr
);
546 // Try to enable pasv mode
549 if (WriteMsg(Tag
,Msg
,"EPSV") == false)
552 // Unsupported function
553 string::size_type Pos
= Msg
.find('(');
554 if (Tag
>= 400 || Pos
== string::npos
)
558 string::const_iterator List
[4];
561 for (string::const_iterator I
= Msg
.begin() + Pos
; I
< Msg
.end(); I
++)
573 unsigned long Proto
= 0;
574 unsigned long Port
= 0;
576 IP
= string(List
[1]+1,List
[2]);
577 Port
= atoi(string(List
[2]+1,List
[3]).c_str());
578 if (IP
.empty() == false)
579 Proto
= atoi(string(List
[0]+1,List
[1]).c_str());
584 // String version of the port
586 snprintf(PStr
,sizeof(PStr
),"%lu",Port
);
588 // Get the IP in text form
589 struct addrinfo Hints
;
590 memset(&Hints
,0,sizeof(Hints
));
591 Hints
.ai_socktype
= SOCK_STREAM
;
592 Hints
.ai_flags
|= AI_NUMERICHOST
;
594 /* The RFC defined case, connect to the old IP/protocol using the
596 if (IP
.empty() == true)
598 // Get the IP in text form
599 char Name
[NI_MAXHOST
];
600 char Service
[NI_MAXSERV
];
601 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
602 Name
,sizeof(Name
),Service
,sizeof(Service
),
603 NI_NUMERICHOST
|NI_NUMERICSERV
);
605 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
611 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
612 if (AFMap
[J
].IETFFamily
== Proto
)
613 Hints
.ai_family
= AFMap
[J
].Family
;
614 if (Hints
.ai_family
== 0)
618 // Get a new passive address.
620 if ((Res
= getaddrinfo(IP
.c_str(),PStr
,&Hints
,&PasvAddr
)) != 0)
626 // FTPConn::Size - Return the size of a file /*{{{*/
627 // ---------------------------------------------------------------------
628 /* Grab the file size from the server, 0 means no size or empty file */
629 bool FTPConn::Size(const char *Path
,unsigned long &Size
)
635 if (WriteMsg(Tag
,Msg
,"SIZE %s",Path
) == false)
639 Size
= strtol(Msg
.c_str(),&End
,10);
640 if (Tag
>= 400 || End
== Msg
.c_str())
645 // FTPConn::ModTime - Return the modification time of the file /*{{{*/
646 // ---------------------------------------------------------------------
647 /* Like Size no error is returned if the command is not supported. If the
648 command fails then time is set to the current time of day to fool
650 bool FTPConn::ModTime(const char *Path
, time_t &Time
)
654 // Query the mod time
657 if (WriteMsg(Tag
,Msg
,"MDTM %s",Path
) == false)
659 if (Tag
>= 400 || Msg
.empty() == true || isdigit(Msg
[0]) == 0)
667 // FTPConn::CreateDataFd - Get a data connection /*{{{*/
668 // ---------------------------------------------------------------------
669 /* Create the data connection. Call FinalizeDataFd after this though.. */
670 bool FTPConn::CreateDataFd()
675 // Attempt to enter passive mode.
676 if (TryPassive
== true)
678 if (GoPasv() == false)
681 // Oops, didn't work out, don't bother trying again.
690 if ((DataFd
= socket(PasvAddr
->ai_family
,PasvAddr
->ai_socktype
,
691 PasvAddr
->ai_protocol
)) < 0)
692 return _error
->Errno("socket",_("Could not create a socket"));
694 // Connect to the server
695 SetNonBlock(DataFd
,true);
696 if (connect(DataFd
,PasvAddr
->ai_addr
,PasvAddr
->ai_addrlen
) < 0 &&
697 errno
!= EINPROGRESS
)
698 return _error
->Errno("socket",_("Could not create a socket"));
700 /* This implements a timeout for connect by opening the connection
702 if (WaitFd(DataFd
,true,TimeOut
) == false)
703 return _error
->Error(_("Could not connect data socket, connection timed out"));
705 unsigned int Len
= sizeof(Err
);
706 if (getsockopt(DataFd
,SOL_SOCKET
,SO_ERROR
,&Err
,&Len
) != 0)
707 return _error
->Errno("getsockopt",_("Failed"));
709 return _error
->Error(_("Could not connect passive socket."));
718 // Get the information for a listening socket.
719 struct addrinfo
*BindAddr
= 0;
720 struct addrinfo Hints
;
721 memset(&Hints
,0,sizeof(Hints
));
722 Hints
.ai_socktype
= SOCK_STREAM
;
723 Hints
.ai_flags
|= AI_PASSIVE
;
724 Hints
.ai_family
= ((struct sockaddr
*)&ServerAddr
)->sa_family
;
726 if ((Res
= getaddrinfo(0,"0",&Hints
,&BindAddr
)) != 0)
727 return _error
->Error(_("getaddrinfo was unable to get a listening socket"));
729 // Construct the socket
730 if ((DataListenFd
= socket(BindAddr
->ai_family
,BindAddr
->ai_socktype
,
731 BindAddr
->ai_protocol
)) < 0)
733 freeaddrinfo(BindAddr
);
734 return _error
->Errno("socket",_("Could not create a socket"));
738 if (bind(DataListenFd
,BindAddr
->ai_addr
,BindAddr
->ai_addrlen
) < 0)
740 freeaddrinfo(BindAddr
);
741 return _error
->Errno("bind",_("Could not bind a socket"));
743 freeaddrinfo(BindAddr
);
744 if (listen(DataListenFd
,1) < 0)
745 return _error
->Errno("listen",_("Could not listen on the socket"));
746 SetNonBlock(DataListenFd
,true);
748 // Determine the name to send to the remote
749 struct sockaddr_storage Addr
;
750 socklen_t AddrLen
= sizeof(Addr
);
751 if (getsockname(DataListenFd
,(sockaddr
*)&Addr
,&AddrLen
) < 0)
752 return _error
->Errno("getsockname",_("Could not determine the socket's name"));
755 // Reverse the address. We need the server address and the data port.
756 char Name
[NI_MAXHOST
];
757 char Service
[NI_MAXSERV
];
758 char Service2
[NI_MAXSERV
];
759 getnameinfo((struct sockaddr
*)&Addr
,AddrLen
,
760 Name
,sizeof(Name
),Service
,sizeof(Service
),
761 NI_NUMERICHOST
|NI_NUMERICSERV
);
762 getnameinfo((struct sockaddr
*)&ServerAddr
,ServerAddrLen
,
763 Name
,sizeof(Name
),Service2
,sizeof(Service2
),
764 NI_NUMERICHOST
|NI_NUMERICSERV
);
766 // Send off an IPv4 address in the old port format
767 if (((struct sockaddr
*)&Addr
)->sa_family
== AF_INET
&&
768 ForceExtended
== false)
770 // Convert the dots in the quad into commas
771 for (char *I
= Name
; *I
!= 0; I
++)
774 unsigned long Port
= atoi(Service
);
776 // Send the port command
779 if (WriteMsg(Tag
,Msg
,"PORT %s,%d,%d",
781 (int)(Port
>> 8) & 0xff, (int)(Port
& 0xff)) == false)
784 return _error
->Error(_("Unable to send PORT command"));
788 // Construct an EPRT command
790 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
791 if (AFMap
[J
].Family
== ((struct sockaddr
*)&Addr
)->sa_family
)
792 Proto
= AFMap
[J
].IETFFamily
;
794 return _error
->Error(_("Unknown address family %u (AF_*)"),
795 ((struct sockaddr
*)&Addr
)->sa_family
);
797 // Send the EPRT command
800 if (WriteMsg(Tag
,Msg
,"EPRT |%u|%s|%s|",Proto
,Name
,Service
) == false)
803 return _error
->Error(_("EPRT failed, server said: %s"),Msg
.c_str());
807 // FTPConn::Finalize - Complete the Data connection /*{{{*/
808 // ---------------------------------------------------------------------
809 /* If the connection is in port mode this waits for the other end to hook
811 bool FTPConn::Finalize()
813 // Passive mode? Do nothing
817 // Close any old socket..
821 // Wait for someone to connect..
822 if (WaitFd(DataListenFd
,false,TimeOut
) == false)
823 return _error
->Error(_("Data socket connect timed out"));
825 // Accept the connection
826 struct sockaddr_in Addr
;
827 socklen_t Len
= sizeof(Addr
);
828 DataFd
= accept(DataListenFd
,(struct sockaddr
*)&Addr
,&Len
);
830 return _error
->Errno("accept",_("Unable to accept connection"));
838 // FTPConn::Get - Get a file /*{{{*/
839 // ---------------------------------------------------------------------
840 /* This opens a data connection, sends REST and RETR and then
841 transfers the file over. */
842 bool FTPConn::Get(const char *Path
,FileFd
&To
,unsigned long Resume
,
843 Hashes
&Hash
,bool &Missing
)
846 if (CreateDataFd() == false)
853 if (WriteMsg(Tag
,Msg
,"REST %u",Resume
) == false)
859 if (To
.Truncate(Resume
) == false)
862 if (To
.Seek(0) == false)
867 if (Hash
.AddFD(To
.Fd(),Resume
) == false)
869 _error
->Errno("read",_("Problem hashing file"));
874 // Send the get command
875 if (WriteMsg(Tag
,Msg
,"RETR %s",Path
) == false)
882 return _error
->Error(_("Unable to fetch file, server said '%s'"),Msg
.c_str());
885 // Finish off the data connection
886 if (Finalize() == false)
890 unsigned char Buffer
[4096];
893 // Wait for some data..
894 if (WaitFd(DataFd
,false,TimeOut
) == false)
897 return _error
->Error(_("Data socket timed out"));
901 int Res
= read(DataFd
,Buffer
,sizeof(Buffer
));
911 Hash
.Add(Buffer
,Res
);
912 if (To
.Write(Buffer
,Res
) == false)
923 // Read the closing message from the server
924 if (ReadResp(Tag
,Msg
) == false)
927 return _error
->Error(_("Data transfer failed, server said '%s'"),Msg
.c_str());
932 // FtpMethod::FtpMethod - Constructor /*{{{*/
933 // ---------------------------------------------------------------------
935 FtpMethod::FtpMethod() : pkgAcqMethod("1.0",SendConfig
)
937 signal(SIGTERM
,SigTerm
);
938 signal(SIGINT
,SigTerm
);
944 // FtpMethod::SigTerm - Handle a fatal signal /*{{{*/
945 // ---------------------------------------------------------------------
946 /* This closes and timestamps the open file. This is neccessary to get
947 resume behavoir on user abort */
948 void FtpMethod::SigTerm(int)
956 UBuf
.actime
= FailTime
;
957 UBuf
.modtime
= FailTime
;
958 utime(FailFile
.c_str(),&UBuf
);
963 // FtpMethod::Configuration - Handle a configuration message /*{{{*/
964 // ---------------------------------------------------------------------
965 /* We stash the desired pipeline depth */
966 bool FtpMethod::Configuration(string Message
)
968 if (pkgAcqMethod::Configuration(Message
) == false)
971 TimeOut
= _config
->FindI("Acquire::Ftp::Timeout",TimeOut
);
975 // FtpMethod::Fetch - Fetch a file /*{{{*/
976 // ---------------------------------------------------------------------
977 /* Fetch a single file, called by the base class.. */
978 bool FtpMethod::Fetch(FetchItem
*Itm
)
981 const char *File
= Get
.Path
.c_str();
983 Res
.Filename
= Itm
->DestFile
;
986 // Connect to the server
987 if (Server
== 0 || Server
->Comp(Get
) == false)
990 Server
= new FTPConn(Get
);
993 // Could not connect is a transient error..
994 if (Server
->Open(this) == false)
1001 // Get the files information
1004 if (Server
->Size(File
,Size
) == false ||
1005 Server
->ModTime(File
,FailTime
) == false)
1012 // See if it is an IMS hit
1013 if (Itm
->LastModified
== FailTime
)
1021 // See if the file exists
1023 if (stat(Itm
->DestFile
.c_str(),&Buf
) == 0)
1025 if (Size
== (unsigned)Buf
.st_size
&& FailTime
== Buf
.st_mtime
)
1027 Res
.Size
= Buf
.st_size
;
1028 Res
.LastModified
= Buf
.st_mtime
;
1029 Res
.ResumePoint
= Buf
.st_size
;
1035 if (FailTime
== Buf
.st_mtime
&& Size
> (unsigned)Buf
.st_size
)
1036 Res
.ResumePoint
= Buf
.st_size
;
1042 FileFd
Fd(Itm
->DestFile
,FileFd::WriteAny
);
1043 if (_error
->PendingError() == true)
1048 FailFile
= Itm
->DestFile
;
1049 FailFile
.c_str(); // Make sure we dont do a malloc in the signal handler
1053 if (Server
->Get(File
,Fd
,Res
.ResumePoint
,Hash
,Missing
) == false)
1058 struct utimbuf UBuf
;
1059 UBuf
.actime
= FailTime
;
1060 UBuf
.modtime
= FailTime
;
1061 utime(FailFile
.c_str(),&UBuf
);
1063 // If the file is missing we hard fail and delete the destfile
1064 // otherwise transient fail
1065 if (Missing
== true) {
1066 unlink(FailFile
.c_str());
1073 Res
.Size
= Fd
.Size();
1076 Res
.LastModified
= FailTime
;
1077 Res
.TakeHashes(Hash
);
1080 struct utimbuf UBuf
;
1081 UBuf
.actime
= FailTime
;
1082 UBuf
.modtime
= FailTime
;
1083 utime(Queue
->DestFile
.c_str(),&UBuf
);
1092 int main(int argc
,const char *argv
[])
1094 setlocale(LC_ALL
, "");
1096 /* See if we should be come the http client - we do this for http
1098 if (getenv("ftp_proxy") != 0)
1100 URI Proxy
= string(getenv("ftp_proxy"));
1102 // Run the HTTP method
1103 if (Proxy
.Access
== "http")
1105 // Copy over the environment setting
1107 snprintf(S
,sizeof(S
),"http_proxy=%s",getenv("ftp_proxy"));
1109 putenv((char *)"no_proxy=");
1111 // Run the http method
1112 string Path
= flNotFile(argv
[0]) + "http";
1113 execl(Path
.c_str(),Path
.c_str(),(char *)NULL
);
1114 cerr
<< _("Unable to invoke ") << Path
<< endl
;