1 #include "ftp.h"
2 #include "lib/ftplib.h"
3 
4 namespace Upp {
5 
6 #define LLOGBLOCK(x)  // LOGBLOCK(x)
7 #define LLOG(x)       // DLOG(x)
8 
9 static bool ftpinit = false;
10 
FtpClient()11 FtpClient::FtpClient()
12 {
13 	ftpconn = NULL;
14 	if(!ftpinit) {
15 		INTERLOCKED {
16 			if(!ftpinit) {
17 				FtpInit();
18 				ftpinit = true;
19 			}
20 		}
21 	}
22 }
23 
~FtpClient()24 FtpClient::~FtpClient()
25 {
26 	Close();
27 }
28 
IsOpen() const29 bool FtpClient::IsOpen() const
30 {
31 	return ftpconn;
32 }
33 
Connect(const char * host,const char * user,const char * password,bool pasv,int idletimeout_secs)34 bool FtpClient::Connect(const char *host, const char *user, const char *password, bool pasv, int idletimeout_secs)
35 {
36 	LOGBLOCK("FtpClient::Connect");
37 	Close();
38 	char perror[512];
39 	LLOG("FtpConnect(" << host << ")");
40 	if(WhenProgress(0,0)) {
41 		error = "connect aborted";
42 		return false;
43 	}
44 	if(!FtpConnect(host, &ftpconn, perror, &FtpClient::Callback, this, 200, idletimeout_secs)) {
45 		error = perror;
46 		return false;
47 	}
48 	LLOG("FtpLogin(" << user << ", " << password << ")");
49 	if(!FtpLogin(user, password, ftpconn)) {
50 		error = FtpError(ftpconn);
51 		Close();
52 		return false;
53 	}
54 	LLOG("FtpOptions(pasv = " << pasv << ")");
55 	if(!FtpOptions(FTPLIB_CONNMODE, pasv ? FTPLIB_PASSIVE : FTPLIB_PORT, ftpconn)) {
56 		error = FtpError(ftpconn);
57 		Close();
58 		return false;
59 	}
60 	return true;
61 }
62 
Callback(netbuf * nControl,int xfered,void * arg)63 int FtpClient::Callback(netbuf *nControl, int xfered, void *arg)
64 {
65 	FtpClient *ftp = (FtpClient *)arg;
66 	return !ftp->WhenProgress(xfered, ftp->save_total);
67 }
68 
Close()69 void FtpClient::Close()
70 {
71 	if(ftpconn) {
72 		FtpQuit(ftpconn);
73 		ftpconn = NULL;
74 	}
75 }
76 
CheckOpen()77 bool FtpClient::CheckOpen()
78 {
79 	if(!IsOpen()) {
80 		error = t_("not connected");
81 		return false;
82 	}
83 	return true;
84 }
85 
Load(const char * path)86 String FtpClient::Load(const char *path)
87 {
88 	LLOGBLOCK("FtpClient::Load");
89 	if(!CheckOpen())
90 		return String::GetVoid();
91 	netbuf *ftpdata;
92 	LLOG("FtpAccess(" << path << ")");
93 	if(WhenProgress(0,0)) {
94 		error = t_("aborted");
95 		return String::GetVoid();
96 	}
97 	if(!FtpAccess(path, FTPLIB_FILE_READ, FTPLIB_IMAGE, ftpconn, &ftpdata)) {
98 		error = FtpError(ftpconn);
99 		return String::GetVoid();
100 	}
101 	load_data = Null;
102 	while(!WhenProgress(load_data.GetLength(), 0)) {
103 		byte buffer[1024];
104 		int ndata = FtpRead(buffer, sizeof(buffer), ftpdata);
105 		LLOG("FtpRead -> " << ndata);
106 		if(ndata < 0) {
107 			error = FtpError(ftpdata);
108 			FtpClose(ftpdata);
109 			return String::GetVoid();
110 		}
111 		if(ndata == 0) {
112 			load_data.Shrink();
113 			error = FtpError(ftpdata);
114 			break;
115 		}
116 		load_data.Cat(buffer, ndata);
117 #ifdef SLOWTRANSFER
118 		int end = GetTickCount() + SLOWTRANSFER;
119 		for(int d; (d = end - GetTickCount()) > 0; Sleep(d))
120 			;
121 #endif
122 	}
123 	FtpClose(ftpdata);
124 	return load_data;
125 }
126 
Save(const char * path,String data)127 bool FtpClient::Save(const char *path, String data)
128 {
129 	return SaveCount(path, data) == data.GetLength();
130 }
131 
SaveCount(const char * path,String data)132 int FtpClient::SaveCount(const char *path, String data)
133 {
134 	LLOGBLOCK("FtpClient::Save");
135 	netbuf *ftpdata;
136 	save_pos = 0;
137 	save_total = data.GetLength();
138 	if(!CheckOpen())
139 		return 0;
140 	LLOG("FtpAccess(" << path << ")");
141 	if(WhenProgress(0,data.GetLength()))
142 		return 0;
143 	if(!FtpAccess(path, FTPLIB_FILE_WRITE, FTPLIB_IMAGE, ftpconn, &ftpdata)) {
144 		error = FtpError(ftpconn);
145 		return 0;
146 	}
147 	while(save_pos < data.GetLength()) {
148 		if(WhenProgress(save_pos, data.GetLength())) {
149 			error = NFormat(t_("write aborted after %d bytes(s)"), save_pos);
150 			FtpClose(ftpdata);
151 			return save_pos;
152 		}
153 		int chunk = min(data.GetLength() - save_pos, 1024);
154 		int ndata = FtpWrite((void *)data.GetIter(save_pos), chunk, ftpdata);
155 		LLOG("FtpWrite(" << chunk << ") -> " << ndata);
156 		if(ndata <= 0 || ndata > chunk) {
157 			error = FtpError(ftpdata);
158 			FtpClose(ftpdata);
159 			return save_pos;
160 		}
161 		save_pos += ndata;
162 #ifdef SLOWTRANSFER
163 		int end = GetTickCount() + SLOWTRANSFER;
164 		for(int d; (d = end - GetTickCount()) > 0; Sleep(d))
165 			;
166 #endif
167 	}
168 	WhenProgress(save_pos, data.GetLength());
169 	LLOG("FtpClose");
170 	FtpClose(ftpdata);
171 	return save_pos;
172 }
173 
Exists(const char * path)174 bool  FtpClient::Exists(const char *path) {
175 	LLOGBLOCK("FtpClient::Exists");
176 	if(!CheckOpen())
177 		return false;
178 	netbuf *data;
179 	LLOG("FtpAccess(" << path << ")");
180 	if(!FtpAccess(path, FTPLIB_FILE_READ, FTPLIB_IMAGE, ftpconn, &data)) {
181 		error = FtpError(ftpconn);
182 		return false;
183 	}
184 	FtpClose(data);
185 	return true;
186 }
187 
Rename(const char * oldpath,const char * newpath)188 bool FtpClient::Rename(const char *oldpath, const char *newpath) {
189 	LLOGBLOCK("FtpClient::Rename");
190 	LLOG("FtpRename(oldname = " << oldpath << ", newname = " << newpath << ")");
191 	return CheckOpen() && !!FtpRename(oldpath, newpath, ftpconn);
192 }
193 
Cd(const char * path)194 bool FtpClient::Cd(const char *path) {
195 	LLOGBLOCK("FtpClient::Cd");
196 	LLOG("FtpChdir(" << path << ")");
197 	return CheckOpen() && !!FtpChdir(path, ftpconn);
198 }
199 
MkDir(const char * path)200 bool FtpClient::MkDir(const char *path) {
201 	LLOGBLOCK("FtpClient::MkDir");
202 	LLOG("FtpMkdir(" << path << ")");
203 	return CheckOpen() && !!FtpMkdir(path, ftpconn);
204 }
205 
RmDir(const char * path)206 bool FtpClient::RmDir(const char *path) {
207 	LLOGBLOCK("FtpClient::RmDir");
208 	LLOG("FtpRmdir(" << path << ")");
209 	return CheckOpen() && !!FtpRmdir(path, ftpconn);
210 }
211 
Delete(const char * path)212 bool FtpClient::Delete(const char *path) {
213 	LLOGBLOCK("FtpClient::Delete");
214 	LLOG("FtpDelete(" << path << ")");
215 	return CheckOpen() && !!FtpDelete(path, ftpconn);
216 }
217 
List(const char * path)218 String FtpClient::List(const char *path)
219 {
220 	LLOGBLOCK("FtpClient::List");
221 	load_data = Null;
222 	if(!CheckOpen())
223 		return String::GetVoid();
224 	netbuf *ftpdata;
225 	LLOG("FtpAccess(" << path << ")");
226 	if(WhenProgress(0,0)) {
227 		error = t_("aborted");
228 		return String::GetVoid();
229 	}
230 	if(!FtpAccess(path, FTPLIB_DIR, FTPLIB_ASCII, ftpconn, &ftpdata)) {
231 		error = FtpError(ftpconn);
232 		return String::GetVoid();
233 	}
234 	while(!WhenProgress(load_data.GetLength(),0)) {
235 		byte buffer[1024];
236 		int ndata = FtpRead(buffer, sizeof(buffer), ftpdata);
237 		LLOG("FtpRead -> " << ndata);
238 		if(ndata < 0) {
239 			error = FtpError(ftpdata);
240 			FtpClose(ftpdata);
241 			return String::GetVoid();
242 		}
243 		if(ndata == 0) {
244 			load_data.Shrink();
245 			break;
246 		}
247 		load_data.Cat(buffer, ndata);
248 	#ifdef SLOWTRANSFER
249 		int end = GetTickCount() + SLOWTRANSFER;
250 		for(int d; (d = end - GetTickCount()) > 0; Sleep(d))
251 			;
252 	#endif
253 	}
254 	FtpClose(ftpdata);
255 	return load_data;
256 }
257 
RealizePath(const char * path)258 void FtpClient::RealizePath(const char *path)
259 {
260 	LLOGBLOCK("FtpClient::RealizePath");
261 	const char *s = path;
262 	if(*s == '\0') return;
263 	for(;;) {
264 		s = strchr(s + 1, '/');
265 		if(!s) break;
266 		MkDir(String(path, s));
267 	}
268 }
269 
FtpClientGet(String url,String * error)270 String FtpClientGet(String url, String *error)
271 {
272 	const char *u = url, *t = u;
273 	while(*t && *t != '?')
274 		if(*t++ == '/') {
275 			if(*t == '/')
276 				u = ++t;
277 			break;
278 		}
279 	t = u;
280 	while(*u && *u != '@' && *u != '/')
281 		u++;
282 	String host = String(t, u);
283 	String user, pwd;
284 	if(*u == '@') {
285 		t = ++u;
286 		while(*u && *u != '/' && *u != ':')
287 			u++;
288 		user = String(t, u);
289 		if(*u == ':') {
290 			t = ++u;
291 			while(*u && *u != '/')
292 				u++;
293 			pwd = String(t, u);
294 		}
295 	}
296 	FtpClient ftp;
297 	if(!ftp.Connect(host, user, pwd)) {
298 		if(error)
299 			*error = ftp.GetError();
300 		return String::GetVoid();
301 	}
302 	String data = ftp.Load(u);
303 	if(data.IsVoid()) {
304 		if(error)
305 			*error = ftp.GetError();
306 	}
307 	return data;
308 }
309 
310 }
311