]>
git.saurik.com Git - apt.git/blob - methods/ftp.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: ftp.cc,v 1.27 2001/04/10 04:51:50 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>
33 #include <netinet/in.h>
34 #include <sys/socket.h>
35 #include <arpa/inet.h>
38 #include "rfc2553emu.h"
43 /* This table is for the EPRT and EPSV commands, it maps the OS address
44 family to the IETF address families */
48 unsigned long IETFFamily
;
52 struct AFMap AFMap
[] = {{AF_INET
,1},{}};
54 struct AFMap AFMap
[] = {{AF_INET
,1},{AF_INET6
,2},{}};
57 unsigned long TimeOut
= 120;
59 string
FtpMethod::FailFile
;
60 int FtpMethod::FailFd
= -1;
61 time_t FtpMethod::FailTime
= 0;
63 // FTPConn::FTPConn - Constructor /*{{{*/
64 // ---------------------------------------------------------------------
66 FTPConn::FTPConn(URI Srv
) : Len(0), ServerFd(-1), DataFd(-1),
67 DataListenFd(-1), ServerName(Srv
)
69 Debug
= _config
->FindB("Debug::Acquire::Ftp",false);
73 // FTPConn::~FTPConn - Destructor /*{{{*/
74 // ---------------------------------------------------------------------
81 // FTPConn::Close - Close down the connection /*{{{*/
82 // ---------------------------------------------------------------------
83 /* Just tear down the socket and data socket */
94 freeaddrinfo(PasvAddr
);
98 // FTPConn::Open - Open a new connection /*{{{*/
99 // ---------------------------------------------------------------------
100 /* Connect to the server using a non-blocking connection and perform a
102 bool FTPConn::Open(pkgAcqMethod
*Owner
)
104 // Use the already open connection if possible.
110 // Determine the proxy setting
111 if (getenv("ftp_proxy") == 0)
113 string DefProxy
= _config
->Find("Acquire::ftp::Proxy");
114 string SpecificProxy
= _config
->Find("Acquire::ftp::Proxy::" + ServerName
.Host
);
115 if (SpecificProxy
.empty() == false)
117 if (SpecificProxy
== "DIRECT")
120 Proxy
= SpecificProxy
;
126 Proxy
= getenv("ftp_proxy");
128 // Parse no_proxy, a , separated list of domains
129 if (getenv("no_proxy") != 0)
131 if (CheckDomainList(ServerName
.Host
,getenv("no_proxy")) == true)
135 // Determine what host and port to use based on the proxy settings
138 if (Proxy
.empty() == true)
140 if (ServerName
.Port
!= 0)
141 Port
= ServerName
.Port
;
142 Host
= ServerName
.Host
;
151 /* Connect to the remote server. Since FTP is connection oriented we
152 want to make sure we get a new server every time we reconnect */
154 if (Connect(Host
,Port
,"ftp",21,ServerFd
,TimeOut
,Owner
) == false)
157 // Login must be before getpeername otherwise dante won't work.
158 Owner
->Status("Logging in");
161 // Get the remote server's address
162 PeerAddrLen
= sizeof(PeerAddr
);
163 if (getpeername(ServerFd
,(sockaddr
*)&PeerAddr
,&PeerAddrLen
) != 0)
164 return _error
->Errno("getpeername","Unable to determine the peer name");
166 // Get the local machine's address
167 ServerAddrLen
= sizeof(ServerAddr
);
168 if (getsockname(ServerFd
,(sockaddr
*)&ServerAddr
,&ServerAddrLen
) != 0)
169 return _error
->Errno("getsockname","Unable to determine the local name");
174 // FTPConn::Login - Login to the remote server /*{{{*/
175 // ---------------------------------------------------------------------
176 /* This performs both normal login and proxy login using a simples script
177 stored in the config file. */
178 bool FTPConn::Login()
183 // Setup the variables needed for authentication
184 string User
= "anonymous";
185 string Pass
= "apt_get_ftp_2.1@debian.linux.user";
187 // Fill in the user/pass
188 if (ServerName
.User
.empty() == false)
189 User
= ServerName
.User
;
190 if (ServerName
.Password
.empty() == false)
191 Pass
= ServerName
.Password
;
193 // Perform simple login
194 if (Proxy
.empty() == true)
196 // Read the initial response
197 if (ReadResp(Tag
,Msg
) == false)
200 return _error
->Error("Server refused our connection and said: %s",Msg
.c_str());
203 if (WriteMsg(Tag
,Msg
,"USER %s",User
.c_str()) == false)
206 return _error
->Error("USER failed, server said: %s",Msg
.c_str());
209 if (WriteMsg(Tag
,Msg
,"PASS %s",Pass
.c_str()) == false)
212 return _error
->Error("PASS failed, server said: %s",Msg
.c_str());
214 // Enter passive mode
215 if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true)
216 TryPassive
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true);
218 TryPassive
= _config
->FindB("Acquire::FTP::Passive",true);
222 // Read the initial response
223 if (ReadResp(Tag
,Msg
) == false)
226 return _error
->Error("Server refused our connection and said: %s",Msg
.c_str());
228 // Perform proxy script execution
229 Configuration::Item
const *Opts
= _config
->Tree("Acquire::ftp::ProxyLogin");
230 if (Opts
== 0 || Opts
->Child
== 0)
231 return _error
->Error("A proxy server was specified but no login "
232 "script, Acquire::ftp::ProxyLogin is empty.");
235 // Iterate over the entire login script
236 for (; Opts
!= 0; Opts
= Opts
->Next
)
238 if (Opts
->Value
.empty() == true)
241 // Substitute the variables into the command
243 if (ServerName
.Port
!= 0)
244 sprintf(SitePort
,"%u",ServerName
.Port
);
246 strcpy(SitePort
,"21");
247 string Tmp
= Opts
->Value
;
248 Tmp
= SubstVar(Tmp
,"$(PROXY_USER)",Proxy
.User
);
249 Tmp
= SubstVar(Tmp
,"$(PROXY_PASS)",Proxy
.Password
);
250 Tmp
= SubstVar(Tmp
,"$(SITE_USER)",User
);
251 Tmp
= SubstVar(Tmp
,"$(SITE_PASS)",Pass
);
252 Tmp
= SubstVar(Tmp
,"$(SITE_PORT)",SitePort
);
253 Tmp
= SubstVar(Tmp
,"$(SITE)",ServerName
.Host
);
256 if (WriteMsg(Tag
,Msg
,"%s",Tmp
.c_str()) == false)
259 return _error
->Error("Login script command '%s' failed, server said: %s",Tmp
.c_str(),Msg
.c_str());
262 // Enter passive mode
264 if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true)
265 TryPassive
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true);
268 if (_config
->Exists("Acquire::FTP::Proxy::Passive") == true)
269 TryPassive
= _config
->FindB("Acquire::FTP::Proxy::Passive",true);
271 TryPassive
= _config
->FindB("Acquire::FTP::Passive",true);
275 // Force the use of extended commands
276 if (_config
->Exists("Acquire::FTP::ForceExtended::" + ServerName
.Host
) == true)
277 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended::" + ServerName
.Host
,true);
279 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended",false);
282 if (WriteMsg(Tag
,Msg
,"TYPE I") == false)
285 return _error
->Error("TYPE failed, server said: %s",Msg
.c_str());
290 // FTPConn::ReadLine - Read a line from the server /*{{{*/
291 // ---------------------------------------------------------------------
292 /* This performs a very simple buffered read. */
293 bool FTPConn::ReadLine(string
&Text
)
299 while (Len
< sizeof(Buffer
))
301 // Scan the buffer for a new line
302 for (unsigned int I
= 0; I
!= Len
; I
++)
304 // Escape some special chars
309 if (Buffer
[I
] != '\n')
313 Text
= string(Buffer
,I
);
314 memmove(Buffer
,Buffer
+I
,Len
- I
);
319 // Wait for some data..
320 if (WaitFd(ServerFd
,false,TimeOut
) == false)
323 return _error
->Error("Connection timeout");
327 int Res
= read(ServerFd
,Buffer
+ Len
,sizeof(Buffer
) - Len
);
329 _error
->Error("Server closed the connection");
332 _error
->Errno("read","Read error");
339 return _error
->Error("A response overflowed the buffer.");
342 // FTPConn::ReadResp - Read a full response from the server /*{{{*/
343 // ---------------------------------------------------------------------
344 /* This reads a reply code from the server, it handles both p */
345 bool FTPConn::ReadResp(unsigned int &Ret
,string
&Text
)
347 // Grab the first line of the response
349 if (ReadLine(Msg
) == false)
354 Ret
= strtol(Msg
.c_str(),&End
,10);
355 if (End
- Msg
.c_str() != 3)
356 return _error
->Error("Protocol corruption");
359 Text
= Msg
.c_str()+4;
363 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
368 return _error
->Error("Protocol corruption");
370 /* Okay, here we do the continued message trick. This is foolish, but
371 proftpd follows the protocol as specified and wu-ftpd doesn't, so
372 we filter. I wonder how many clients break if you use proftpd and
373 put a '- in the 3rd spot in the message? */
375 strncpy(Leader
,Msg
.c_str(),3);
377 while (ReadLine(Msg
) == true)
379 // Short, it must be using RFC continuation..
380 if (Msg
.length() < 4)
387 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == ' ')
389 Text
+= Msg
.c_str()+4;
393 // This message has the wu-ftpd style reply code prefixed
394 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == '-')
396 Text
+= Msg
.c_str()+4;
400 // Must be RFC style prefixing
404 if (Debug
== true && _error
->PendingError() == false)
405 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
407 return !_error
->PendingError();
410 // FTPConn::WriteMsg - Send a message to the server /*{{{*/
411 // ---------------------------------------------------------------------
412 /* Simple printf like function.. */
413 bool FTPConn::WriteMsg(unsigned int &Ret
,string
&Text
,const char *Fmt
,...)
418 // sprintf the description
420 vsnprintf(S
,sizeof(S
) - 4,Fmt
,args
);
424 cerr
<< "-> '" << QuoteString(S
,"") << "'" << endl
;
427 unsigned long Len
= strlen(S
);
428 unsigned long Start
= 0;
431 if (WaitFd(ServerFd
,true,TimeOut
) == false)
434 return _error
->Error("Connection timeout");
437 int Res
= write(ServerFd
,S
+ Start
,Len
);
440 _error
->Errno("write","Write Error");
449 return ReadResp(Ret
,Text
);
452 // FTPConn::GoPasv - Enter Passive mode /*{{{*/
453 // ---------------------------------------------------------------------
454 /* Try to enter passive mode, the return code does not indicate if passive
455 mode could or could not be established, only if there was a fatal error.
456 We have to enter passive mode every time we make a data connection :| */
457 bool FTPConn::GoPasv()
459 /* The PASV command only works on IPv4 sockets, even though it could
460 in theory suppory IPv6 via an all zeros reply */
461 if (((struct sockaddr
*)&PeerAddr
)->sa_family
!= AF_INET
||
462 ForceExtended
== true)
466 freeaddrinfo(PasvAddr
);
469 // Try to enable pasv mode
472 if (WriteMsg(Tag
,Msg
,"PASV") == false)
475 // Unsupported function
476 string::size_type Pos
= Msg
.find('(');
477 if (Tag
>= 400 || Pos
== string::npos
)
481 unsigned a0
,a1
,a2
,a3
,p0
,p1
;
482 if (sscanf(Msg
.c_str() + Pos
,"(%u,%u,%u,%u,%u,%u)",&a0
,&a1
,&a2
,&a3
,&p0
,&p1
) != 6)
485 /* Some evil servers return 0 to mean their addr. We can actually speak
486 to these servers natively using IPv6 */
487 if (a0
== 0 && a1
== 0 && a2
== 0 && a3
== 0)
489 // Get the IP in text form
490 char Name
[NI_MAXHOST
];
491 char Service
[NI_MAXSERV
];
492 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
493 Name
,sizeof(Name
),Service
,sizeof(Service
),
494 NI_NUMERICHOST
|NI_NUMERICSERV
);
496 struct addrinfo Hints
;
497 memset(&Hints
,0,sizeof(Hints
));
498 Hints
.ai_socktype
= SOCK_STREAM
;
499 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
500 Hints
.ai_flags
|= AI_NUMERICHOST
;
502 // Get a new passive address.
504 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
505 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
510 struct addrinfo Hints
;
511 memset(&Hints
,0,sizeof(Hints
));
512 Hints
.ai_socktype
= SOCK_STREAM
;
513 Hints
.ai_family
= AF_INET
;
514 Hints
.ai_flags
|= AI_NUMERICHOST
;
516 // Get a new passive address.
518 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
520 snprintf(Name
,sizeof(Name
),"%u.%u.%u.%u",a0
,a1
,a2
,a3
);
521 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
526 // FTPConn::ExtGoPasv - Enter Extended Passive mode /*{{{*/
527 // ---------------------------------------------------------------------
528 /* Try to enter extended passive mode. See GoPasv above and RFC 2428 */
529 bool FTPConn::ExtGoPasv()
532 freeaddrinfo(PasvAddr
);
535 // Try to enable pasv mode
538 if (WriteMsg(Tag
,Msg
,"EPSV") == false)
541 // Unsupported function
542 string::size_type Pos
= Msg
.find('(');
543 if (Tag
>= 400 || Pos
== string::npos
)
547 string::const_iterator List
[4];
550 for (string::const_iterator I
= Msg
.begin() + Pos
; I
< Msg
.end(); I
++)
562 unsigned long Proto
= 0;
563 unsigned long Port
= 0;
565 IP
= string(List
[1]+1,List
[2]);
566 Port
= atoi(string(List
[2]+1,List
[3]).c_str());
567 if (IP
.empty() == false)
568 Proto
= atoi(string(List
[0]+1,List
[1]).c_str());
573 // String version of the port
575 snprintf(PStr
,sizeof(PStr
),"%lu",Port
);
577 // Get the IP in text form
578 struct addrinfo Hints
;
579 memset(&Hints
,0,sizeof(Hints
));
580 Hints
.ai_socktype
= SOCK_STREAM
;
581 Hints
.ai_flags
|= AI_NUMERICHOST
;
583 /* The RFC defined case, connect to the old IP/protocol using the
585 if (IP
.empty() == true)
587 // Get the IP in text form
588 char Name
[NI_MAXHOST
];
589 char Service
[NI_MAXSERV
];
590 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
591 Name
,sizeof(Name
),Service
,sizeof(Service
),
592 NI_NUMERICHOST
|NI_NUMERICSERV
);
594 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
600 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
601 if (AFMap
[J
].IETFFamily
== Proto
)
602 Hints
.ai_family
= AFMap
[J
].Family
;
603 if (Hints
.ai_family
== 0)
607 // Get a new passive address.
609 if ((Res
= getaddrinfo(IP
.c_str(),PStr
,&Hints
,&PasvAddr
)) != 0)
615 // FTPConn::Size - Return the size of a file /*{{{*/
616 // ---------------------------------------------------------------------
617 /* Grab the file size from the server, 0 means no size or empty file */
618 bool FTPConn::Size(const char *Path
,unsigned long &Size
)
624 if (WriteMsg(Tag
,Msg
,"SIZE %s",Path
) == false)
628 Size
= strtol(Msg
.c_str(),&End
,10);
629 if (Tag
>= 400 || End
== Msg
.c_str())
634 // FTPConn::ModTime - Return the modification time of the file /*{{{*/
635 // ---------------------------------------------------------------------
636 /* Like Size no error is returned if the command is not supported. If the
637 command fails then time is set to the current time of day to fool
639 bool FTPConn::ModTime(const char *Path
, time_t &Time
)
643 // Query the mod time
646 if (WriteMsg(Tag
,Msg
,"MDTM %s",Path
) == false)
648 if (Tag
>= 400 || Msg
.empty() == true || isdigit(Msg
[0]) == 0)
656 // FTPConn::CreateDataFd - Get a data connection /*{{{*/
657 // ---------------------------------------------------------------------
658 /* Create the data connection. Call FinalizeDataFd after this though.. */
659 bool FTPConn::CreateDataFd()
664 // Attempt to enter passive mode.
665 if (TryPassive
== true)
667 if (GoPasv() == false)
670 // Oops, didn't work out, don't bother trying again.
679 if ((DataFd
= socket(PasvAddr
->ai_family
,PasvAddr
->ai_socktype
,
680 PasvAddr
->ai_protocol
)) < 0)
681 return _error
->Errno("socket","Could not create a socket");
683 // Connect to the server
684 SetNonBlock(DataFd
,true);
685 if (connect(DataFd
,PasvAddr
->ai_addr
,PasvAddr
->ai_addrlen
) < 0 &&
686 errno
!= EINPROGRESS
)
687 return _error
->Errno("socket","Could not create a socket");
689 /* This implements a timeout for connect by opening the connection
691 if (WaitFd(DataFd
,true,TimeOut
) == false)
692 return _error
->Error("Could not connect data socket, connection timed out");
694 unsigned int Len
= sizeof(Err
);
695 if (getsockopt(DataFd
,SOL_SOCKET
,SO_ERROR
,&Err
,&Len
) != 0)
696 return _error
->Errno("getsockopt","Failed");
698 return _error
->Error("Could not connect passive socket.");
707 // Get the information for a listening socket.
708 struct addrinfo
*BindAddr
= 0;
709 struct addrinfo Hints
;
710 memset(&Hints
,0,sizeof(Hints
));
711 Hints
.ai_socktype
= SOCK_STREAM
;
712 Hints
.ai_flags
|= AI_PASSIVE
;
713 Hints
.ai_family
= ((struct sockaddr
*)&ServerAddr
)->sa_family
;
715 if ((Res
= getaddrinfo(0,"0",&Hints
,&BindAddr
)) != 0)
716 return _error
->Error("getaddrinfo was unable to get a listening socket");
718 // Construct the socket
719 if ((DataListenFd
= socket(BindAddr
->ai_family
,BindAddr
->ai_socktype
,
720 BindAddr
->ai_protocol
)) < 0)
722 freeaddrinfo(BindAddr
);
723 return _error
->Errno("socket","Could not create a socket");
727 if (bind(DataListenFd
,BindAddr
->ai_addr
,BindAddr
->ai_addrlen
) < 0)
729 freeaddrinfo(BindAddr
);
730 return _error
->Errno("bind","Could not bind a socket");
732 freeaddrinfo(BindAddr
);
733 if (listen(DataListenFd
,1) < 0)
734 return _error
->Errno("listen","Could not listen on the socket");
735 SetNonBlock(DataListenFd
,true);
737 // Determine the name to send to the remote
738 struct sockaddr_storage Addr
;
739 socklen_t AddrLen
= sizeof(Addr
);
740 if (getsockname(DataListenFd
,(sockaddr
*)&Addr
,&AddrLen
) < 0)
741 return _error
->Errno("getsockname","Could not determine the socket's name");
743 // Reverse the address. We need the server address and the data port.
744 char Name
[NI_MAXHOST
];
745 char Service
[NI_MAXSERV
];
746 char Service2
[NI_MAXSERV
];
747 getnameinfo((struct sockaddr
*)&Addr
,AddrLen
,
748 Name
,sizeof(Name
),Service
,sizeof(Service
),
749 NI_NUMERICHOST
|NI_NUMERICSERV
);
750 getnameinfo((struct sockaddr
*)&ServerAddr
,ServerAddrLen
,
751 Name
,sizeof(Name
),Service2
,sizeof(Service2
),
752 NI_NUMERICHOST
|NI_NUMERICSERV
);
754 // Send off an IPv4 address in the old port format
755 if (((struct sockaddr
*)&Addr
)->sa_family
== AF_INET
&&
756 ForceExtended
== false)
758 // Convert the dots in the quad into commas
759 for (char *I
= Name
; *I
!= 0; I
++)
762 unsigned long Port
= atoi(Service
);
764 // Send the port command
767 if (WriteMsg(Tag
,Msg
,"PORT %s,%d,%d",
769 (int)(Port
>> 8) & 0xff, (int)(Port
& 0xff)) == false)
772 return _error
->Error("Unable to send PORT command");
776 // Construct an EPRT command
778 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
779 if (AFMap
[J
].Family
== ((struct sockaddr
*)&Addr
)->sa_family
)
780 Proto
= AFMap
[J
].IETFFamily
;
782 return _error
->Error("Unkonwn address family %u (AF_*)",
783 ((struct sockaddr
*)&Addr
)->sa_family
);
785 // Send the EPRT command
788 if (WriteMsg(Tag
,Msg
,"EPRT |%u|%s|%s|",Proto
,Name
,Service
) == false)
791 return _error
->Error("EPRT failed, server said: %s",Msg
.c_str());
795 // FTPConn::Finalize - Complete the Data connection /*{{{*/
796 // ---------------------------------------------------------------------
797 /* If the connection is in port mode this waits for the other end to hook
799 bool FTPConn::Finalize()
801 // Passive mode? Do nothing
805 // Close any old socket..
809 // Wait for someone to connect..
810 if (WaitFd(DataListenFd
,false,TimeOut
) == false)
811 return _error
->Error("Data socket connect timed out");
813 // Accept the connection
814 struct sockaddr_in Addr
;
815 socklen_t Len
= sizeof(Addr
);
816 DataFd
= accept(DataListenFd
,(struct sockaddr
*)&Addr
,&Len
);
818 return _error
->Errno("accept","Unable to accept connection");
826 // FTPConn::Get - Get a file /*{{{*/
827 // ---------------------------------------------------------------------
828 /* This opens a data connection, sends REST and RETR and then
829 transfers the file over. */
830 bool FTPConn::Get(const char *Path
,FileFd
&To
,unsigned long Resume
,
831 Hashes
&Hash
,bool &Missing
)
834 if (CreateDataFd() == false)
841 if (WriteMsg(Tag
,Msg
,"REST %u",Resume
) == false)
847 if (To
.Truncate(Resume
) == false)
850 if (To
.Seek(0) == false)
855 if (Hash
.AddFD(To
.Fd(),Resume
) == false)
857 _error
->Errno("read","Problem hashing file");
862 // Send the get command
863 if (WriteMsg(Tag
,Msg
,"RETR %s",Path
) == false)
870 return _error
->Error("Unable to fetch file, server said '%s'",Msg
.c_str());
873 // Finish off the data connection
874 if (Finalize() == false)
878 unsigned char Buffer
[4096];
881 // Wait for some data..
882 if (WaitFd(DataFd
,false,TimeOut
) == false)
885 return _error
->Error("Data socket timed out");
889 int Res
= read(DataFd
,Buffer
,sizeof(Buffer
));
899 Hash
.Add(Buffer
,Res
);
900 if (To
.Write(Buffer
,Res
) == false)
911 // Read the closing message from the server
912 if (ReadResp(Tag
,Msg
) == false)
915 return _error
->Error("Data transfer failed, server said '%s'",Msg
.c_str());
920 // FtpMethod::FtpMethod - Constructor /*{{{*/
921 // ---------------------------------------------------------------------
923 FtpMethod::FtpMethod() : pkgAcqMethod("1.0",SendConfig
)
925 signal(SIGTERM
,SigTerm
);
926 signal(SIGINT
,SigTerm
);
932 // FtpMethod::SigTerm - Handle a fatal signal /*{{{*/
933 // ---------------------------------------------------------------------
934 /* This closes and timestamps the open file. This is neccessary to get
935 resume behavoir on user abort */
936 void FtpMethod::SigTerm(int)
944 UBuf
.actime
= FailTime
;
945 UBuf
.modtime
= FailTime
;
946 utime(FailFile
.c_str(),&UBuf
);
951 // FtpMethod::Configuration - Handle a configuration message /*{{{*/
952 // ---------------------------------------------------------------------
953 /* We stash the desired pipeline depth */
954 bool FtpMethod::Configuration(string Message
)
956 if (pkgAcqMethod::Configuration(Message
) == false)
959 TimeOut
= _config
->FindI("Acquire::Ftp::Timeout",TimeOut
);
963 // FtpMethod::Fetch - Fetch a file /*{{{*/
964 // ---------------------------------------------------------------------
965 /* Fetch a single file, called by the base class.. */
966 bool FtpMethod::Fetch(FetchItem
*Itm
)
969 const char *File
= Get
.Path
.c_str();
971 Res
.Filename
= Itm
->DestFile
;
974 // Connect to the server
975 if (Server
== 0 || Server
->Comp(Get
) == false)
978 Server
= new FTPConn(Get
);
981 // Could not connect is a transient error..
982 if (Server
->Open(this) == false)
989 // Get the files information
992 if (Server
->Size(File
,Size
) == false ||
993 Server
->ModTime(File
,FailTime
) == false)
1000 // See if it is an IMS hit
1001 if (Itm
->LastModified
== FailTime
)
1009 // See if the file exists
1011 if (stat(Itm
->DestFile
.c_str(),&Buf
) == 0)
1013 if (Size
== (unsigned)Buf
.st_size
&& FailTime
== Buf
.st_mtime
)
1015 Res
.Size
= Buf
.st_size
;
1016 Res
.LastModified
= Buf
.st_mtime
;
1017 Res
.ResumePoint
= Buf
.st_size
;
1023 if (FailTime
== Buf
.st_mtime
&& Size
> (unsigned)Buf
.st_size
)
1024 Res
.ResumePoint
= Buf
.st_size
;
1030 FileFd
Fd(Itm
->DestFile
,FileFd::WriteAny
);
1031 if (_error
->PendingError() == true)
1036 FailFile
= Itm
->DestFile
;
1037 FailFile
.c_str(); // Make sure we dont do a malloc in the signal handler
1041 if (Server
->Get(File
,Fd
,Res
.ResumePoint
,Hash
,Missing
) == false)
1046 struct utimbuf UBuf
;
1047 UBuf
.actime
= FailTime
;
1048 UBuf
.modtime
= FailTime
;
1049 utime(FailFile
.c_str(),&UBuf
);
1051 // If the file is missing we hard fail otherwise transient fail
1052 if (Missing
== true)
1058 Res
.Size
= Fd
.Size();
1061 Res
.LastModified
= FailTime
;
1062 Res
.TakeHashes(Hash
);
1065 struct utimbuf UBuf
;
1066 UBuf
.actime
= FailTime
;
1067 UBuf
.modtime
= FailTime
;
1068 utime(Queue
->DestFile
.c_str(),&UBuf
);
1077 int main(int argc
,const char *argv
[])
1079 /* See if we should be come the http client - we do this for http
1081 if (getenv("ftp_proxy") != 0)
1083 URI Proxy
= string(getenv("ftp_proxy"));
1085 // Run the HTTP method
1086 if (Proxy
.Access
== "http")
1088 // Copy over the environment setting
1090 snprintf(S
,sizeof(S
),"http_proxy=%s",getenv("ftp_proxy"));
1092 putenv("no_proxy=");
1094 // Run the http method
1095 string Path
= flNotFile(argv
[0]) + "/http";
1096 execl(Path
.c_str(),Path
.c_str(),0);
1097 cerr
<< "Unable to invoke " << Path
<< endl
;