]>
git.saurik.com Git - apt.git/blob - methods/ftp.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: ftp.cc,v 1.28 2001/05/22 04:02:00 jgg 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>
34 #include <netinet/in.h>
35 #include <sys/socket.h>
36 #include <arpa/inet.h>
39 #include "rfc2553emu.h"
46 /* This table is for the EPRT and EPSV commands, it maps the OS address
47 family to the IETF address families */
51 unsigned long IETFFamily
;
55 struct AFMap AFMap
[] = {{AF_INET
,1},{}};
57 struct AFMap AFMap
[] = {{AF_INET
,1},{AF_INET6
,2},{}};
60 unsigned long TimeOut
= 120;
62 string
FtpMethod::FailFile
;
63 int FtpMethod::FailFd
= -1;
64 time_t FtpMethod::FailTime
= 0;
66 // FTPConn::FTPConn - Constructor /*{{{*/
67 // ---------------------------------------------------------------------
69 FTPConn::FTPConn(URI Srv
) : Len(0), ServerFd(-1), DataFd(-1),
70 DataListenFd(-1), ServerName(Srv
)
72 Debug
= _config
->FindB("Debug::Acquire::Ftp",false);
76 // FTPConn::~FTPConn - Destructor /*{{{*/
77 // ---------------------------------------------------------------------
84 // FTPConn::Close - Close down the connection /*{{{*/
85 // ---------------------------------------------------------------------
86 /* Just tear down the socket and data socket */
97 freeaddrinfo(PasvAddr
);
101 // FTPConn::Open - Open a new connection /*{{{*/
102 // ---------------------------------------------------------------------
103 /* Connect to the server using a non-blocking connection and perform a
105 bool FTPConn::Open(pkgAcqMethod
*Owner
)
107 // Use the already open connection if possible.
113 // Determine the proxy setting
114 if (getenv("ftp_proxy") == 0)
116 string DefProxy
= _config
->Find("Acquire::ftp::Proxy");
117 string SpecificProxy
= _config
->Find("Acquire::ftp::Proxy::" + ServerName
.Host
);
118 if (SpecificProxy
.empty() == false)
120 if (SpecificProxy
== "DIRECT")
123 Proxy
= SpecificProxy
;
129 Proxy
= getenv("ftp_proxy");
131 // Parse no_proxy, a , separated list of domains
132 if (getenv("no_proxy") != 0)
134 if (CheckDomainList(ServerName
.Host
,getenv("no_proxy")) == true)
138 // Determine what host and port to use based on the proxy settings
141 if (Proxy
.empty() == true)
143 if (ServerName
.Port
!= 0)
144 Port
= ServerName
.Port
;
145 Host
= ServerName
.Host
;
154 /* Connect to the remote server. Since FTP is connection oriented we
155 want to make sure we get a new server every time we reconnect */
157 if (Connect(Host
,Port
,"ftp",21,ServerFd
,TimeOut
,Owner
) == false)
160 // Login must be before getpeername otherwise dante won't work.
161 Owner
->Status("Logging in");
164 // Get the remote server's address
165 PeerAddrLen
= sizeof(PeerAddr
);
166 if (getpeername(ServerFd
,(sockaddr
*)&PeerAddr
,&PeerAddrLen
) != 0)
167 return _error
->Errno("getpeername","Unable to determine the peer name");
169 // Get the local machine's address
170 ServerAddrLen
= sizeof(ServerAddr
);
171 if (getsockname(ServerFd
,(sockaddr
*)&ServerAddr
,&ServerAddrLen
) != 0)
172 return _error
->Errno("getsockname","Unable to determine the local name");
177 // FTPConn::Login - Login to the remote server /*{{{*/
178 // ---------------------------------------------------------------------
179 /* This performs both normal login and proxy login using a simples script
180 stored in the config file. */
181 bool FTPConn::Login()
186 // Setup the variables needed for authentication
187 string User
= "anonymous";
188 string Pass
= "apt_get_ftp_2.1@debian.linux.user";
190 // Fill in the user/pass
191 if (ServerName
.User
.empty() == false)
192 User
= ServerName
.User
;
193 if (ServerName
.Password
.empty() == false)
194 Pass
= ServerName
.Password
;
196 // Perform simple login
197 if (Proxy
.empty() == true)
199 // Read the initial response
200 if (ReadResp(Tag
,Msg
) == false)
203 return _error
->Error("Server refused our connection and said: %s",Msg
.c_str());
206 if (WriteMsg(Tag
,Msg
,"USER %s",User
.c_str()) == false)
209 return _error
->Error("USER failed, server said: %s",Msg
.c_str());
212 if (WriteMsg(Tag
,Msg
,"PASS %s",Pass
.c_str()) == false)
215 return _error
->Error("PASS failed, server said: %s",Msg
.c_str());
217 // Enter passive mode
218 if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true)
219 TryPassive
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true);
221 TryPassive
= _config
->FindB("Acquire::FTP::Passive",true);
225 // Read the initial response
226 if (ReadResp(Tag
,Msg
) == false)
229 return _error
->Error("Server refused our connection and said: %s",Msg
.c_str());
231 // Perform proxy script execution
232 Configuration::Item
const *Opts
= _config
->Tree("Acquire::ftp::ProxyLogin");
233 if (Opts
== 0 || Opts
->Child
== 0)
234 return _error
->Error("A proxy server was specified but no login "
235 "script, Acquire::ftp::ProxyLogin is empty.");
238 // Iterate over the entire login script
239 for (; Opts
!= 0; Opts
= Opts
->Next
)
241 if (Opts
->Value
.empty() == true)
244 // Substitute the variables into the command
246 if (ServerName
.Port
!= 0)
247 sprintf(SitePort
,"%u",ServerName
.Port
);
249 strcpy(SitePort
,"21");
250 string Tmp
= Opts
->Value
;
251 Tmp
= SubstVar(Tmp
,"$(PROXY_USER)",Proxy
.User
);
252 Tmp
= SubstVar(Tmp
,"$(PROXY_PASS)",Proxy
.Password
);
253 Tmp
= SubstVar(Tmp
,"$(SITE_USER)",User
);
254 Tmp
= SubstVar(Tmp
,"$(SITE_PASS)",Pass
);
255 Tmp
= SubstVar(Tmp
,"$(SITE_PORT)",SitePort
);
256 Tmp
= SubstVar(Tmp
,"$(SITE)",ServerName
.Host
);
259 if (WriteMsg(Tag
,Msg
,"%s",Tmp
.c_str()) == false)
262 return _error
->Error("Login script command '%s' failed, server said: %s",Tmp
.c_str(),Msg
.c_str());
265 // Enter passive mode
267 if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true)
268 TryPassive
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true);
271 if (_config
->Exists("Acquire::FTP::Proxy::Passive") == true)
272 TryPassive
= _config
->FindB("Acquire::FTP::Proxy::Passive",true);
274 TryPassive
= _config
->FindB("Acquire::FTP::Passive",true);
278 // Force the use of extended commands
279 if (_config
->Exists("Acquire::FTP::ForceExtended::" + ServerName
.Host
) == true)
280 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended::" + ServerName
.Host
,true);
282 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended",false);
285 if (WriteMsg(Tag
,Msg
,"TYPE I") == false)
288 return _error
->Error("TYPE failed, server said: %s",Msg
.c_str());
293 // FTPConn::ReadLine - Read a line from the server /*{{{*/
294 // ---------------------------------------------------------------------
295 /* This performs a very simple buffered read. */
296 bool FTPConn::ReadLine(string
&Text
)
302 while (Len
< sizeof(Buffer
))
304 // Scan the buffer for a new line
305 for (unsigned int I
= 0; I
!= Len
; I
++)
307 // Escape some special chars
312 if (Buffer
[I
] != '\n')
316 Text
= string(Buffer
,I
);
317 memmove(Buffer
,Buffer
+I
,Len
- I
);
322 // Wait for some data..
323 if (WaitFd(ServerFd
,false,TimeOut
) == false)
326 return _error
->Error("Connection timeout");
330 int Res
= read(ServerFd
,Buffer
+ Len
,sizeof(Buffer
) - Len
);
332 _error
->Error("Server closed the connection");
335 _error
->Errno("read","Read error");
342 return _error
->Error("A response overflowed the buffer.");
345 // FTPConn::ReadResp - Read a full response from the server /*{{{*/
346 // ---------------------------------------------------------------------
347 /* This reads a reply code from the server, it handles both p */
348 bool FTPConn::ReadResp(unsigned int &Ret
,string
&Text
)
350 // Grab the first line of the response
352 if (ReadLine(Msg
) == false)
357 Ret
= strtol(Msg
.c_str(),&End
,10);
358 if (End
- Msg
.c_str() != 3)
359 return _error
->Error("Protocol corruption");
362 Text
= Msg
.c_str()+4;
366 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
371 return _error
->Error("Protocol corruption");
373 /* Okay, here we do the continued message trick. This is foolish, but
374 proftpd follows the protocol as specified and wu-ftpd doesn't, so
375 we filter. I wonder how many clients break if you use proftpd and
376 put a '- in the 3rd spot in the message? */
378 strncpy(Leader
,Msg
.c_str(),3);
380 while (ReadLine(Msg
) == true)
382 // Short, it must be using RFC continuation..
383 if (Msg
.length() < 4)
390 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == ' ')
392 Text
+= Msg
.c_str()+4;
396 // This message has the wu-ftpd style reply code prefixed
397 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == '-')
399 Text
+= Msg
.c_str()+4;
403 // Must be RFC style prefixing
407 if (Debug
== true && _error
->PendingError() == false)
408 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
410 return !_error
->PendingError();
413 // FTPConn::WriteMsg - Send a message to the server /*{{{*/
414 // ---------------------------------------------------------------------
415 /* Simple printf like function.. */
416 bool FTPConn::WriteMsg(unsigned int &Ret
,string
&Text
,const char *Fmt
,...)
421 // sprintf the description
423 vsnprintf(S
,sizeof(S
) - 4,Fmt
,args
);
427 cerr
<< "-> '" << QuoteString(S
,"") << "'" << endl
;
430 unsigned long Len
= strlen(S
);
431 unsigned long Start
= 0;
434 if (WaitFd(ServerFd
,true,TimeOut
) == false)
437 return _error
->Error("Connection timeout");
440 int Res
= write(ServerFd
,S
+ Start
,Len
);
443 _error
->Errno("write","Write Error");
452 return ReadResp(Ret
,Text
);
455 // FTPConn::GoPasv - Enter Passive mode /*{{{*/
456 // ---------------------------------------------------------------------
457 /* Try to enter passive mode, the return code does not indicate if passive
458 mode could or could not be established, only if there was a fatal error.
459 We have to enter passive mode every time we make a data connection :| */
460 bool FTPConn::GoPasv()
462 /* The PASV command only works on IPv4 sockets, even though it could
463 in theory suppory IPv6 via an all zeros reply */
464 if (((struct sockaddr
*)&PeerAddr
)->sa_family
!= AF_INET
||
465 ForceExtended
== true)
469 freeaddrinfo(PasvAddr
);
472 // Try to enable pasv mode
475 if (WriteMsg(Tag
,Msg
,"PASV") == false)
478 // Unsupported function
479 string::size_type Pos
= Msg
.find('(');
480 if (Tag
>= 400 || Pos
== string::npos
)
484 unsigned a0
,a1
,a2
,a3
,p0
,p1
;
485 if (sscanf(Msg
.c_str() + Pos
,"(%u,%u,%u,%u,%u,%u)",&a0
,&a1
,&a2
,&a3
,&p0
,&p1
) != 6)
488 /* Some evil servers return 0 to mean their addr. We can actually speak
489 to these servers natively using IPv6 */
490 if (a0
== 0 && a1
== 0 && a2
== 0 && a3
== 0)
492 // Get the IP in text form
493 char Name
[NI_MAXHOST
];
494 char Service
[NI_MAXSERV
];
495 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
496 Name
,sizeof(Name
),Service
,sizeof(Service
),
497 NI_NUMERICHOST
|NI_NUMERICSERV
);
499 struct addrinfo Hints
;
500 memset(&Hints
,0,sizeof(Hints
));
501 Hints
.ai_socktype
= SOCK_STREAM
;
502 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
503 Hints
.ai_flags
|= AI_NUMERICHOST
;
505 // Get a new passive address.
507 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
508 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
513 struct addrinfo Hints
;
514 memset(&Hints
,0,sizeof(Hints
));
515 Hints
.ai_socktype
= SOCK_STREAM
;
516 Hints
.ai_family
= AF_INET
;
517 Hints
.ai_flags
|= AI_NUMERICHOST
;
519 // Get a new passive address.
521 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
523 snprintf(Name
,sizeof(Name
),"%u.%u.%u.%u",a0
,a1
,a2
,a3
);
524 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
529 // FTPConn::ExtGoPasv - Enter Extended Passive mode /*{{{*/
530 // ---------------------------------------------------------------------
531 /* Try to enter extended passive mode. See GoPasv above and RFC 2428 */
532 bool FTPConn::ExtGoPasv()
535 freeaddrinfo(PasvAddr
);
538 // Try to enable pasv mode
541 if (WriteMsg(Tag
,Msg
,"EPSV") == false)
544 // Unsupported function
545 string::size_type Pos
= Msg
.find('(');
546 if (Tag
>= 400 || Pos
== string::npos
)
550 string::const_iterator List
[4];
553 for (string::const_iterator I
= Msg
.begin() + Pos
; I
< Msg
.end(); I
++)
565 unsigned long Proto
= 0;
566 unsigned long Port
= 0;
568 IP
= string(List
[1]+1,List
[2]);
569 Port
= atoi(string(List
[2]+1,List
[3]).c_str());
570 if (IP
.empty() == false)
571 Proto
= atoi(string(List
[0]+1,List
[1]).c_str());
576 // String version of the port
578 snprintf(PStr
,sizeof(PStr
),"%lu",Port
);
580 // Get the IP in text form
581 struct addrinfo Hints
;
582 memset(&Hints
,0,sizeof(Hints
));
583 Hints
.ai_socktype
= SOCK_STREAM
;
584 Hints
.ai_flags
|= AI_NUMERICHOST
;
586 /* The RFC defined case, connect to the old IP/protocol using the
588 if (IP
.empty() == true)
590 // Get the IP in text form
591 char Name
[NI_MAXHOST
];
592 char Service
[NI_MAXSERV
];
593 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
594 Name
,sizeof(Name
),Service
,sizeof(Service
),
595 NI_NUMERICHOST
|NI_NUMERICSERV
);
597 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
603 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
604 if (AFMap
[J
].IETFFamily
== Proto
)
605 Hints
.ai_family
= AFMap
[J
].Family
;
606 if (Hints
.ai_family
== 0)
610 // Get a new passive address.
612 if ((Res
= getaddrinfo(IP
.c_str(),PStr
,&Hints
,&PasvAddr
)) != 0)
618 // FTPConn::Size - Return the size of a file /*{{{*/
619 // ---------------------------------------------------------------------
620 /* Grab the file size from the server, 0 means no size or empty file */
621 bool FTPConn::Size(const char *Path
,unsigned long &Size
)
627 if (WriteMsg(Tag
,Msg
,"SIZE %s",Path
) == false)
631 Size
= strtol(Msg
.c_str(),&End
,10);
632 if (Tag
>= 400 || End
== Msg
.c_str())
637 // FTPConn::ModTime - Return the modification time of the file /*{{{*/
638 // ---------------------------------------------------------------------
639 /* Like Size no error is returned if the command is not supported. If the
640 command fails then time is set to the current time of day to fool
642 bool FTPConn::ModTime(const char *Path
, time_t &Time
)
646 // Query the mod time
649 if (WriteMsg(Tag
,Msg
,"MDTM %s",Path
) == false)
651 if (Tag
>= 400 || Msg
.empty() == true || isdigit(Msg
[0]) == 0)
659 // FTPConn::CreateDataFd - Get a data connection /*{{{*/
660 // ---------------------------------------------------------------------
661 /* Create the data connection. Call FinalizeDataFd after this though.. */
662 bool FTPConn::CreateDataFd()
667 // Attempt to enter passive mode.
668 if (TryPassive
== true)
670 if (GoPasv() == false)
673 // Oops, didn't work out, don't bother trying again.
682 if ((DataFd
= socket(PasvAddr
->ai_family
,PasvAddr
->ai_socktype
,
683 PasvAddr
->ai_protocol
)) < 0)
684 return _error
->Errno("socket","Could not create a socket");
686 // Connect to the server
687 SetNonBlock(DataFd
,true);
688 if (connect(DataFd
,PasvAddr
->ai_addr
,PasvAddr
->ai_addrlen
) < 0 &&
689 errno
!= EINPROGRESS
)
690 return _error
->Errno("socket","Could not create a socket");
692 /* This implements a timeout for connect by opening the connection
694 if (WaitFd(DataFd
,true,TimeOut
) == false)
695 return _error
->Error("Could not connect data socket, connection timed out");
697 unsigned int Len
= sizeof(Err
);
698 if (getsockopt(DataFd
,SOL_SOCKET
,SO_ERROR
,&Err
,&Len
) != 0)
699 return _error
->Errno("getsockopt","Failed");
701 return _error
->Error("Could not connect passive socket.");
710 // Get the information for a listening socket.
711 struct addrinfo
*BindAddr
= 0;
712 struct addrinfo Hints
;
713 memset(&Hints
,0,sizeof(Hints
));
714 Hints
.ai_socktype
= SOCK_STREAM
;
715 Hints
.ai_flags
|= AI_PASSIVE
;
716 Hints
.ai_family
= ((struct sockaddr
*)&ServerAddr
)->sa_family
;
718 if ((Res
= getaddrinfo(0,"0",&Hints
,&BindAddr
)) != 0)
719 return _error
->Error("getaddrinfo was unable to get a listening socket");
721 // Construct the socket
722 if ((DataListenFd
= socket(BindAddr
->ai_family
,BindAddr
->ai_socktype
,
723 BindAddr
->ai_protocol
)) < 0)
725 freeaddrinfo(BindAddr
);
726 return _error
->Errno("socket","Could not create a socket");
730 if (bind(DataListenFd
,BindAddr
->ai_addr
,BindAddr
->ai_addrlen
) < 0)
732 freeaddrinfo(BindAddr
);
733 return _error
->Errno("bind","Could not bind a socket");
735 freeaddrinfo(BindAddr
);
736 if (listen(DataListenFd
,1) < 0)
737 return _error
->Errno("listen","Could not listen on the socket");
738 SetNonBlock(DataListenFd
,true);
740 // Determine the name to send to the remote
741 struct sockaddr_storage Addr
;
742 socklen_t AddrLen
= sizeof(Addr
);
743 if (getsockname(DataListenFd
,(sockaddr
*)&Addr
,&AddrLen
) < 0)
744 return _error
->Errno("getsockname","Could not determine the socket's name");
746 // Reverse the address. We need the server address and the data port.
747 char Name
[NI_MAXHOST
];
748 char Service
[NI_MAXSERV
];
749 char Service2
[NI_MAXSERV
];
750 getnameinfo((struct sockaddr
*)&Addr
,AddrLen
,
751 Name
,sizeof(Name
),Service
,sizeof(Service
),
752 NI_NUMERICHOST
|NI_NUMERICSERV
);
753 getnameinfo((struct sockaddr
*)&ServerAddr
,ServerAddrLen
,
754 Name
,sizeof(Name
),Service2
,sizeof(Service2
),
755 NI_NUMERICHOST
|NI_NUMERICSERV
);
757 // Send off an IPv4 address in the old port format
758 if (((struct sockaddr
*)&Addr
)->sa_family
== AF_INET
&&
759 ForceExtended
== false)
761 // Convert the dots in the quad into commas
762 for (char *I
= Name
; *I
!= 0; I
++)
765 unsigned long Port
= atoi(Service
);
767 // Send the port command
770 if (WriteMsg(Tag
,Msg
,"PORT %s,%d,%d",
772 (int)(Port
>> 8) & 0xff, (int)(Port
& 0xff)) == false)
775 return _error
->Error("Unable to send PORT command");
779 // Construct an EPRT command
781 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
782 if (AFMap
[J
].Family
== ((struct sockaddr
*)&Addr
)->sa_family
)
783 Proto
= AFMap
[J
].IETFFamily
;
785 return _error
->Error("Unkonwn address family %u (AF_*)",
786 ((struct sockaddr
*)&Addr
)->sa_family
);
788 // Send the EPRT command
791 if (WriteMsg(Tag
,Msg
,"EPRT |%u|%s|%s|",Proto
,Name
,Service
) == false)
794 return _error
->Error("EPRT failed, server said: %s",Msg
.c_str());
798 // FTPConn::Finalize - Complete the Data connection /*{{{*/
799 // ---------------------------------------------------------------------
800 /* If the connection is in port mode this waits for the other end to hook
802 bool FTPConn::Finalize()
804 // Passive mode? Do nothing
808 // Close any old socket..
812 // Wait for someone to connect..
813 if (WaitFd(DataListenFd
,false,TimeOut
) == false)
814 return _error
->Error("Data socket connect timed out");
816 // Accept the connection
817 struct sockaddr_in Addr
;
818 socklen_t Len
= sizeof(Addr
);
819 DataFd
= accept(DataListenFd
,(struct sockaddr
*)&Addr
,&Len
);
821 return _error
->Errno("accept","Unable to accept connection");
829 // FTPConn::Get - Get a file /*{{{*/
830 // ---------------------------------------------------------------------
831 /* This opens a data connection, sends REST and RETR and then
832 transfers the file over. */
833 bool FTPConn::Get(const char *Path
,FileFd
&To
,unsigned long Resume
,
834 Hashes
&Hash
,bool &Missing
)
837 if (CreateDataFd() == false)
844 if (WriteMsg(Tag
,Msg
,"REST %u",Resume
) == false)
850 if (To
.Truncate(Resume
) == false)
853 if (To
.Seek(0) == false)
858 if (Hash
.AddFD(To
.Fd(),Resume
) == false)
860 _error
->Errno("read","Problem hashing file");
865 // Send the get command
866 if (WriteMsg(Tag
,Msg
,"RETR %s",Path
) == false)
873 return _error
->Error("Unable to fetch file, server said '%s'",Msg
.c_str());
876 // Finish off the data connection
877 if (Finalize() == false)
881 unsigned char Buffer
[4096];
884 // Wait for some data..
885 if (WaitFd(DataFd
,false,TimeOut
) == false)
888 return _error
->Error("Data socket timed out");
892 int Res
= read(DataFd
,Buffer
,sizeof(Buffer
));
902 Hash
.Add(Buffer
,Res
);
903 if (To
.Write(Buffer
,Res
) == false)
914 // Read the closing message from the server
915 if (ReadResp(Tag
,Msg
) == false)
918 return _error
->Error("Data transfer failed, server said '%s'",Msg
.c_str());
923 // FtpMethod::FtpMethod - Constructor /*{{{*/
924 // ---------------------------------------------------------------------
926 FtpMethod::FtpMethod() : pkgAcqMethod("1.0",SendConfig
)
928 signal(SIGTERM
,SigTerm
);
929 signal(SIGINT
,SigTerm
);
935 // FtpMethod::SigTerm - Handle a fatal signal /*{{{*/
936 // ---------------------------------------------------------------------
937 /* This closes and timestamps the open file. This is neccessary to get
938 resume behavoir on user abort */
939 void FtpMethod::SigTerm(int)
947 UBuf
.actime
= FailTime
;
948 UBuf
.modtime
= FailTime
;
949 utime(FailFile
.c_str(),&UBuf
);
954 // FtpMethod::Configuration - Handle a configuration message /*{{{*/
955 // ---------------------------------------------------------------------
956 /* We stash the desired pipeline depth */
957 bool FtpMethod::Configuration(string Message
)
959 if (pkgAcqMethod::Configuration(Message
) == false)
962 TimeOut
= _config
->FindI("Acquire::Ftp::Timeout",TimeOut
);
966 // FtpMethod::Fetch - Fetch a file /*{{{*/
967 // ---------------------------------------------------------------------
968 /* Fetch a single file, called by the base class.. */
969 bool FtpMethod::Fetch(FetchItem
*Itm
)
972 const char *File
= Get
.Path
.c_str();
974 Res
.Filename
= Itm
->DestFile
;
977 // Connect to the server
978 if (Server
== 0 || Server
->Comp(Get
) == false)
981 Server
= new FTPConn(Get
);
984 // Could not connect is a transient error..
985 if (Server
->Open(this) == false)
992 // Get the files information
995 if (Server
->Size(File
,Size
) == false ||
996 Server
->ModTime(File
,FailTime
) == false)
1003 // See if it is an IMS hit
1004 if (Itm
->LastModified
== FailTime
)
1012 // See if the file exists
1014 if (stat(Itm
->DestFile
.c_str(),&Buf
) == 0)
1016 if (Size
== (unsigned)Buf
.st_size
&& FailTime
== Buf
.st_mtime
)
1018 Res
.Size
= Buf
.st_size
;
1019 Res
.LastModified
= Buf
.st_mtime
;
1020 Res
.ResumePoint
= Buf
.st_size
;
1026 if (FailTime
== Buf
.st_mtime
&& Size
> (unsigned)Buf
.st_size
)
1027 Res
.ResumePoint
= Buf
.st_size
;
1033 FileFd
Fd(Itm
->DestFile
,FileFd::WriteAny
);
1034 if (_error
->PendingError() == true)
1039 FailFile
= Itm
->DestFile
;
1040 FailFile
.c_str(); // Make sure we dont do a malloc in the signal handler
1044 if (Server
->Get(File
,Fd
,Res
.ResumePoint
,Hash
,Missing
) == false)
1049 struct utimbuf UBuf
;
1050 UBuf
.actime
= FailTime
;
1051 UBuf
.modtime
= FailTime
;
1052 utime(FailFile
.c_str(),&UBuf
);
1054 // If the file is missing we hard fail otherwise transient fail
1055 if (Missing
== true)
1061 Res
.Size
= Fd
.Size();
1064 Res
.LastModified
= FailTime
;
1065 Res
.TakeHashes(Hash
);
1068 struct utimbuf UBuf
;
1069 UBuf
.actime
= FailTime
;
1070 UBuf
.modtime
= FailTime
;
1071 utime(Queue
->DestFile
.c_str(),&UBuf
);
1080 int main(int argc
,const char *argv
[])
1082 /* See if we should be come the http client - we do this for http
1084 if (getenv("ftp_proxy") != 0)
1086 URI Proxy
= string(getenv("ftp_proxy"));
1088 // Run the HTTP method
1089 if (Proxy
.Access
== "http")
1091 // Copy over the environment setting
1093 snprintf(S
,sizeof(S
),"http_proxy=%s",getenv("ftp_proxy"));
1095 putenv("no_proxy=");
1097 // Run the http method
1098 string Path
= flNotFile(argv
[0]) + "/http";
1099 execl(Path
.c_str(),Path
.c_str(),0);
1100 cerr
<< "Unable to invoke " << Path
<< endl
;