]>
git.saurik.com Git - apt.git/blob - methods/ftp.cc
0d617dd8f83385738b2cd6b381ce7f3aa8d0ea7c
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: ftp.cc,v 1.21 2001/02/20 07:03:18 jgg Exp $
4 /* ######################################################################
6 HTTP 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/md5.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 // Determine what host and port to use based on the proxy settings
131 if (Proxy
.empty() == true)
133 if (ServerName
.Port
!= 0)
134 Port
= ServerName
.Port
;
135 Host
= ServerName
.Host
;
144 /* Connect to the remote server. Since FTP is connection oriented we
145 want to make sure we get a new server every time we reconnect */
147 if (Connect(Host
,Port
,"ftp",21,ServerFd
,TimeOut
,Owner
) == false)
150 // Get the remote server's address
151 PeerAddrLen
= sizeof(PeerAddr
);
152 if (getpeername(ServerFd
,(sockaddr
*)&PeerAddr
,&PeerAddrLen
) != 0)
153 return _error
->Errno("getpeername","Unable to determine the peer name");
155 // Get the local machine's address
156 ServerAddrLen
= sizeof(ServerAddr
);
157 if (getsockname(ServerFd
,(sockaddr
*)&ServerAddr
,&ServerAddrLen
) != 0)
158 return _error
->Errno("getsockname","Unable to determine the local name");
160 Owner
->Status("Logging in");
164 // FTPConn::Login - Login to the remote server /*{{{*/
165 // ---------------------------------------------------------------------
166 /* This performs both normal login and proxy login using a simples script
167 stored in the config file. */
168 bool FTPConn::Login()
173 // Setup the variables needed for authentication
174 string User
= "anonymous";
175 string Pass
= "apt_get_ftp_2.0@debian.linux.user";
177 // Fill in the user/pass
178 if (ServerName
.User
.empty() == false)
179 User
= ServerName
.User
;
180 if (ServerName
.Password
.empty() == false)
181 Pass
= ServerName
.Password
;
183 // Perform simple login
184 if (Proxy
.empty() == true)
186 // Read the initial response
187 if (ReadResp(Tag
,Msg
) == false)
190 return _error
->Error("Server refused our connection and said: %s",Msg
.c_str());
193 if (WriteMsg(Tag
,Msg
,"USER %s",User
.c_str()) == false)
196 return _error
->Error("USER failed, server said: %s",Msg
.c_str());
199 if (WriteMsg(Tag
,Msg
,"PASS %s",Pass
.c_str()) == false)
202 return _error
->Error("PASS failed, server said: %s",Msg
.c_str());
204 // Enter passive mode
205 if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true)
206 TryPassive
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true);
208 TryPassive
= _config
->FindB("Acquire::FTP::Passive",true);
212 // Read the initial response
213 if (ReadResp(Tag
,Msg
) == false)
216 return _error
->Error("Server refused our connection and said: %s",Msg
.c_str());
218 // Perform proxy script execution
219 Configuration::Item
const *Opts
= _config
->Tree("Acquire::ftp::ProxyLogin");
220 if (Opts
== 0 || Opts
->Child
== 0)
221 return _error
->Error("A proxy server was specified but no login "
222 "script, Acquire::ftp::ProxyLogin is empty.");
225 // Iterate over the entire login script
226 for (; Opts
!= 0; Opts
= Opts
->Next
)
228 if (Opts
->Value
.empty() == true)
231 // Substitute the variables into the command
233 if (ServerName
.Port
!= 0)
234 sprintf(SitePort
,"%u",ServerName
.Port
);
236 strcpy(SitePort
,"21");
237 string Tmp
= Opts
->Value
;
238 Tmp
= SubstVar(Tmp
,"$(PROXY_USER)",Proxy
.User
);
239 Tmp
= SubstVar(Tmp
,"$(PROXY_PASS)",Proxy
.Password
);
240 Tmp
= SubstVar(Tmp
,"$(SITE_USER)",User
);
241 Tmp
= SubstVar(Tmp
,"$(SITE_PASS)",Pass
);
242 Tmp
= SubstVar(Tmp
,"$(SITE_PORT)",SitePort
);
243 Tmp
= SubstVar(Tmp
,"$(SITE)",ServerName
.Host
);
246 if (WriteMsg(Tag
,Msg
,"%s",Tmp
.c_str()) == false)
249 return _error
->Error("Login script command '%s' failed, server said: %s",Tmp
.c_str(),Msg
.c_str());
252 // Enter passive mode
254 if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true)
255 TryPassive
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true);
258 if (_config
->Exists("Acquire::FTP::Proxy::Passive") == true)
259 TryPassive
= _config
->FindB("Acquire::FTP::Proxy::Passive",true);
261 TryPassive
= _config
->FindB("Acquire::FTP::Passive",true);
265 // Force the use of extended commands
266 if (_config
->Exists("Acquire::FTP::ForceExtended::" + ServerName
.Host
) == true)
267 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended::" + ServerName
.Host
,true);
269 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended",false);
272 if (WriteMsg(Tag
,Msg
,"TYPE I") == false)
275 return _error
->Error("TYPE failed, server said: %s",Msg
.c_str());
280 // FTPConn::ReadLine - Read a line from the server /*{{{*/
281 // ---------------------------------------------------------------------
282 /* This performs a very simple buffered read. */
283 bool FTPConn::ReadLine(string
&Text
)
289 while (Len
< sizeof(Buffer
))
291 // Scan the buffer for a new line
292 for (unsigned int I
= 0; I
!= Len
; I
++)
294 // Escape some special chars
299 if (Buffer
[I
] != '\n')
303 Text
= string(Buffer
,I
);
304 memmove(Buffer
,Buffer
+I
,Len
- I
);
309 // Wait for some data..
310 if (WaitFd(ServerFd
,false,TimeOut
) == false)
313 return _error
->Error("Connection timeout");
317 int Res
= read(ServerFd
,Buffer
+ Len
,sizeof(Buffer
) - Len
);
319 _error
->Error("Server closed the connection");
322 _error
->Errno("read","Read error");
329 return _error
->Error("A response overflowed the buffer.");
332 // FTPConn::ReadResp - Read a full response from the server /*{{{*/
333 // ---------------------------------------------------------------------
334 /* This reads a reply code from the server, it handles both p */
335 bool FTPConn::ReadResp(unsigned int &Ret
,string
&Text
)
337 // Grab the first line of the response
339 if (ReadLine(Msg
) == false)
344 Ret
= strtol(Msg
.c_str(),&End
,10);
345 if (End
- Msg
.c_str() != 3)
346 return _error
->Error("Protocol corruption");
349 Text
= Msg
.c_str()+4;
353 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
358 return _error
->Error("Protocol corruption");
360 /* Okay, here we do the continued message trick. This is foolish, but
361 proftpd follows the protocol as specified and wu-ftpd doesn't, so
362 we filter. I wonder how many clients break if you use proftpd and
363 put a '- in the 3rd spot in the message? */
365 strncpy(Leader
,Msg
.c_str(),3);
367 while (ReadLine(Msg
) == true)
369 // Short, it must be using RFC continuation..
370 if (Msg
.length() < 4)
377 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == ' ')
379 Text
+= Msg
.c_str()+4;
383 // This message has the wu-ftpd style reply code prefixed
384 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == '-')
386 Text
+= Msg
.c_str()+4;
390 // Must be RFC style prefixing
394 if (Debug
== true && _error
->PendingError() == false)
395 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
397 return !_error
->PendingError();
400 // FTPConn::WriteMsg - Send a message to the server /*{{{*/
401 // ---------------------------------------------------------------------
402 /* Simple printf like function.. */
403 bool FTPConn::WriteMsg(unsigned int &Ret
,string
&Text
,const char *Fmt
,...)
408 // sprintf the description
410 vsnprintf(S
,sizeof(S
) - 4,Fmt
,args
);
414 cerr
<< "-> '" << QuoteString(S
,"") << "'" << endl
;
417 unsigned long Len
= strlen(S
);
418 unsigned long Start
= 0;
421 if (WaitFd(ServerFd
,true,TimeOut
) == false)
424 return _error
->Error("Connection timeout");
427 int Res
= write(ServerFd
,S
+ Start
,Len
);
430 _error
->Errno("write","Write Error");
439 return ReadResp(Ret
,Text
);
442 // FTPConn::GoPasv - Enter Passive mode /*{{{*/
443 // ---------------------------------------------------------------------
444 /* Try to enter passive mode, the return code does not indicate if passive
445 mode could or could not be established, only if there was a fatal error.
446 We have to enter passive mode every time we make a data connection :| */
447 bool FTPConn::GoPasv()
449 /* The PASV command only works on IPv4 sockets, even though it could
450 in theory suppory IPv6 via an all zeros reply */
451 if (((struct sockaddr
*)&PeerAddr
)->sa_family
!= AF_INET
||
452 ForceExtended
== true)
456 freeaddrinfo(PasvAddr
);
459 // Try to enable pasv mode
462 if (WriteMsg(Tag
,Msg
,"PASV") == false)
465 // Unsupported function
466 string::size_type Pos
= Msg
.find('(');
467 if (Tag
>= 400 || Pos
== string::npos
)
471 unsigned a0
,a1
,a2
,a3
,p0
,p1
;
472 if (sscanf(Msg
.c_str() + Pos
,"(%u,%u,%u,%u,%u,%u)",&a0
,&a1
,&a2
,&a3
,&p0
,&p1
) != 6)
475 /* Some evil servers return 0 to mean their addr. We can actually speak
476 to these servers natively using IPv6 */
477 if (a0
== 0 && a1
== 0 && a2
== 0 && a3
== 0)
479 // Get the IP in text form
480 char Name
[NI_MAXHOST
];
481 char Service
[NI_MAXSERV
];
482 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
483 Name
,sizeof(Name
),Service
,sizeof(Service
),
484 NI_NUMERICHOST
|NI_NUMERICSERV
);
486 struct addrinfo Hints
;
487 memset(&Hints
,0,sizeof(Hints
));
488 Hints
.ai_socktype
= SOCK_STREAM
;
489 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
490 Hints
.ai_flags
|= AI_NUMERICHOST
;
492 // Get a new passive address.
494 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
495 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
500 struct addrinfo Hints
;
501 memset(&Hints
,0,sizeof(Hints
));
502 Hints
.ai_socktype
= SOCK_STREAM
;
503 Hints
.ai_family
= AF_INET
;
504 Hints
.ai_flags
|= AI_NUMERICHOST
;
506 // Get a new passive address.
508 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
510 snprintf(Name
,sizeof(Name
),"%u.%u.%u.%u",a0
,a1
,a2
,a3
);
511 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
516 // FTPConn::ExtGoPasv - Enter Extended Passive mode /*{{{*/
517 // ---------------------------------------------------------------------
518 /* Try to enter extended passive mode. See GoPasv above and RFC 2428 */
519 bool FTPConn::ExtGoPasv()
522 freeaddrinfo(PasvAddr
);
525 // Try to enable pasv mode
528 if (WriteMsg(Tag
,Msg
,"EPSV") == false)
531 // Unsupported function
532 string::size_type Pos
= Msg
.find('(');
533 if (Tag
>= 400 || Pos
== string::npos
)
537 string::const_iterator List
[4];
540 for (string::const_iterator I
= Msg
.begin() + Pos
; I
< Msg
.end(); I
++)
552 unsigned long Proto
= 0;
553 unsigned long Port
= 0;
555 IP
= string(List
[1]+1,List
[2]);
556 Port
= atoi(string(List
[2]+1,List
[3]).c_str());
557 if (IP
.empty() == false)
558 Proto
= atoi(string(List
[0]+1,List
[1]).c_str());
563 // String version of the port
565 snprintf(PStr
,sizeof(PStr
),"%lu",Port
);
567 // Get the IP in text form
568 struct addrinfo Hints
;
569 memset(&Hints
,0,sizeof(Hints
));
570 Hints
.ai_socktype
= SOCK_STREAM
;
571 Hints
.ai_flags
|= AI_NUMERICHOST
;
573 /* The RFC defined case, connect to the old IP/protocol using the
575 if (IP
.empty() == true)
577 // Get the IP in text form
578 char Name
[NI_MAXHOST
];
579 char Service
[NI_MAXSERV
];
580 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
581 Name
,sizeof(Name
),Service
,sizeof(Service
),
582 NI_NUMERICHOST
|NI_NUMERICSERV
);
584 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
590 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
591 if (AFMap
[J
].IETFFamily
== Proto
)
592 Hints
.ai_family
= AFMap
[J
].Family
;
593 if (Hints
.ai_family
== 0)
597 // Get a new passive address.
599 if ((Res
= getaddrinfo(IP
.c_str(),PStr
,&Hints
,&PasvAddr
)) != 0)
605 // FTPConn::Size - Return the size of a file /*{{{*/
606 // ---------------------------------------------------------------------
607 /* Grab the file size from the server, 0 means no size or empty file */
608 bool FTPConn::Size(const char *Path
,unsigned long &Size
)
614 if (WriteMsg(Tag
,Msg
,"SIZE %s",Path
) == false)
618 Size
= strtol(Msg
.c_str(),&End
,10);
619 if (Tag
>= 400 || End
== Msg
.c_str())
624 // FTPConn::ModTime - Return the modification time of the file /*{{{*/
625 // ---------------------------------------------------------------------
626 /* Like Size no error is returned if the command is not supported. If the
627 command fails then time is set to the current time of day to fool
629 bool FTPConn::ModTime(const char *Path
, time_t &Time
)
633 // Query the mod time
636 if (WriteMsg(Tag
,Msg
,"MDTM %s",Path
) == false)
638 if (Tag
>= 400 || Msg
.empty() == true || isdigit(Msg
[0]) == 0)
646 // FTPConn::CreateDataFd - Get a data connection /*{{{*/
647 // ---------------------------------------------------------------------
648 /* Create the data connection. Call FinalizeDataFd after this though.. */
649 bool FTPConn::CreateDataFd()
654 // Attempt to enter passive mode.
655 if (TryPassive
== true)
657 if (GoPasv() == false)
660 // Oops, didn't work out, don't bother trying again.
669 if ((DataFd
= socket(PasvAddr
->ai_family
,PasvAddr
->ai_socktype
,
670 PasvAddr
->ai_protocol
)) < 0)
671 return _error
->Errno("socket","Could not create a socket");
673 // Connect to the server
674 SetNonBlock(DataFd
,true);
675 if (connect(DataFd
,PasvAddr
->ai_addr
,PasvAddr
->ai_addrlen
) < 0 &&
676 errno
!= EINPROGRESS
)
677 return _error
->Errno("socket","Could not create a socket");
679 /* This implements a timeout for connect by opening the connection
681 if (WaitFd(DataFd
,true,TimeOut
) == false)
682 return _error
->Error("Could not connect data socket, connection timed out");
684 unsigned int Len
= sizeof(Err
);
685 if (getsockopt(DataFd
,SOL_SOCKET
,SO_ERROR
,&Err
,&Len
) != 0)
686 return _error
->Errno("getsockopt","Failed");
688 return _error
->Error("Could not connect passive socket.");
697 // Get the information for a listening socket.
698 struct addrinfo
*BindAddr
= 0;
699 struct addrinfo Hints
;
700 memset(&Hints
,0,sizeof(Hints
));
701 Hints
.ai_socktype
= SOCK_STREAM
;
702 Hints
.ai_flags
|= AI_PASSIVE
;
703 Hints
.ai_family
= ((struct sockaddr
*)&ServerAddr
)->sa_family
;
705 if ((Res
= getaddrinfo(0,"0",&Hints
,&BindAddr
)) != 0)
706 return _error
->Error("getaddrinfo was unable to get a listening socket");
708 // Construct the socket
709 if ((DataListenFd
= socket(BindAddr
->ai_family
,BindAddr
->ai_socktype
,
710 BindAddr
->ai_protocol
)) < 0)
712 freeaddrinfo(BindAddr
);
713 return _error
->Errno("socket","Could not create a socket");
717 if (bind(DataListenFd
,BindAddr
->ai_addr
,BindAddr
->ai_addrlen
) < 0)
719 freeaddrinfo(BindAddr
);
720 return _error
->Errno("bind","Could not bind a socket");
722 freeaddrinfo(BindAddr
);
723 if (listen(DataListenFd
,1) < 0)
724 return _error
->Errno("listen","Could not listen on the socket");
725 SetNonBlock(DataListenFd
,true);
727 // Determine the name to send to the remote
728 struct sockaddr_storage Addr
;
729 socklen_t AddrLen
= sizeof(Addr
);
730 if (getsockname(DataListenFd
,(sockaddr
*)&Addr
,&AddrLen
) < 0)
731 return _error
->Errno("getsockname","Could not determine the socket's name");
733 // Reverse the address. We need the server address and the data port.
734 char Name
[NI_MAXHOST
];
735 char Service
[NI_MAXSERV
];
736 char Service2
[NI_MAXSERV
];
737 getnameinfo((struct sockaddr
*)&Addr
,AddrLen
,
738 Name
,sizeof(Name
),Service
,sizeof(Service
),
739 NI_NUMERICHOST
|NI_NUMERICSERV
);
740 getnameinfo((struct sockaddr
*)&ServerAddr
,ServerAddrLen
,
741 Name
,sizeof(Name
),Service2
,sizeof(Service2
),
742 NI_NUMERICHOST
|NI_NUMERICSERV
);
744 // Send off an IPv4 address in the old port format
745 if (((struct sockaddr
*)&Addr
)->sa_family
== AF_INET
&&
746 ForceExtended
== false)
748 // Convert the dots in the quad into commas
749 for (char *I
= Name
; *I
!= 0; I
++)
752 unsigned long Port
= atoi(Service
);
754 // Send the port command
757 if (WriteMsg(Tag
,Msg
,"PORT %s,%d,%d",
759 (int)(Port
>> 8) & 0xff, (int)(Port
& 0xff)) == false)
762 return _error
->Error("Unable to send PORT command");
766 // Construct an EPRT command
768 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
769 if (AFMap
[J
].Family
== ((struct sockaddr
*)&Addr
)->sa_family
)
770 Proto
= AFMap
[J
].IETFFamily
;
772 return _error
->Error("Unkonwn address family %u (AF_*)",
773 ((struct sockaddr
*)&Addr
)->sa_family
);
775 // Send the EPRT command
778 if (WriteMsg(Tag
,Msg
,"EPRT |%u|%s|%s|",Proto
,Name
,Service
) == false)
781 return _error
->Error("EPRT failed, server said: %s",Msg
.c_str());
785 // FTPConn::Finalize - Complete the Data connection /*{{{*/
786 // ---------------------------------------------------------------------
787 /* If the connection is in port mode this waits for the other end to hook
789 bool FTPConn::Finalize()
791 // Passive mode? Do nothing
795 // Close any old socket..
799 // Wait for someone to connect..
800 if (WaitFd(DataListenFd
,false,TimeOut
) == false)
801 return _error
->Error("Data socket connect timed out");
803 // Accept the connection
804 struct sockaddr_in Addr
;
805 socklen_t Len
= sizeof(Addr
);
806 DataFd
= accept(DataListenFd
,(struct sockaddr
*)&Addr
,&Len
);
808 return _error
->Errno("accept","Unable to accept connection");
816 // FTPConn::Get - Get a file /*{{{*/
817 // ---------------------------------------------------------------------
818 /* This opens a data connection, sends REST and RETR and then
819 transfers the file over. */
820 bool FTPConn::Get(const char *Path
,FileFd
&To
,unsigned long Resume
,
821 MD5Summation
&MD5
,bool &Missing
)
824 if (CreateDataFd() == false)
831 if (WriteMsg(Tag
,Msg
,"REST %u",Resume
) == false)
837 if (To
.Truncate(Resume
) == false)
840 if (To
.Seek(0) == false)
845 if (MD5
.AddFD(To
.Fd(),Resume
) == false)
847 _error
->Errno("read","Problem hashing file");
852 // Send the get command
853 if (WriteMsg(Tag
,Msg
,"RETR %s",Path
) == false)
860 return _error
->Error("Unable to fetch file, server said '%s'",Msg
.c_str());
863 // Finish off the data connection
864 if (Finalize() == false)
868 unsigned char Buffer
[4096];
871 // Wait for some data..
872 if (WaitFd(DataFd
,false,TimeOut
) == false)
875 return _error
->Error("Data socket timed out");
879 int Res
= read(DataFd
,Buffer
,sizeof(Buffer
));
890 if (To
.Write(Buffer
,Res
) == false)
901 // Read the closing message from the server
902 if (ReadResp(Tag
,Msg
) == false)
905 return _error
->Error("Data transfer failed, server said '%s'",Msg
.c_str());
910 // FtpMethod::FtpMethod - Constructor /*{{{*/
911 // ---------------------------------------------------------------------
913 FtpMethod::FtpMethod() : pkgAcqMethod("1.0",SendConfig
)
915 signal(SIGTERM
,SigTerm
);
916 signal(SIGINT
,SigTerm
);
922 // FtpMethod::SigTerm - Handle a fatal signal /*{{{*/
923 // ---------------------------------------------------------------------
924 /* This closes and timestamps the open file. This is neccessary to get
925 resume behavoir on user abort */
926 void FtpMethod::SigTerm(int)
934 UBuf
.actime
= FailTime
;
935 UBuf
.modtime
= FailTime
;
936 utime(FailFile
.c_str(),&UBuf
);
941 // FtpMethod::Configuration - Handle a configuration message /*{{{*/
942 // ---------------------------------------------------------------------
943 /* We stash the desired pipeline depth */
944 bool FtpMethod::Configuration(string Message
)
946 if (pkgAcqMethod::Configuration(Message
) == false)
949 TimeOut
= _config
->FindI("Acquire::Ftp::Timeout",TimeOut
);
953 // FtpMethod::Fetch - Fetch a file /*{{{*/
954 // ---------------------------------------------------------------------
955 /* Fetch a single file, called by the base class.. */
956 bool FtpMethod::Fetch(FetchItem
*Itm
)
959 const char *File
= Get
.Path
.c_str();
961 Res
.Filename
= Itm
->DestFile
;
964 // Connect to the server
965 if (Server
== 0 || Server
->Comp(Get
) == false)
968 Server
= new FTPConn(Get
);
971 // Could not connect is a transient error..
972 if (Server
->Open(this) == false)
979 // Get the files information
982 if (Server
->Size(File
,Size
) == false ||
983 Server
->ModTime(File
,FailTime
) == false)
990 // See if it is an IMS hit
991 if (Itm
->LastModified
== FailTime
)
999 // See if the file exists
1001 if (stat(Itm
->DestFile
.c_str(),&Buf
) == 0)
1003 if (Size
== (unsigned)Buf
.st_size
&& FailTime
== Buf
.st_mtime
)
1005 Res
.Size
= Buf
.st_size
;
1006 Res
.LastModified
= Buf
.st_mtime
;
1007 Res
.ResumePoint
= Buf
.st_size
;
1013 if (FailTime
== Buf
.st_mtime
&& Size
> (unsigned)Buf
.st_size
)
1014 Res
.ResumePoint
= Buf
.st_size
;
1020 FileFd
Fd(Itm
->DestFile
,FileFd::WriteAny
);
1021 if (_error
->PendingError() == true)
1026 FailFile
= Itm
->DestFile
;
1027 FailFile
.c_str(); // Make sure we dont do a malloc in the signal handler
1031 if (Server
->Get(File
,Fd
,Res
.ResumePoint
,MD5
,Missing
) == false)
1036 struct utimbuf UBuf
;
1037 UBuf
.actime
= FailTime
;
1038 UBuf
.modtime
= FailTime
;
1039 utime(FailFile
.c_str(),&UBuf
);
1041 // If the file is missing we hard fail otherwise transient fail
1042 if (Missing
== true)
1048 Res
.Size
= Fd
.Size();
1051 Res
.LastModified
= FailTime
;
1052 Res
.MD5Sum
= MD5
.Result();
1055 struct utimbuf UBuf
;
1056 UBuf
.actime
= FailTime
;
1057 UBuf
.modtime
= FailTime
;
1058 utime(Queue
->DestFile
.c_str(),&UBuf
);
1067 int main(int argc
,const char *argv
[])
1069 /* See if we should be come the http client - we do this for http
1071 if (getenv("ftp_proxy") != 0)
1073 URI Proxy
= string(getenv("ftp_proxy"));
1074 if (Proxy
.Access
== "http")
1076 // Copy over the environment setting
1078 snprintf(S
,sizeof(S
),"http_proxy=%s",getenv("ftp_proxy"));
1081 // Run the http method
1082 string Path
= flNotFile(argv
[0]) + "/http";
1083 execl(Path
.c_str(),Path
.c_str(),0);
1084 cerr
<< "Unable to invoke " << Path
<< endl
;