]>
git.saurik.com Git - apt.git/blob - methods/ftp.cc
a3a7cd08a63a308af8197b33341911ec5aa66cf5
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: ftp.cc,v 1.25 2001/03/13 06:51:46 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 // Get the remote server's address
158 PeerAddrLen
= sizeof(PeerAddr
);
159 if (getpeername(ServerFd
,(sockaddr
*)&PeerAddr
,&PeerAddrLen
) != 0)
160 return _error
->Errno("getpeername","Unable to determine the peer name");
162 // Get the local machine's address
163 ServerAddrLen
= sizeof(ServerAddr
);
164 if (getsockname(ServerFd
,(sockaddr
*)&ServerAddr
,&ServerAddrLen
) != 0)
165 return _error
->Errno("getsockname","Unable to determine the local name");
167 Owner
->Status("Logging in");
171 // FTPConn::Login - Login to the remote server /*{{{*/
172 // ---------------------------------------------------------------------
173 /* This performs both normal login and proxy login using a simples script
174 stored in the config file. */
175 bool FTPConn::Login()
180 // Setup the variables needed for authentication
181 string User
= "anonymous";
182 string Pass
= "apt_get_ftp_2.1@debian.linux.user";
184 // Fill in the user/pass
185 if (ServerName
.User
.empty() == false)
186 User
= ServerName
.User
;
187 if (ServerName
.Password
.empty() == false)
188 Pass
= ServerName
.Password
;
190 // Perform simple login
191 if (Proxy
.empty() == true)
193 // Read the initial response
194 if (ReadResp(Tag
,Msg
) == false)
197 return _error
->Error("Server refused our connection and said: %s",Msg
.c_str());
200 if (WriteMsg(Tag
,Msg
,"USER %s",User
.c_str()) == false)
203 return _error
->Error("USER failed, server said: %s",Msg
.c_str());
206 if (WriteMsg(Tag
,Msg
,"PASS %s",Pass
.c_str()) == false)
209 return _error
->Error("PASS failed, server said: %s",Msg
.c_str());
211 // Enter passive mode
212 if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true)
213 TryPassive
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true);
215 TryPassive
= _config
->FindB("Acquire::FTP::Passive",true);
219 // Read the initial response
220 if (ReadResp(Tag
,Msg
) == false)
223 return _error
->Error("Server refused our connection and said: %s",Msg
.c_str());
225 // Perform proxy script execution
226 Configuration::Item
const *Opts
= _config
->Tree("Acquire::ftp::ProxyLogin");
227 if (Opts
== 0 || Opts
->Child
== 0)
228 return _error
->Error("A proxy server was specified but no login "
229 "script, Acquire::ftp::ProxyLogin is empty.");
232 // Iterate over the entire login script
233 for (; Opts
!= 0; Opts
= Opts
->Next
)
235 if (Opts
->Value
.empty() == true)
238 // Substitute the variables into the command
240 if (ServerName
.Port
!= 0)
241 sprintf(SitePort
,"%u",ServerName
.Port
);
243 strcpy(SitePort
,"21");
244 string Tmp
= Opts
->Value
;
245 Tmp
= SubstVar(Tmp
,"$(PROXY_USER)",Proxy
.User
);
246 Tmp
= SubstVar(Tmp
,"$(PROXY_PASS)",Proxy
.Password
);
247 Tmp
= SubstVar(Tmp
,"$(SITE_USER)",User
);
248 Tmp
= SubstVar(Tmp
,"$(SITE_PASS)",Pass
);
249 Tmp
= SubstVar(Tmp
,"$(SITE_PORT)",SitePort
);
250 Tmp
= SubstVar(Tmp
,"$(SITE)",ServerName
.Host
);
253 if (WriteMsg(Tag
,Msg
,"%s",Tmp
.c_str()) == false)
256 return _error
->Error("Login script command '%s' failed, server said: %s",Tmp
.c_str(),Msg
.c_str());
259 // Enter passive mode
261 if (_config
->Exists("Acquire::FTP::Passive::" + ServerName
.Host
) == true)
262 TryPassive
= _config
->FindB("Acquire::FTP::Passive::" + ServerName
.Host
,true);
265 if (_config
->Exists("Acquire::FTP::Proxy::Passive") == true)
266 TryPassive
= _config
->FindB("Acquire::FTP::Proxy::Passive",true);
268 TryPassive
= _config
->FindB("Acquire::FTP::Passive",true);
272 // Force the use of extended commands
273 if (_config
->Exists("Acquire::FTP::ForceExtended::" + ServerName
.Host
) == true)
274 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended::" + ServerName
.Host
,true);
276 ForceExtended
= _config
->FindB("Acquire::FTP::ForceExtended",false);
279 if (WriteMsg(Tag
,Msg
,"TYPE I") == false)
282 return _error
->Error("TYPE failed, server said: %s",Msg
.c_str());
287 // FTPConn::ReadLine - Read a line from the server /*{{{*/
288 // ---------------------------------------------------------------------
289 /* This performs a very simple buffered read. */
290 bool FTPConn::ReadLine(string
&Text
)
296 while (Len
< sizeof(Buffer
))
298 // Scan the buffer for a new line
299 for (unsigned int I
= 0; I
!= Len
; I
++)
301 // Escape some special chars
306 if (Buffer
[I
] != '\n')
310 Text
= string(Buffer
,I
);
311 memmove(Buffer
,Buffer
+I
,Len
- I
);
316 // Wait for some data..
317 if (WaitFd(ServerFd
,false,TimeOut
) == false)
320 return _error
->Error("Connection timeout");
324 int Res
= read(ServerFd
,Buffer
+ Len
,sizeof(Buffer
) - Len
);
326 _error
->Error("Server closed the connection");
329 _error
->Errno("read","Read error");
336 return _error
->Error("A response overflowed the buffer.");
339 // FTPConn::ReadResp - Read a full response from the server /*{{{*/
340 // ---------------------------------------------------------------------
341 /* This reads a reply code from the server, it handles both p */
342 bool FTPConn::ReadResp(unsigned int &Ret
,string
&Text
)
344 // Grab the first line of the response
346 if (ReadLine(Msg
) == false)
351 Ret
= strtol(Msg
.c_str(),&End
,10);
352 if (End
- Msg
.c_str() != 3)
353 return _error
->Error("Protocol corruption");
356 Text
= Msg
.c_str()+4;
360 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
365 return _error
->Error("Protocol corruption");
367 /* Okay, here we do the continued message trick. This is foolish, but
368 proftpd follows the protocol as specified and wu-ftpd doesn't, so
369 we filter. I wonder how many clients break if you use proftpd and
370 put a '- in the 3rd spot in the message? */
372 strncpy(Leader
,Msg
.c_str(),3);
374 while (ReadLine(Msg
) == true)
376 // Short, it must be using RFC continuation..
377 if (Msg
.length() < 4)
384 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == ' ')
386 Text
+= Msg
.c_str()+4;
390 // This message has the wu-ftpd style reply code prefixed
391 if (strncmp(Msg
.c_str(),Leader
,3) == 0 && Msg
[3] == '-')
393 Text
+= Msg
.c_str()+4;
397 // Must be RFC style prefixing
401 if (Debug
== true && _error
->PendingError() == false)
402 cerr
<< "<- '" << QuoteString(Text
,"") << "'" << endl
;
404 return !_error
->PendingError();
407 // FTPConn::WriteMsg - Send a message to the server /*{{{*/
408 // ---------------------------------------------------------------------
409 /* Simple printf like function.. */
410 bool FTPConn::WriteMsg(unsigned int &Ret
,string
&Text
,const char *Fmt
,...)
415 // sprintf the description
417 vsnprintf(S
,sizeof(S
) - 4,Fmt
,args
);
421 cerr
<< "-> '" << QuoteString(S
,"") << "'" << endl
;
424 unsigned long Len
= strlen(S
);
425 unsigned long Start
= 0;
428 if (WaitFd(ServerFd
,true,TimeOut
) == false)
431 return _error
->Error("Connection timeout");
434 int Res
= write(ServerFd
,S
+ Start
,Len
);
437 _error
->Errno("write","Write Error");
446 return ReadResp(Ret
,Text
);
449 // FTPConn::GoPasv - Enter Passive mode /*{{{*/
450 // ---------------------------------------------------------------------
451 /* Try to enter passive mode, the return code does not indicate if passive
452 mode could or could not be established, only if there was a fatal error.
453 We have to enter passive mode every time we make a data connection :| */
454 bool FTPConn::GoPasv()
456 /* The PASV command only works on IPv4 sockets, even though it could
457 in theory suppory IPv6 via an all zeros reply */
458 if (((struct sockaddr
*)&PeerAddr
)->sa_family
!= AF_INET
||
459 ForceExtended
== true)
463 freeaddrinfo(PasvAddr
);
466 // Try to enable pasv mode
469 if (WriteMsg(Tag
,Msg
,"PASV") == false)
472 // Unsupported function
473 string::size_type Pos
= Msg
.find('(');
474 if (Tag
>= 400 || Pos
== string::npos
)
478 unsigned a0
,a1
,a2
,a3
,p0
,p1
;
479 if (sscanf(Msg
.c_str() + Pos
,"(%u,%u,%u,%u,%u,%u)",&a0
,&a1
,&a2
,&a3
,&p0
,&p1
) != 6)
482 /* Some evil servers return 0 to mean their addr. We can actually speak
483 to these servers natively using IPv6 */
484 if (a0
== 0 && a1
== 0 && a2
== 0 && a3
== 0)
486 // Get the IP in text form
487 char Name
[NI_MAXHOST
];
488 char Service
[NI_MAXSERV
];
489 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
490 Name
,sizeof(Name
),Service
,sizeof(Service
),
491 NI_NUMERICHOST
|NI_NUMERICSERV
);
493 struct addrinfo Hints
;
494 memset(&Hints
,0,sizeof(Hints
));
495 Hints
.ai_socktype
= SOCK_STREAM
;
496 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
497 Hints
.ai_flags
|= AI_NUMERICHOST
;
499 // Get a new passive address.
501 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
502 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
507 struct addrinfo Hints
;
508 memset(&Hints
,0,sizeof(Hints
));
509 Hints
.ai_socktype
= SOCK_STREAM
;
510 Hints
.ai_family
= AF_INET
;
511 Hints
.ai_flags
|= AI_NUMERICHOST
;
513 // Get a new passive address.
515 snprintf(Port
,sizeof(Port
),"%u",(p0
<< 8) + p1
);
517 snprintf(Name
,sizeof(Name
),"%u.%u.%u.%u",a0
,a1
,a2
,a3
);
518 if (getaddrinfo(Name
,Port
,&Hints
,&PasvAddr
) != 0)
523 // FTPConn::ExtGoPasv - Enter Extended Passive mode /*{{{*/
524 // ---------------------------------------------------------------------
525 /* Try to enter extended passive mode. See GoPasv above and RFC 2428 */
526 bool FTPConn::ExtGoPasv()
529 freeaddrinfo(PasvAddr
);
532 // Try to enable pasv mode
535 if (WriteMsg(Tag
,Msg
,"EPSV") == false)
538 // Unsupported function
539 string::size_type Pos
= Msg
.find('(');
540 if (Tag
>= 400 || Pos
== string::npos
)
544 string::const_iterator List
[4];
547 for (string::const_iterator I
= Msg
.begin() + Pos
; I
< Msg
.end(); I
++)
559 unsigned long Proto
= 0;
560 unsigned long Port
= 0;
562 IP
= string(List
[1]+1,List
[2]);
563 Port
= atoi(string(List
[2]+1,List
[3]).c_str());
564 if (IP
.empty() == false)
565 Proto
= atoi(string(List
[0]+1,List
[1]).c_str());
570 // String version of the port
572 snprintf(PStr
,sizeof(PStr
),"%lu",Port
);
574 // Get the IP in text form
575 struct addrinfo Hints
;
576 memset(&Hints
,0,sizeof(Hints
));
577 Hints
.ai_socktype
= SOCK_STREAM
;
578 Hints
.ai_flags
|= AI_NUMERICHOST
;
580 /* The RFC defined case, connect to the old IP/protocol using the
582 if (IP
.empty() == true)
584 // Get the IP in text form
585 char Name
[NI_MAXHOST
];
586 char Service
[NI_MAXSERV
];
587 getnameinfo((struct sockaddr
*)&PeerAddr
,PeerAddrLen
,
588 Name
,sizeof(Name
),Service
,sizeof(Service
),
589 NI_NUMERICHOST
|NI_NUMERICSERV
);
591 Hints
.ai_family
= ((struct sockaddr
*)&PeerAddr
)->sa_family
;
597 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
598 if (AFMap
[J
].IETFFamily
== Proto
)
599 Hints
.ai_family
= AFMap
[J
].Family
;
600 if (Hints
.ai_family
== 0)
604 // Get a new passive address.
606 if ((Res
= getaddrinfo(IP
.c_str(),PStr
,&Hints
,&PasvAddr
)) != 0)
612 // FTPConn::Size - Return the size of a file /*{{{*/
613 // ---------------------------------------------------------------------
614 /* Grab the file size from the server, 0 means no size or empty file */
615 bool FTPConn::Size(const char *Path
,unsigned long &Size
)
621 if (WriteMsg(Tag
,Msg
,"SIZE %s",Path
) == false)
625 Size
= strtol(Msg
.c_str(),&End
,10);
626 if (Tag
>= 400 || End
== Msg
.c_str())
631 // FTPConn::ModTime - Return the modification time of the file /*{{{*/
632 // ---------------------------------------------------------------------
633 /* Like Size no error is returned if the command is not supported. If the
634 command fails then time is set to the current time of day to fool
636 bool FTPConn::ModTime(const char *Path
, time_t &Time
)
640 // Query the mod time
643 if (WriteMsg(Tag
,Msg
,"MDTM %s",Path
) == false)
645 if (Tag
>= 400 || Msg
.empty() == true || isdigit(Msg
[0]) == 0)
653 // FTPConn::CreateDataFd - Get a data connection /*{{{*/
654 // ---------------------------------------------------------------------
655 /* Create the data connection. Call FinalizeDataFd after this though.. */
656 bool FTPConn::CreateDataFd()
661 // Attempt to enter passive mode.
662 if (TryPassive
== true)
664 if (GoPasv() == false)
667 // Oops, didn't work out, don't bother trying again.
676 if ((DataFd
= socket(PasvAddr
->ai_family
,PasvAddr
->ai_socktype
,
677 PasvAddr
->ai_protocol
)) < 0)
678 return _error
->Errno("socket","Could not create a socket");
680 // Connect to the server
681 SetNonBlock(DataFd
,true);
682 if (connect(DataFd
,PasvAddr
->ai_addr
,PasvAddr
->ai_addrlen
) < 0 &&
683 errno
!= EINPROGRESS
)
684 return _error
->Errno("socket","Could not create a socket");
686 /* This implements a timeout for connect by opening the connection
688 if (WaitFd(DataFd
,true,TimeOut
) == false)
689 return _error
->Error("Could not connect data socket, connection timed out");
691 unsigned int Len
= sizeof(Err
);
692 if (getsockopt(DataFd
,SOL_SOCKET
,SO_ERROR
,&Err
,&Len
) != 0)
693 return _error
->Errno("getsockopt","Failed");
695 return _error
->Error("Could not connect passive socket.");
704 // Get the information for a listening socket.
705 struct addrinfo
*BindAddr
= 0;
706 struct addrinfo Hints
;
707 memset(&Hints
,0,sizeof(Hints
));
708 Hints
.ai_socktype
= SOCK_STREAM
;
709 Hints
.ai_flags
|= AI_PASSIVE
;
710 Hints
.ai_family
= ((struct sockaddr
*)&ServerAddr
)->sa_family
;
712 if ((Res
= getaddrinfo(0,"0",&Hints
,&BindAddr
)) != 0)
713 return _error
->Error("getaddrinfo was unable to get a listening socket");
715 // Construct the socket
716 if ((DataListenFd
= socket(BindAddr
->ai_family
,BindAddr
->ai_socktype
,
717 BindAddr
->ai_protocol
)) < 0)
719 freeaddrinfo(BindAddr
);
720 return _error
->Errno("socket","Could not create a socket");
724 if (bind(DataListenFd
,BindAddr
->ai_addr
,BindAddr
->ai_addrlen
) < 0)
726 freeaddrinfo(BindAddr
);
727 return _error
->Errno("bind","Could not bind a socket");
729 freeaddrinfo(BindAddr
);
730 if (listen(DataListenFd
,1) < 0)
731 return _error
->Errno("listen","Could not listen on the socket");
732 SetNonBlock(DataListenFd
,true);
734 // Determine the name to send to the remote
735 struct sockaddr_storage Addr
;
736 socklen_t AddrLen
= sizeof(Addr
);
737 if (getsockname(DataListenFd
,(sockaddr
*)&Addr
,&AddrLen
) < 0)
738 return _error
->Errno("getsockname","Could not determine the socket's name");
740 // Reverse the address. We need the server address and the data port.
741 char Name
[NI_MAXHOST
];
742 char Service
[NI_MAXSERV
];
743 char Service2
[NI_MAXSERV
];
744 getnameinfo((struct sockaddr
*)&Addr
,AddrLen
,
745 Name
,sizeof(Name
),Service
,sizeof(Service
),
746 NI_NUMERICHOST
|NI_NUMERICSERV
);
747 getnameinfo((struct sockaddr
*)&ServerAddr
,ServerAddrLen
,
748 Name
,sizeof(Name
),Service2
,sizeof(Service2
),
749 NI_NUMERICHOST
|NI_NUMERICSERV
);
751 // Send off an IPv4 address in the old port format
752 if (((struct sockaddr
*)&Addr
)->sa_family
== AF_INET
&&
753 ForceExtended
== false)
755 // Convert the dots in the quad into commas
756 for (char *I
= Name
; *I
!= 0; I
++)
759 unsigned long Port
= atoi(Service
);
761 // Send the port command
764 if (WriteMsg(Tag
,Msg
,"PORT %s,%d,%d",
766 (int)(Port
>> 8) & 0xff, (int)(Port
& 0xff)) == false)
769 return _error
->Error("Unable to send PORT command");
773 // Construct an EPRT command
775 for (unsigned J
= 0; AFMap
[J
].Family
!= 0; J
++)
776 if (AFMap
[J
].Family
== ((struct sockaddr
*)&Addr
)->sa_family
)
777 Proto
= AFMap
[J
].IETFFamily
;
779 return _error
->Error("Unkonwn address family %u (AF_*)",
780 ((struct sockaddr
*)&Addr
)->sa_family
);
782 // Send the EPRT command
785 if (WriteMsg(Tag
,Msg
,"EPRT |%u|%s|%s|",Proto
,Name
,Service
) == false)
788 return _error
->Error("EPRT failed, server said: %s",Msg
.c_str());
792 // FTPConn::Finalize - Complete the Data connection /*{{{*/
793 // ---------------------------------------------------------------------
794 /* If the connection is in port mode this waits for the other end to hook
796 bool FTPConn::Finalize()
798 // Passive mode? Do nothing
802 // Close any old socket..
806 // Wait for someone to connect..
807 if (WaitFd(DataListenFd
,false,TimeOut
) == false)
808 return _error
->Error("Data socket connect timed out");
810 // Accept the connection
811 struct sockaddr_in Addr
;
812 socklen_t Len
= sizeof(Addr
);
813 DataFd
= accept(DataListenFd
,(struct sockaddr
*)&Addr
,&Len
);
815 return _error
->Errno("accept","Unable to accept connection");
823 // FTPConn::Get - Get a file /*{{{*/
824 // ---------------------------------------------------------------------
825 /* This opens a data connection, sends REST and RETR and then
826 transfers the file over. */
827 bool FTPConn::Get(const char *Path
,FileFd
&To
,unsigned long Resume
,
828 Hashes
&Hash
,bool &Missing
)
831 if (CreateDataFd() == false)
838 if (WriteMsg(Tag
,Msg
,"REST %u",Resume
) == false)
844 if (To
.Truncate(Resume
) == false)
847 if (To
.Seek(0) == false)
852 if (Hash
.AddFD(To
.Fd(),Resume
) == false)
854 _error
->Errno("read","Problem hashing file");
859 // Send the get command
860 if (WriteMsg(Tag
,Msg
,"RETR %s",Path
) == false)
867 return _error
->Error("Unable to fetch file, server said '%s'",Msg
.c_str());
870 // Finish off the data connection
871 if (Finalize() == false)
875 unsigned char Buffer
[4096];
878 // Wait for some data..
879 if (WaitFd(DataFd
,false,TimeOut
) == false)
882 return _error
->Error("Data socket timed out");
886 int Res
= read(DataFd
,Buffer
,sizeof(Buffer
));
896 Hash
.Add(Buffer
,Res
);
897 if (To
.Write(Buffer
,Res
) == false)
908 // Read the closing message from the server
909 if (ReadResp(Tag
,Msg
) == false)
912 return _error
->Error("Data transfer failed, server said '%s'",Msg
.c_str());
917 // FtpMethod::FtpMethod - Constructor /*{{{*/
918 // ---------------------------------------------------------------------
920 FtpMethod::FtpMethod() : pkgAcqMethod("1.0",SendConfig
)
922 signal(SIGTERM
,SigTerm
);
923 signal(SIGINT
,SigTerm
);
929 // FtpMethod::SigTerm - Handle a fatal signal /*{{{*/
930 // ---------------------------------------------------------------------
931 /* This closes and timestamps the open file. This is neccessary to get
932 resume behavoir on user abort */
933 void FtpMethod::SigTerm(int)
941 UBuf
.actime
= FailTime
;
942 UBuf
.modtime
= FailTime
;
943 utime(FailFile
.c_str(),&UBuf
);
948 // FtpMethod::Configuration - Handle a configuration message /*{{{*/
949 // ---------------------------------------------------------------------
950 /* We stash the desired pipeline depth */
951 bool FtpMethod::Configuration(string Message
)
953 if (pkgAcqMethod::Configuration(Message
) == false)
956 TimeOut
= _config
->FindI("Acquire::Ftp::Timeout",TimeOut
);
960 // FtpMethod::Fetch - Fetch a file /*{{{*/
961 // ---------------------------------------------------------------------
962 /* Fetch a single file, called by the base class.. */
963 bool FtpMethod::Fetch(FetchItem
*Itm
)
966 const char *File
= Get
.Path
.c_str();
968 Res
.Filename
= Itm
->DestFile
;
971 // Connect to the server
972 if (Server
== 0 || Server
->Comp(Get
) == false)
975 Server
= new FTPConn(Get
);
978 // Could not connect is a transient error..
979 if (Server
->Open(this) == false)
986 // Get the files information
989 if (Server
->Size(File
,Size
) == false ||
990 Server
->ModTime(File
,FailTime
) == false)
997 // See if it is an IMS hit
998 if (Itm
->LastModified
== FailTime
)
1006 // See if the file exists
1008 if (stat(Itm
->DestFile
.c_str(),&Buf
) == 0)
1010 if (Size
== (unsigned)Buf
.st_size
&& FailTime
== Buf
.st_mtime
)
1012 Res
.Size
= Buf
.st_size
;
1013 Res
.LastModified
= Buf
.st_mtime
;
1014 Res
.ResumePoint
= Buf
.st_size
;
1020 if (FailTime
== Buf
.st_mtime
&& Size
> (unsigned)Buf
.st_size
)
1021 Res
.ResumePoint
= Buf
.st_size
;
1027 FileFd
Fd(Itm
->DestFile
,FileFd::WriteAny
);
1028 if (_error
->PendingError() == true)
1033 FailFile
= Itm
->DestFile
;
1034 FailFile
.c_str(); // Make sure we dont do a malloc in the signal handler
1038 if (Server
->Get(File
,Fd
,Res
.ResumePoint
,Hash
,Missing
) == false)
1043 struct utimbuf UBuf
;
1044 UBuf
.actime
= FailTime
;
1045 UBuf
.modtime
= FailTime
;
1046 utime(FailFile
.c_str(),&UBuf
);
1048 // If the file is missing we hard fail otherwise transient fail
1049 if (Missing
== true)
1055 Res
.Size
= Fd
.Size();
1058 Res
.LastModified
= FailTime
;
1059 Res
.TakeHashes(Hash
);
1062 struct utimbuf UBuf
;
1063 UBuf
.actime
= FailTime
;
1064 UBuf
.modtime
= FailTime
;
1065 utime(Queue
->DestFile
.c_str(),&UBuf
);
1074 int main(int argc
,const char *argv
[])
1076 /* See if we should be come the http client - we do this for http
1078 if (getenv("ftp_proxy") != 0)
1080 URI Proxy
= string(getenv("ftp_proxy"));
1082 // Parse no_proxy, a , separated list of domains
1083 if (getenv("no_proxy") != 0)
1085 if (CheckDomainList(Proxy
.Host
,getenv("no_proxy")) == true)
1089 // Run the HTTP method
1090 if (Proxy
.Access
== "http")
1092 // Copy over the environment setting
1094 snprintf(S
,sizeof(S
),"http_proxy=%s",getenv("ftp_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
;