]>
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 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());
212 if (Tag
== 331) { // 331 User name okay, need password.
214 if (WriteMsg(Tag
,Msg
,"PASS %s",Pass
.c_str()) == false)
217 return _error
->Error(_("PASS failed, server said: %s"),Msg
.c_str());
220 // Enter passive mode
221 if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true)
222 TryPassive
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true);
224 TryPassive
= _config
->FindB("Acquire::FTP::Passive",true);
228 // Read the initial response
229 if (ReadResp(Tag
,Msg
) == false)
232 return _error
->Error(_("Server refused our connection and said: %s"),Msg
.c_str());
234 // Perform proxy script execution
235 Configuration::Item
const *Opts
= _config
->Tree("Acquire::ftp::ProxyLogin");
236 if (Opts
== 0 || Opts
->Child
== 0)
237 return _error
->Error(_("A proxy server was specified but no login "
238 "script, Acquire::ftp::ProxyLogin is empty."));
241 // Iterate over the entire login script
242 for (; Opts
!= 0; Opts
= Opts
->Next
)
244 if (Opts
->Value
.empty() == true)
247 // Substitute the variables into the command
249 if (ServerName
.Port
!= 0)
250 sprintf(SitePort
,"%u",ServerName
.Port
);
252 strcpy(SitePort
,"21");
253 string Tmp
= Opts
->Value
;
254 Tmp
= SubstVar(Tmp
,"$(PROXY_USER)",Proxy
.User
);
255 Tmp
= SubstVar(Tmp
,"$(PROXY_PASS)",Proxy
.Password
);
256 Tmp
= SubstVar(Tmp
,"$(SITE_USER)",User
);
257 Tmp
= SubstVar(Tmp
,"$(SITE_PASS)",Pass
);
258 Tmp
= SubstVar(Tmp
,"$(SITE_PORT)",SitePort
);
259 Tmp
= SubstVar(Tmp
,"$(SITE)",ServerName
.Host
);
262 if (WriteMsg(Tag
,Msg
,"%s",Tmp
.c_str()) == false)
265 return _error
->Error(_("Login script command '%s' failed, server said: %s"),Tmp
.c_str(),Msg
.c_str());
268 // Enter passive mode
270 if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true)
271 TryPassive
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true);
274 if (_config
->Exists("Acquire::FTP::Proxy::Passive") == true)
275 TryPassive
= _config
->FindB("Acquire::FTP::Proxy::Passive",true);
277 TryPassive
= _config
->FindB("Acquire::FTP::Passive",true);
281 // Force the use of extended commands
282 if (_config
->Exists("Acquire::FTP::ForceExtended::" + ServerName
.Host
) == true)
283 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended::" + ServerName
.Host
,true);
285 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended",false);
288 if (WriteMsg(Tag
,Msg
,"TYPE I") == false)
291 return _error
->Error(_("TYPE failed, server said: %s"),Msg
.c_str());
296 // FTPConn::ReadLine - Read a line from the server /*{{{*/
297 // ---------------------------------------------------------------------
298 /* This performs a very simple buffered read. */
299 bool FTPConn::ReadLine(string
&Text
)
305 while (Len
< sizeof(Buffer
))
307 // Scan the buffer for a new line
308 for (unsigned int I
= 0; I
!= Len
; I
++)
310 // Escape some special chars
315 if (Buffer
[I
] != '\n')
319 Text
= string(Buffer
,I
);
320 memmove(Buffer
,Buffer
+I
,Len
- I
);
325 // Wait for some data..
326 if (WaitFd(ServerFd
,false,TimeOut
) == false)
329 return _error
->Error(_("Connection timeout"));
333 int Res
= read(ServerFd
,Buffer
+ Len
,sizeof(Buffer
) - Len
);
335 _error
->Error(_("Server closed the connection"));
338 _error
->Errno("read",_("Read error"));
345 return _error
->Error(_("A response overflowed the buffer."));
348 // FTPConn::ReadResp - Read a full response from the server /*{{{*/
349 // ---------------------------------------------------------------------
350 /* This reads a reply code from the server, it handles both p */
351 bool FTPConn::ReadResp(unsigned int &Ret
,string
&Text
)
353 // Grab the first line of the response
355 if (ReadLine(Msg
) == false)
360 Ret
= strtol(Msg
.c_str(),&End
,10);
361 if (End
- Msg
.c_str() != 3)
362 return _error
->Error(_("Protocol corruption"));
365 Text
= Msg
.c_str()+4;
369 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
374 return _error
->Error(_("Protocol corruption"));
376 /* Okay, here we do the continued message trick. This is foolish, but
377 proftpd follows the protocol as specified and wu-ftpd doesn't, so
378 we filter. I wonder how many clients break if you use proftpd and
379 put a '- in the 3rd spot in the message? */
381 strncpy(Leader
,Msg
.c_str(),3);
383 while (ReadLine(Msg
) == true)
385 // Short, it must be using RFC continuation..
386 if (Msg
.length() < 4)
393 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == ' ')
395 Text
+= Msg
.c_str()+4;
399 // This message has the wu-ftpd style reply code prefixed
400 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == '-')
402 Text
+= Msg
.c_str()+4;
406 // Must be RFC style prefixing
410 if (Debug
== true && _error
->PendingError() == false)
411 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
413 return !_error
->PendingError();
416 // FTPConn::WriteMsg - Send a message to the server /*{{{*/
417 // ---------------------------------------------------------------------
418 /* Simple printf like function.. */
419 bool FTPConn::WriteMsg(unsigned int &Ret
,string
&Text
,const char *Fmt
,...)
424 // sprintf the description
426 vsnprintf(S
,sizeof(S
) - 4,Fmt
,args
);
430 cerr
<< "-> '" << QuoteString(S
,"") << "'" << endl
;
433 unsigned long Len
= strlen(S
);
434 unsigned long Start
= 0;
437 if (WaitFd(ServerFd
,true,TimeOut
) == false)
440 return _error
->Error(_("Connection timeout"));
443 int Res
= write(ServerFd
,S
+ Start
,Len
);
446 _error
->Errno("write",_("Write Error"));
455 return ReadResp(Ret
,Text
);
458 // FTPConn::GoPasv - Enter Passive mode /*{{{*/
459 // ---------------------------------------------------------------------
460 /* Try to enter passive mode, the return code does not indicate if passive
461 mode could or could not be established, only if there was a fatal error.
462 We have to enter passive mode every time we make a data connection :| */
463 bool FTPConn::GoPasv()
465 /* The PASV command only works on IPv4 sockets, even though it could
466 in theory suppory IPv6 via an all zeros reply */
467 if (((struct sockaddr
*)&PeerAddr
)->sa_family
!= AF_INET
||
468 ForceExtended
== true)
472 freeaddrinfo(PasvAddr
);
475 // Try to enable pasv mode
478 if (WriteMsg(Tag
,Msg
,"PASV") == false)
481 // Unsupported function
482 string::size_type Pos
= Msg
.find('(');
483 if (Tag
>= 400 || Pos
== string::npos
)
487 unsigned a0
,a1
,a2
,a3
,p0
,p1
;
488 if (sscanf(Msg
.c_str() + Pos
,"(%u,%u,%u,%u,%u,%u)",&a0
,&a1
,&a2
,&a3
,&p0
,&p1
) != 6)
491 /* Some evil servers return 0 to mean their addr. We can actually speak
492 to these servers natively using IPv6 */
493 if (a0
== 0 && a1
== 0 && a2
== 0 && a3
== 0)
495 // Get the IP in text form
496 char Name
[NI_MAXHOST
];
497 char Service
[NI_MAXSERV
];
498 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
499 Name
,sizeof(Name
),Service
,sizeof(Service
),
500 NI_NUMERICHOST
|NI_NUMERICSERV
);
502 struct addrinfo Hints
;
503 memset(&Hints
,0,sizeof(Hints
));
504 Hints
.ai_socktype
= SOCK_STREAM
;
505 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
506 Hints
.ai_flags
|= AI_NUMERICHOST
;
508 // Get a new passive address.
510 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
511 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
516 struct addrinfo Hints
;
517 memset(&Hints
,0,sizeof(Hints
));
518 Hints
.ai_socktype
= SOCK_STREAM
;
519 Hints
.ai_family
= AF_INET
;
520 Hints
.ai_flags
|= AI_NUMERICHOST
;
522 // Get a new passive address.
524 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
526 snprintf(Name
,sizeof(Name
),"%u.%u.%u.%u",a0
,a1
,a2
,a3
);
527 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
532 // FTPConn::ExtGoPasv - Enter Extended Passive mode /*{{{*/
533 // ---------------------------------------------------------------------
534 /* Try to enter extended passive mode. See GoPasv above and RFC 2428 */
535 bool FTPConn::ExtGoPasv()
538 freeaddrinfo(PasvAddr
);
541 // Try to enable pasv mode
544 if (WriteMsg(Tag
,Msg
,"EPSV") == false)
547 // Unsupported function
548 string::size_type Pos
= Msg
.find('(');
549 if (Tag
>= 400 || Pos
== string::npos
)
553 string::const_iterator List
[4];
556 for (string::const_iterator I
= Msg
.begin() + Pos
; I
< Msg
.end(); I
++)
568 unsigned long Proto
= 0;
569 unsigned long Port
= 0;
571 IP
= string(List
[1]+1,List
[2]);
572 Port
= atoi(string(List
[2]+1,List
[3]).c_str());
573 if (IP
.empty() == false)
574 Proto
= atoi(string(List
[0]+1,List
[1]).c_str());
579 // String version of the port
581 snprintf(PStr
,sizeof(PStr
),"%lu",Port
);
583 // Get the IP in text form
584 struct addrinfo Hints
;
585 memset(&Hints
,0,sizeof(Hints
));
586 Hints
.ai_socktype
= SOCK_STREAM
;
587 Hints
.ai_flags
|= AI_NUMERICHOST
;
589 /* The RFC defined case, connect to the old IP/protocol using the
591 if (IP
.empty() == true)
593 // Get the IP in text form
594 char Name
[NI_MAXHOST
];
595 char Service
[NI_MAXSERV
];
596 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
597 Name
,sizeof(Name
),Service
,sizeof(Service
),
598 NI_NUMERICHOST
|NI_NUMERICSERV
);
600 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
606 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
607 if (AFMap
[J
].IETFFamily
== Proto
)
608 Hints
.ai_family
= AFMap
[J
].Family
;
609 if (Hints
.ai_family
== 0)
613 // Get a new passive address.
615 if ((Res
= getaddrinfo(IP
.c_str(),PStr
,&Hints
,&PasvAddr
)) != 0)
621 // FTPConn::Size - Return the size of a file /*{{{*/
622 // ---------------------------------------------------------------------
623 /* Grab the file size from the server, 0 means no size or empty file */
624 bool FTPConn::Size(const char *Path
,unsigned long &Size
)
630 if (WriteMsg(Tag
,Msg
,"SIZE %s",Path
) == false)
634 Size
= strtol(Msg
.c_str(),&End
,10);
635 if (Tag
>= 400 || End
== Msg
.c_str())
640 // FTPConn::ModTime - Return the modification time of the file /*{{{*/
641 // ---------------------------------------------------------------------
642 /* Like Size no error is returned if the command is not supported. If the
643 command fails then time is set to the current time of day to fool
645 bool FTPConn::ModTime(const char *Path
, time_t &Time
)
649 // Query the mod time
652 if (WriteMsg(Tag
,Msg
,"MDTM %s",Path
) == false)
654 if (Tag
>= 400 || Msg
.empty() == true || isdigit(Msg
[0]) == 0)
662 // FTPConn::CreateDataFd - Get a data connection /*{{{*/
663 // ---------------------------------------------------------------------
664 /* Create the data connection. Call FinalizeDataFd after this though.. */
665 bool FTPConn::CreateDataFd()
670 // Attempt to enter passive mode.
671 if (TryPassive
== true)
673 if (GoPasv() == false)
676 // Oops, didn't work out, don't bother trying again.
685 if ((DataFd
= socket(PasvAddr
->ai_family
,PasvAddr
->ai_socktype
,
686 PasvAddr
->ai_protocol
)) < 0)
687 return _error
->Errno("socket",_("Could not create a socket"));
689 // Connect to the server
690 SetNonBlock(DataFd
,true);
691 if (connect(DataFd
,PasvAddr
->ai_addr
,PasvAddr
->ai_addrlen
) < 0 &&
692 errno
!= EINPROGRESS
)
693 return _error
->Errno("socket",_("Could not create a socket"));
695 /* This implements a timeout for connect by opening the connection
697 if (WaitFd(DataFd
,true,TimeOut
) == false)
698 return _error
->Error(_("Could not connect data socket, connection timed out"));
700 unsigned int Len
= sizeof(Err
);
701 if (getsockopt(DataFd
,SOL_SOCKET
,SO_ERROR
,&Err
,&Len
) != 0)
702 return _error
->Errno("getsockopt",_("Failed"));
704 return _error
->Error(_("Could not connect passive socket."));
713 // Get the information for a listening socket.
714 struct addrinfo
*BindAddr
= 0;
715 struct addrinfo Hints
;
716 memset(&Hints
,0,sizeof(Hints
));
717 Hints
.ai_socktype
= SOCK_STREAM
;
718 Hints
.ai_flags
|= AI_PASSIVE
;
719 Hints
.ai_family
= ((struct sockaddr
*)&ServerAddr
)->sa_family
;
721 if ((Res
= getaddrinfo(0,"0",&Hints
,&BindAddr
)) != 0)
722 return _error
->Error(_("getaddrinfo was unable to get a listening socket"));
724 // Construct the socket
725 if ((DataListenFd
= socket(BindAddr
->ai_family
,BindAddr
->ai_socktype
,
726 BindAddr
->ai_protocol
)) < 0)
728 freeaddrinfo(BindAddr
);
729 return _error
->Errno("socket",_("Could not create a socket"));
733 if (bind(DataListenFd
,BindAddr
->ai_addr
,BindAddr
->ai_addrlen
) < 0)
735 freeaddrinfo(BindAddr
);
736 return _error
->Errno("bind",_("Could not bind a socket"));
738 freeaddrinfo(BindAddr
);
739 if (listen(DataListenFd
,1) < 0)
740 return _error
->Errno("listen",_("Could not listen on the socket"));
741 SetNonBlock(DataListenFd
,true);
743 // Determine the name to send to the remote
744 struct sockaddr_storage Addr
;
745 socklen_t AddrLen
= sizeof(Addr
);
746 if (getsockname(DataListenFd
,(sockaddr
*)&Addr
,&AddrLen
) < 0)
747 return _error
->Errno("getsockname",_("Could not determine the socket's name"));
750 // Reverse the address. We need the server address and the data port.
751 char Name
[NI_MAXHOST
];
752 char Service
[NI_MAXSERV
];
753 char Service2
[NI_MAXSERV
];
754 getnameinfo((struct sockaddr
*)&Addr
,AddrLen
,
755 Name
,sizeof(Name
),Service
,sizeof(Service
),
756 NI_NUMERICHOST
|NI_NUMERICSERV
);
757 getnameinfo((struct sockaddr
*)&ServerAddr
,ServerAddrLen
,
758 Name
,sizeof(Name
),Service2
,sizeof(Service2
),
759 NI_NUMERICHOST
|NI_NUMERICSERV
);
761 // Send off an IPv4 address in the old port format
762 if (((struct sockaddr
*)&Addr
)->sa_family
== AF_INET
&&
763 ForceExtended
== false)
765 // Convert the dots in the quad into commas
766 for (char *I
= Name
; *I
!= 0; I
++)
769 unsigned long Port
= atoi(Service
);
771 // Send the port command
774 if (WriteMsg(Tag
,Msg
,"PORT %s,%d,%d",
776 (int)(Port
>> 8) & 0xff, (int)(Port
& 0xff)) == false)
779 return _error
->Error(_("Unable to send PORT command"));
783 // Construct an EPRT command
785 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
786 if (AFMap
[J
].Family
== ((struct sockaddr
*)&Addr
)->sa_family
)
787 Proto
= AFMap
[J
].IETFFamily
;
789 return _error
->Error(_("Unknown address family %u (AF_*)"),
790 ((struct sockaddr
*)&Addr
)->sa_family
);
792 // Send the EPRT command
795 if (WriteMsg(Tag
,Msg
,"EPRT |%u|%s|%s|",Proto
,Name
,Service
) == false)
798 return _error
->Error(_("EPRT failed, server said: %s"),Msg
.c_str());
802 // FTPConn::Finalize - Complete the Data connection /*{{{*/
803 // ---------------------------------------------------------------------
804 /* If the connection is in port mode this waits for the other end to hook
806 bool FTPConn::Finalize()
808 // Passive mode? Do nothing
812 // Close any old socket..
816 // Wait for someone to connect..
817 if (WaitFd(DataListenFd
,false,TimeOut
) == false)
818 return _error
->Error(_("Data socket connect timed out"));
820 // Accept the connection
821 struct sockaddr_in Addr
;
822 socklen_t Len
= sizeof(Addr
);
823 DataFd
= accept(DataListenFd
,(struct sockaddr
*)&Addr
,&Len
);
825 return _error
->Errno("accept",_("Unable to accept connection"));
833 // FTPConn::Get - Get a file /*{{{*/
834 // ---------------------------------------------------------------------
835 /* This opens a data connection, sends REST and RETR and then
836 transfers the file over. */
837 bool FTPConn::Get(const char *Path
,FileFd
&To
,unsigned long Resume
,
838 Hashes
&Hash
,bool &Missing
)
841 if (CreateDataFd() == false)
848 if (WriteMsg(Tag
,Msg
,"REST %u",Resume
) == false)
854 if (To
.Truncate(Resume
) == false)
857 if (To
.Seek(0) == false)
862 if (Hash
.AddFD(To
.Fd(),Resume
) == false)
864 _error
->Errno("read",_("Problem hashing file"));
869 // Send the get command
870 if (WriteMsg(Tag
,Msg
,"RETR %s",Path
) == false)
877 return _error
->Error(_("Unable to fetch file, server said '%s'"),Msg
.c_str());
880 // Finish off the data connection
881 if (Finalize() == false)
885 unsigned char Buffer
[4096];
888 // Wait for some data..
889 if (WaitFd(DataFd
,false,TimeOut
) == false)
892 return _error
->Error(_("Data socket timed out"));
896 int Res
= read(DataFd
,Buffer
,sizeof(Buffer
));
906 Hash
.Add(Buffer
,Res
);
907 if (To
.Write(Buffer
,Res
) == false)
918 // Read the closing message from the server
919 if (ReadResp(Tag
,Msg
) == false)
922 return _error
->Error(_("Data transfer failed, server said '%s'"),Msg
.c_str());
927 // FtpMethod::FtpMethod - Constructor /*{{{*/
928 // ---------------------------------------------------------------------
930 FtpMethod::FtpMethod() : pkgAcqMethod("1.0",SendConfig
)
932 signal(SIGTERM
,SigTerm
);
933 signal(SIGINT
,SigTerm
);
939 // FtpMethod::SigTerm - Handle a fatal signal /*{{{*/
940 // ---------------------------------------------------------------------
941 /* This closes and timestamps the open file. This is neccessary to get
942 resume behavoir on user abort */
943 void FtpMethod::SigTerm(int)
951 UBuf
.actime
= FailTime
;
952 UBuf
.modtime
= FailTime
;
953 utime(FailFile
.c_str(),&UBuf
);
958 // FtpMethod::Configuration - Handle a configuration message /*{{{*/
959 // ---------------------------------------------------------------------
960 /* We stash the desired pipeline depth */
961 bool FtpMethod::Configuration(string Message
)
963 if (pkgAcqMethod::Configuration(Message
) == false)
966 TimeOut
= _config
->FindI("Acquire::Ftp::Timeout",TimeOut
);
970 // FtpMethod::Fetch - Fetch a file /*{{{*/
971 // ---------------------------------------------------------------------
972 /* Fetch a single file, called by the base class.. */
973 bool FtpMethod::Fetch(FetchItem
*Itm
)
976 const char *File
= Get
.Path
.c_str();
978 Res
.Filename
= Itm
->DestFile
;
981 // Connect to the server
982 if (Server
== 0 || Server
->Comp(Get
) == false)
985 Server
= new FTPConn(Get
);
988 // Could not connect is a transient error..
989 if (Server
->Open(this) == false)
996 // Get the files information
999 if (Server
->Size(File
,Size
) == false ||
1000 Server
->ModTime(File
,FailTime
) == false)
1007 // See if it is an IMS hit
1008 if (Itm
->LastModified
== FailTime
)
1016 // See if the file exists
1018 if (stat(Itm
->DestFile
.c_str(),&Buf
) == 0)
1020 if (Size
== (unsigned)Buf
.st_size
&& FailTime
== Buf
.st_mtime
)
1022 Res
.Size
= Buf
.st_size
;
1023 Res
.LastModified
= Buf
.st_mtime
;
1024 Res
.ResumePoint
= Buf
.st_size
;
1030 if (FailTime
== Buf
.st_mtime
&& Size
> (unsigned)Buf
.st_size
)
1031 Res
.ResumePoint
= Buf
.st_size
;
1037 FileFd
Fd(Itm
->DestFile
,FileFd::WriteAny
);
1038 if (_error
->PendingError() == true)
1043 FailFile
= Itm
->DestFile
;
1044 FailFile
.c_str(); // Make sure we dont do a malloc in the signal handler
1048 if (Server
->Get(File
,Fd
,Res
.ResumePoint
,Hash
,Missing
) == false)
1053 struct utimbuf UBuf
;
1054 UBuf
.actime
= FailTime
;
1055 UBuf
.modtime
= FailTime
;
1056 utime(FailFile
.c_str(),&UBuf
);
1058 // If the file is missing we hard fail otherwise transient fail
1059 if (Missing
== true)
1065 Res
.Size
= Fd
.Size();
1068 Res
.LastModified
= FailTime
;
1069 Res
.TakeHashes(Hash
);
1072 struct utimbuf UBuf
;
1073 UBuf
.actime
= FailTime
;
1074 UBuf
.modtime
= FailTime
;
1075 utime(Queue
->DestFile
.c_str(),&UBuf
);
1084 int main(int argc
,const char *argv
[])
1086 setlocale(LC_ALL
, "");
1088 /* See if we should be come the http client - we do this for http
1090 if (getenv("ftp_proxy") != 0)
1092 URI Proxy
= string(getenv("ftp_proxy"));
1094 // Run the HTTP method
1095 if (Proxy
.Access
== "http")
1097 // Copy over the environment setting
1099 snprintf(S
,sizeof(S
),"http_proxy=%s",getenv("ftp_proxy"));
1101 putenv("no_proxy=");
1103 // Run the http method
1104 string Path
= flNotFile(argv
[0]) + "http";
1105 execl(Path
.c_str(),Path
.c_str(),0);
1106 cerr
<< _("Unable to invoke ") << Path
<< endl
;