1 /*
2  * lftp - file transfer program
3  *
4  * Copyright (c) 1996-2012 by Alexander V. Lukyanov (lav@yars.free.net)
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <config.h>
21 
22 #include "FileCopyFtp.h"
23 #include "log.h"
24 
25 #define super FileCopy
26 
27 #if !USE_SSL
28 #define protect false
29 #define passive_ssl_connect true
30 #endif
31 
32 #define ftp_src get->GetSession().Cast<Ftp>()
33 #define ftp_dst put->GetSession().Cast<Ftp>()
34 
Close()35 void FileCopyFtp::Close()
36 {
37    ftp_src->Close();
38    ftp_dst->Close();
39 }
40 
Do()41 int FileCopyFtp::Do()
42 {
43    int src_res,dst_res;
44    int m=super::Do();
45 
46    if(disable_fxp || Error() || !put || !get)
47       return m;
48 
49    if(state==PUT_WAIT && put->GetSeekPos()!=FILE_END)
50    {
51       if(get->GetSize()>=0 && put->GetSeekPos()>=get->GetSize())
52 	 return m;
53       if(ftp_dst->IsClosed())
54       {
55 	 put->OpenSession();
56 	 ftp_dst->SetCopyMode(Ftp::COPY_DEST,!passive_source,protect,
57 	       passive_source^passive_ssl_connect,dst_retries,dst_try_time);
58 	 m=MOVED;
59       }
60    }
61 
62    if(state!=DO_COPY || put->GetSeekPos()==FILE_END || get->Eof())
63       return m;
64 
65    // FileCopy can suspend peers.
66    get->Resume();
67    put->Resume();
68 
69    if(ftp_src->IsClosed())
70    {
71       get->OpenSession();
72       ftp_src->SetCopyMode(Ftp::COPY_SOURCE,passive_source,protect,
73 	    !(passive_source^passive_ssl_connect),src_retries,src_try_time);
74       m=MOVED;
75    }
76    if(ftp_dst->IsClosed())
77    {
78       put->OpenSession();
79       ftp_dst->SetCopyMode(Ftp::COPY_DEST,!passive_source,protect,
80 	    passive_source^passive_ssl_connect,dst_retries,dst_try_time);
81       m=MOVED;
82    }
83 
84    // check for errors
85    if(ftp_src->CopyFailed() || ftp_dst->CopyFailed())
86    {
87       if(ftp_src->RestartFailed() || ftp_dst->RestartFailed())
88       {
89 	 get->CannotSeek(put->GetSize());
90 	 put->CannotSeek(put->GetSize());
91       }
92       else if(passive_source==orig_passive_source)
93       {
94 	 passive_source=!passive_source;
95 	 Log::global->Write(0,_("**** FXP: trying to reverse ftp:fxp-passive-source\n"));
96       }
97 #if USE_SSL
98       else if(passive_ssl_connect==orig_passive_ssl_connect)
99       {
100 	 passive_ssl_connect=!passive_ssl_connect;
101 	 passive_source=orig_passive_source;
102 	 Log::global->Write(0,_("**** FXP: trying to reverse ftp:fxp-passive-sscn\n"));
103       }
104       else if(protect
105       && !ResMgr::QueryBool("ftp:ssl-force",ftp_src->GetHostName())
106       && !ResMgr::QueryBool("ftp:ssl-force",ftp_dst->GetHostName()))
107       {
108 	 passive_source=orig_passive_source;
109 	 protect=false;
110 	 Log::global->Write(0,_("**** FXP: trying to reverse ftp:ssl-protect-fxp\n"));
111       }
112 #endif // USE_SSL
113       else
114       {
115 	 // both ways failed. Fall back to normal copying.
116 	 Log::global->Write(0,_("**** FXP: giving up, reverting to plain copy\n"));
117 	 Close();
118 	 disable_fxp=true;
119 	 get->SetFXP(false);
120 	 put->SetFXP(false);
121 
122 	 if(ResMgr::QueryBool("ftp:fxp-force",ftp_src->GetHostName())
123 	 || ResMgr::QueryBool("ftp:fxp-force",ftp_dst->GetHostName()))
124 	 {
125 	    SetError(_("ftp:fxp-force is set but FXP is not available"));
126 	    return MOVED;
127 	 }
128 
129 	 off_t pos=put->GetRealPos();
130 	 if(!get->CanSeek(pos) || !put->CanSeek(pos))
131 	    pos=0;
132 	 get->Seek(pos);
133 	 put->Seek(pos);
134 	 RateReset();
135 	 return MOVED;
136       }
137       RateReset();
138 
139       src_retries=ftp_src->GetRetries();
140       dst_retries=ftp_dst->GetRetries();
141       src_try_time=ftp_src->GetTryTime();
142       dst_try_time=ftp_dst->GetTryTime();
143       Close();
144       if(put->CanSeek())
145       {
146 	 put->Seek(FILE_END);
147 	 get->Suspend();
148 	 put->Resume();
149 	 state=PUT_WAIT;
150       }
151       else
152 	 put->Seek(0);
153       return MOVED;
154    }
155 
156    src_res=ftp_src->Done();
157    dst_res=ftp_dst->Done();
158    if(src_res==FA::OK && dst_res==FA::OK)
159    {
160       Close();
161       const long long size=GetSize();
162       if(size>=0)
163       {
164 	 get->SetPos(size);
165 	 put->SetPos(size);
166       }
167       get->PutEOF();
168       return MOVED;
169    }
170    if(src_res!=FA::IN_PROGRESS && src_res!=FA::OK)
171    {
172       SetError(ftp_src->StrError(src_res));
173       return MOVED;
174    }
175    if(dst_res!=FA::IN_PROGRESS && dst_res!=FA::OK)
176    {
177       SetError(ftp_dst->StrError(dst_res));
178       return MOVED;
179    }
180 
181    // exchange copy address
182    if(ftp_dst->SetCopyAddress(ftp_src) || ftp_src->SetCopyAddress(ftp_dst))
183       m=MOVED;
184 
185    if(!ftp_dst->CopyStoreAllowed()
186    && ftp_src->CopyIsReadyForStore() && ftp_dst->CopyIsReadyForStore())
187    {
188       ftp_dst->CopyAllowStore();
189       m=MOVED;
190       RateReset();
191    }
192 
193    // check for timeout when one session is done, and the other is stuck
194    if(dst_res==FA::OK && src_res==FA::IN_PROGRESS)
195       ftp_src->CopyCheckTimeout(ftp_dst);
196    if(src_res==FA::OK && dst_res==FA::IN_PROGRESS)
197       ftp_dst->CopyCheckTimeout(ftp_src);
198 
199    off_t add=ftp_dst->GetPos()-put->GetRealPos();
200    if(add>0)
201    {
202       RateAdd(add);
203       bytes_count+=add;
204    }
205 
206    off_t pos=ftp_dst->GetPos();
207    get->SetPos(pos);
208    put->SetPos(pos);
209 
210    return m;
211 }
212 
Init()213 void FileCopyFtp::Init()
214 {
215    no_rest=false;
216    orig_passive_source=passive_source=false;
217    src_retries=dst_retries=0;
218    src_try_time=dst_try_time=0;
219    disable_fxp=false;
220 #if USE_SSL
221    protect=false;
222    orig_passive_ssl_connect=passive_ssl_connect=true;
223 #endif
224 }
225 
226 // s,d must be FileCopyPeerFA, s->GetSession(),d->GetSession() must be Ftp.
FileCopyFtp(FileCopyPeer * s,FileCopyPeer * d,bool c,bool rp)227 FileCopyFtp::FileCopyFtp(FileCopyPeer *s,FileCopyPeer *d,bool c,bool rp)
228    : super(s,d,c)
229 {
230    Init();
231    passive_source=rp;
232 
233    get->SetFXP(true);
234    put->SetFXP(true);
235 
236    if(ftp_src->IsPassive() && !ftp_dst->IsPassive())
237       passive_source=true;
238    else if(!ftp_src->IsPassive() && ftp_dst->IsPassive())
239       passive_source=false;
240    orig_passive_source=passive_source;
241 
242 #if USE_SSL
243    if(ResMgr::QueryBool("ftp:ssl-protect-fxp",ftp_src->GetHostName())
244    || ResMgr::QueryBool("ftp:ssl-protect-fxp",ftp_dst->GetHostName()))
245       protect=true;
246    passive_ssl_connect=ResMgr::QueryBool("ftp:fxp-passive-sscn",0);
247    orig_passive_ssl_connect=passive_ssl_connect;
248 #endif
249 }
250 
New(FileCopyPeer * s,FileCopyPeer * d,bool c)251 FileCopy *FileCopyFtp::New(FileCopyPeer *s,FileCopyPeer *d,bool c)
252 {
253    const FileAccessRef& s_s=s->GetSession();
254    const FileAccessRef& d_s=d->GetSession();
255    if(!s_s || !d_s)
256       return 0;
257    if((strcmp(s_s->GetProto(),"ftp") && strcmp(s_s->GetProto(),"ftps"))
258    || (strcmp(d_s->GetProto(),"ftp") && strcmp(d_s->GetProto(),"ftps")))
259       return 0;
260    if(!ResMgr::QueryBool("ftp:use-fxp",s_s->GetHostName())
261    || !ResMgr::QueryBool("ftp:use-fxp",d_s->GetHostName()))
262       return 0;
263    return new FileCopyFtp(s,d,c,ResMgr::QueryBool("ftp:fxp-passive-source",0));
264 }
265