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