forked from BojanJurca/Esp32_oscilloscope
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathftpServer.hpp
540 lines (399 loc) · 27.4 KB
/
ftpServer.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
/*
ftpServer.hpp
This file is part of Esp32_web_ftp_telnet_server_template project: https://github.com/BojanJurca/Esp32_web_ftp_telnet_server_template
FtpServer is built upon TcpServer with connectionHandler that handles TcpConnection according to FTP protocol.
This goes for control connection at least. FTP is a little more complicated since it uses another TCP connection
for data transfer. Beside that, data connection can be initialized by FTP server or FTP client and it is FTP client's
responsibility to decide which way it is going to be.
July, 25, 2021, Bojan Jurca
*/
#ifndef __FTP_SERVER__
#define __FTP_SERVER__
#ifndef HOSTNAME
#define HOSTNAME "MyESP32Server" // use default if not defined
#endif
#include <WiFi.h>
/*
Support for telnet dmesg command. If telnetServer.hpp is included in the project __ftpDmesg__ function will be redirected
to message queue defined there and dmesg command will display its contetn. Otherwise it will just display message on the
Serial console.
*/
void __ftpDmesg__ (String message) {
#ifdef __TELNET_SERVER__ // use dmesg from telnet server if possible
dmesg (message);
#else
Serial.printf ("[%10lu] %s\n", millis (), message.c_str ());
#endif
}
void (* ftpDmesg) (String) = __ftpDmesg__; // use this pointer to display / record system messages
#ifndef dmesg
#define dmesg ftpDmesg
#endif
/*
When FTP server is working in passive mode it tells FTP client on which port to establish data connection. Since there may be
multiple FTP sessions running at the same time the server must pass a different port number for passive data connection
to each client.
*/
int __pasiveDataPort__ () {
static SemaphoreHandle_t __pasiveDataPortSemaphore__= xSemaphoreCreateMutex ();
static int lastPasiveDataPort = 1024; // change pasive data port range if needed
xSemaphoreTake (__pasiveDataPortSemaphore__, portMAX_DELAY);
int pasiveDataPort = lastPasiveDataPort = (((lastPasiveDataPort + 1) % 16) + 1024); // change pasive data port range if needed
xSemaphoreGive (__pasiveDataPortSemaphore__);
return pasiveDataPort;
}
#include "TcpServer.hpp" // ftpServer.hpp is built upon TcpServer.hpp
#include "file_system.h" // ftpServer.hpp needs file_system.h
#include "user_management.h" // ftpServer.hpp needs user_management.h
class ftpServer: public TcpServer {
public:
// keep ftp session parameters in a structure
typedef struct {
String userName;
String homeDir;
String workingDir;
TcpConnection *controlConnection; // ftp control connection
TcpServer *pasiveDataServer; // for ftp pasive mode data connection
TcpClient *activeDataClient; // for ftp active mode data connection
// feel free to add more
} ftpSessionParameters;
ftpServer (String serverIP, // FTP server IP address, 0.0.0.0 for all available IP addresses
int serverPort, // FTP server port
bool (* firewallCallback) (String connectingIP) // a reference to callback function that will be celled when new connection arrives
): TcpServer (__staticFtpConnectionHandler__, (void *) this, 8 * 1024, (TIME_OUT_TYPE) 300000, serverIP, serverPort, firewallCallback)
{
if (started ()) dmesg ("[ftpServer] started on " + String (serverIP) + ":" + String (serverPort) + (firewallCallback ? " with firewall." : "."));
else dmesg ("[ftpServer] couldn't start.");
}
~ftpServer () { if (started ()) dmesg ("[ftpServer] stopped."); }
private:
static void __staticFtpConnectionHandler__ (TcpConnection *connection, void *ths) { // connectionHandler callback function
((ftpServer *) ths)->__ftpConnectionHandler__ (connection);
}
virtual void __ftpConnectionHandler__ (TcpConnection *connection) { // connectionHandler callback function
// this is where ftp session begins
ftpSessionParameters fsp = {"", "", "", connection, NULL, NULL};
String cmdLine;
if (!__fileSystemMounted__) {
connection->sendData ("220-" + String (HOSTNAME) + " file system file system not mounted. You may have to use telnet and mkfs.fat to format flash disk first.");
goto closeFtpConnection;
}
#if USER_MANAGEMENT == NO_USER_MANAGEMENT
if (!connection->sendData ("220-" + String (HOSTNAME) + " FTP server - everyone is allowed to login\r\n220 \r\n")) goto closeFtpConnection;
#else
if (!connection->sendData ("220-" + String (HOSTNAME) + " FTP server - please login\r\n220 \r\n")) goto closeFtpConnection;
#endif
while (13 == __readLineFromClient__ (&cmdLine, connection)) { // read and process comands in a loop
if (cmdLine.length ()) {
// ----- parse command line into arguments (max 32) -----
int argc = 0; String argv [8] = {"", "", "", "", "", "", "", ""};
argv [0] = String (cmdLine); argv [0].trim ();
String param = ""; // everything behind argv [0]
if (argv [0] != "") {
argc = 1;
while (argc < 8) {
int l = argv [argc - 1].indexOf (" ");
if (l > 0) {
argv [argc] = argv [argc - 1].substring (l + 1);
argv [argc].trim ();
if (argc == 1) param = argv [argc]; // remember the whole parameter line in its original form
argv [argc - 1] = argv [argc - 1].substring (0, l); // no need to trim
argc ++;
} else {
break;
}
}
}
//debug FTP protocol: Serial.print ("<--"); for (int i = 0; i < argc; i++) Serial.print (argv [i] + " "); Serial.println ();
// ----- try to handle ftp command -----
String s = __internalFtpCommandHandler__ (argc, argv, param, &fsp);
if (!connection->sendData (s)) goto closeFtpConnection; // send reply to FTP client
//debug FTP protocol: Serial.println ("-->" + s);
} // if cmdLine is not empty
} // read and process comands in a loop
closeFtpConnection:
if (fsp.userName != "") dmesg ("[ftpServer] " + fsp.userName + " logged out.");
}
// returns last chracter pressed (Enter or 0 in case of error
static char __readLineFromClient__ (String *line, TcpConnection *connection) {
*line = "";
char c;
while (connection->recvData (&c, 1)) { // read and process incomming data in a loop
switch (c) {
case 10: // ignore
break;
case 13: // end of command line
return 13;
default: // fill the buffer
*line += c;
break;
} // switch
} // while
return false;
}
// ----- built-in commands -----
String __internalFtpCommandHandler__ (int argc, String argv [], String param, ftpServer::ftpSessionParameters *fsp) {
argv [0].toUpperCase ();
if (argv [0] == "OPTS") { //------------------------------------------- OPTS (before login process)
if (argc == 3) return this->__OPTS__ (argv [1], argv [2]);
} else if (argv [0] == "USER") { //------------------------------------ USER (login process)
if (argc <= 2) return this->__USER__ (argv [1], fsp); // not entering user name may be ok for anonymous login
} else if (argv [0] == "PASS") { //------------------------------------ PASS (login process)
if (argc <= 2) return this->__PASS__ (argv [1], fsp); // not entering password may be ok for anonymous login
} else if (argv [0] == "CWD") { //------------------------------------ CWD (cd directory)
if (argc >= 2) return this->__CWD__ (param, fsp); // use param instead of argv [1] to enable the use of long file names
} else if (argv [0] == "PWD" || argv [0] == "XPWD") { //-------------- PWD, XPWD
if (argc == 1) return this->__PWD__ (fsp);
} else if (argv [0] == "TYPE") { //----------------------------------- TYPE
return this->__TYPE__ (fsp);
} else if (argv [0] == "NOOP") { //----------------------------------- NOOP
return this->__NOOP__ ();
} else if (argv [0] == "SYST") { //----------------------------------- SYST
return this->__SYST__ (fsp);
} else if (argv [0] == "SIZE") { //----------------------------------- SIZE
if (argc == 2) return this->__SIZE__ (argv [1], fsp);
} else if (argv [0] == "PASV") { //----------------------------------- PASV - start pasive data transfer (always followed by one of data transfer commands)
if (argc == 1) return this->__PASV__ (fsp);
} else if (argv [0] == "PORT") { //----------------------------------- PORT - start active data transfer (always followed by one of data transfer commands)
if (argc == 2) return this->__PORT__ (argv [1], fsp);
} else if (argv [0] == "NLST" || argv [0] == "LIST") { //------------- NLST, LIST (ls directory) - also ends pasive or active data transfer
if (argc == 1) return this->__NLST__ (fsp->workingDir, fsp);
if (argc == 2) return this->__NLST__ (argv [1], fsp);
} else if (argv [0] == "CWD") { //------------------------------------ CWD (cd directory)
if (argc >= 2) return this->__CWD__ (param, fsp); // use param instead of argv [1] to enable the use of long file names
} else if (argv [0] == "XMKD" || argv [0] == "MKD") { //-------------- XMKD, MKD (mkdir directory)
if (argc >= 2) return this->__XMKD__ (param, fsp); // use param instead of argv [1] to enable the use of long file names
} else if (argv [0] == "RNFR" || argv [0] == ".....") { //-------------- RNFR, MKD (... file or directory)
if (argc >= 2) return this->__RNFR__ (param, fsp); // use param instead of argv [1] to enable the use of long file names
} else if (argv [0] == "XRMD" || argv [0] == "DELE") { //------------- XRMD, DELE (rm file or directory)
if (argc >= 2) return this->__XRMD__ (param, fsp); // use param instead of argv [1] to enable the use of long file names
} else if (argv [0] == "RETR") { //----------------------------------- RETR (get fileName) - also ends pasive or active data transfer
if (argc >= 2) return this->__RETR__ (param, fsp); // use param instead of argv [1] to enable the use of long file names
} else if (argv [0] == "STOR") { //----------------------------------- STOR (put fileName) - also ends pasive or active data transfer
if (argc >= 2) return this->__STOR__ (param, fsp); // use param instead of argv [1] to enable the use of long file names
} else if (argv [0] == "QUIT") { //----------------------------------- QUIT
return this->__QUIT__ (fsp);
}
// -------------------------------------------------------------------- INVALID COMMAND
return "502 command not implemented\r\n"; // "220 unsupported command\r\n";
}
inline String __OPTS__ (String argv1, String argv2) { // enable UTF8
argv1.toUpperCase (); argv2.toUpperCase ();
if (argv1 == "UTF8" && argv2 == "ON") return "200 UTF8 enabled\r\n";
return "502 command not implemented\r\n";
}
inline String __USER__ (String userName, ftpSessionParameters *fsp) { // save user name and require password
fsp->userName = userName; return "331 enter password\r\n";
}
String __PASS__ (String password, ftpSessionParameters *fsp) { // login
if (checkUserNameAndPassword (fsp->userName, password)) fsp->workingDir = fsp->homeDir = getUserHomeDirectory (fsp->userName);
if (fsp->homeDir > "") { // if logged in
dmesg ("[ftpServer] " + fsp->userName + " logged in.");
return "230 logged on, your home directory is \"" + fsp->homeDir + "\"\r\n";
} else {
dmesg ("[ftpServer] " + fsp->userName + " login attempt failed.");
return "530 user name or password incorrect\r\n";
}
}
inline String __PWD__ (ftpSessionParameters *fsp) {
if (fsp->homeDir == "") return "530 not logged in\r\n";
return "257 \"" + fsp->workingDir + "\"\r\n";
}
inline String __TYPE__ (ftpSessionParameters *fsp) { // command needs to be implemented but we are not going to repond to it
if (fsp->homeDir == "") return "530 not logged in\r\n";
return "200 ok\r\n";
}
inline String __NOOP__ () {
return "200 ok\r\n";
}
inline String __SYST__ (ftpSessionParameters *fsp) { // command needs to be implemented but we are going to pretend this is a UNIX system
if (fsp->homeDir == "") return "530 not logged in\r\n";
return "215 UNIX Type: L8\r\n";
}
inline String __SIZE__ (String fileName, ftpSessionParameters *fsp) {
if (fsp->homeDir == "") return "530 not logged in\r\n";
String fp = fullFilePath (fileName, fsp->workingDir);
if (fp == "") return "550 invalid file name\r\n";
if (!userMayAccess (fp, fsp->homeDir)) return "550 access denyed\r\n";
unsigned long fSize = 0;
File f = FFat.open (fp, FILE_READ);
if (f) {
fSize = f.size ();
f.close ();
}
return "213 " + String (fSize) + "\r\n";
}
inline String __PASV__ (ftpSessionParameters *fsp) {
if (fsp->homeDir == "") return "530 not logged in\r\n";
int ip1, ip2, ip3, ip4, p1, p2; // get (this) server IP and next free port
if (4 == sscanf (fsp->controlConnection->getThisSideIP ().c_str (), "%i.%i.%i.%i", &ip1, &ip2, &ip3, &ip4)) {
// get next free port
int pasiveDataPort = __pasiveDataPort__ ();
// open a new TCP server to accept pasive data connection
fsp->pasiveDataServer = new TcpServer ((TIME_OUT_TYPE) 5000, fsp->controlConnection->getThisSideIP (), pasiveDataPort, NULL);
// report to ftp client through control connection how to connect for data exchange
p2 = pasiveDataPort % 256;
p1 = pasiveDataPort / 256;
if (fsp->pasiveDataServer) return "227 entering passive mode (" + String (ip1) + "," + String (ip2) + "," + String (ip3) + "," + String (ip4) + "," + String (p1) + "," + String (p2) + ")\r\n";
}
return "425 can't open data connection\r\n";
}
inline String __PORT__ (String dataConnectionInfo, ftpSessionParameters *fsp) {
if (fsp->homeDir == "") return "530 not logged in\r\n";
int ip1, ip2, ip3, ip4, p1, p2; // get client IP and port
if (6 == sscanf (dataConnectionInfo.c_str (), "%i,%i,%i,%i,%i,%i", &ip1, &ip2, &ip3, &ip4, &p1, &p2)) {
char activeDataIP [16];
int activeDataPort;
sprintf (activeDataIP, "%i.%i.%i.%i", ip1, ip2, ip3, ip4);
activeDataPort = 256 * p1 + p2;
fsp->activeDataClient = new TcpClient (activeDataIP, activeDataPort, (TIME_OUT_TYPE) 5000); // open a new TCP client for active data connection
if (fsp->activeDataClient) {
if (fsp->activeDataClient->connection ()) return "200 port ok\r\n";
delete (fsp->activeDataClient);
fsp->activeDataClient = NULL;
}
}
return "425 can't open data connection\r\n";
}
inline String __NLST__ (String& directory, ftpSessionParameters *fsp) {
if (fsp->homeDir == "") return "530 not logged in\r\n";
String fp = fullDirectoryPath (directory, fsp->workingDir);
if (fp == "" || !isDirectory (fp)) return "550 invalid directory\r\n";
if (!userMayAccess (fp, fsp->homeDir)) return "550 access denyed\r\n";
if (fp.substring (fp.length () - 1) != "/") fp += '/'; // full directoy path always ends with /
if (!fsp->controlConnection->sendData ((char *) "150 starting transfer\r\n")) return ""; // control connection closed
int bytesWritten = 0;
TcpConnection *dataConnection = NULL;
if (fsp->pasiveDataServer) while (!(dataConnection = fsp->pasiveDataServer->connection ()) && !fsp->pasiveDataServer->timeOut ()) delay (1); // wait until a connection arrives to non-threaded server or time-out occurs
if (fsp->activeDataClient) dataConnection = fsp->activeDataClient->connection (); // non-threaded client differs from non-threaded server - connection is established before constructor returns or not at all
if (dataConnection) bytesWritten = dataConnection->sendData (listDirectory (fp) + "\r\n");
if (fsp->activeDataClient) { delete (fsp->activeDataClient); fsp->activeDataClient = NULL; }
if (fsp->pasiveDataServer) { delete (fsp->pasiveDataServer); fsp->pasiveDataServer = NULL; }
if (bytesWritten) return "226 transfer complete\r\n";
return "425 can't open data connection\r\n";
}
inline String __CWD__ (String directory, ftpSessionParameters *fsp) {
if (fsp->homeDir == "") return "530 not logged in\r\n";
String fp = fullDirectoryPath (directory, fsp->workingDir);
if (fp == "" || !isDirectory (fp)) return "501 invalid directory " + fp +"\r\n";
if (!userMayAccess (fp, fsp->homeDir)) return "550 access denyed\r\n";
fsp->workingDir = fp; return "250 your working directory is now " + fsp->workingDir + "\r\n";
}
inline String __XMKD__ (String directory, ftpSessionParameters *fsp) {
if (fsp->homeDir == "") return "530 not logged in\r\n";
String fp = fullDirectoryPath (directory, fsp->workingDir);
if (fp == "") return "501 invalid directory\r\n";
if (!userMayAccess (fp, fsp->homeDir)) return "550 access denyed\r\n";
if (makeDir (fp)) return "257 \"" + fp + "\" created\r\n";
return "550 could not create \"" + fp + "\"\r\n";
}
inline String __RNFR__ (String fileOrDirName, ftpSessionParameters *fsp) {
if (fsp->homeDir == "") return "530 not logged in\r\n";
if (!fsp->controlConnection->sendData ((char *) "350 need more information\r\n")) return "";
String s;
if (13 != __readLineFromClient__ (&s, fsp->controlConnection)) return "503 wrong command syntax\r\n";
if (s.substring (0, 4) != "RNTO") return "503 wrong command syntax\r\n";
s = s.substring (4); s.trim ();
String fp1 = fullFilePath (fileOrDirName, fsp->workingDir);
if (fp1 == "") return "501 invalid directory\r\n";
if (!userMayAccess (fp1, fsp->homeDir)) return "553 access denyed\r\n";
String fp2 = fullFilePath (s, fsp->workingDir);
if (fp2 == "") return "501 invalid directory\r\n";
if (!userMayAccess (fp2, fsp->homeDir)) return "553 access denyed\r\n";
if (FFat.rename (fp1, fp2)) return "250 renamed to " + s + "\r\n";
return "553 unable to rename " + fileOrDirName + "\r\n";
}
inline String __XRMD__ (String fileOrDirName, ftpSessionParameters *fsp) {
if (fsp->homeDir == "") return "530 not logged in\r\n";
String fp = fullFilePath (fileOrDirName, fsp->workingDir);
if (fp == "") return "501 invalid file or directory name\r\n";
if (!userMayAccess (fp, fsp->homeDir)) return "550 access denyed\r\n";
if (isFile (fp)) {
if (deleteFile (fp)) return "250 " + fp + " deleted\r\n";
return "452 could not delete " + fp + "\r\n";
} else {
if (fp == fsp->homeDir) return "550 you can't remove your home directory\r\n";
if (fp == fsp->workingDir) return "550 you can't remove your working directory\r\n";
if (removeDir (fp)) return "250 " + fp + " removed\r\n";
return "452 could not remove " + fp + "\r\n";
}
}
inline String __RETR__ (String fileName, ftpSessionParameters *fsp) {
if (fsp->homeDir == "") return "530 not logged in\r\n";
String fp = fullFilePath (fileName, fsp->workingDir);
if (fp == "" || !isFile (fp)) return "501 invalid file name\r\n";
if (!userMayAccess (fp, fsp->homeDir)) return "550 access denyed\r\n";
if (!fsp->controlConnection->sendData ((char *) "150 starting transfer\r\n")) return ""; // control connection closed
int bytesWritten = 0; int bytesRead = 0;
TcpConnection *dataConnection = NULL;
if (fsp->pasiveDataServer) while (!(dataConnection = fsp->pasiveDataServer->connection ()) && !fsp->pasiveDataServer->timeOut ()) delay (1); // wait until a connection arrives to non-threaded server or time-out occurs
if (fsp->activeDataClient) dataConnection = fsp->activeDataClient->connection (); // non-threaded client differs from non-threaded server - connection is established before constructor returns or not at all
if (dataConnection) {
File f = FFat.open (fp, FILE_READ);
if (f) {
if (!f.isDirectory ()) {
// read data from file and transfer it through data connection
byte *buff = (byte *) malloc (2048); // get 2048 B of memory from heap (not from the stack)
if (buff) {
int i = bytesWritten = 0;
while (f.available ()) {
*(buff + i++) = f.read ();
if (i == 2048) { bytesRead += 2048; bytesWritten += dataConnection->sendData ((char *) buff, 2048); i = 0; }
}
if (i) { bytesRead += i; bytesWritten += dataConnection->sendData ((char *) buff, i); }
free (buff);
}
}
f.close ();
}
}
if (fsp->activeDataClient) { delete (fsp->activeDataClient); fsp->activeDataClient = NULL; }
if (fsp->pasiveDataServer) { delete (fsp->pasiveDataServer); fsp->pasiveDataServer = NULL; }
if (bytesRead && bytesWritten == bytesRead) return "226 transfer complete\r\n";
return "550 file could not be transfered\r\n";
}
inline String __STOR__ (String fileName, ftpSessionParameters *fsp) {
if (fsp->homeDir == "") return "530 not logged in\r\n";
String fp = fullFilePath (fileName, fsp->workingDir);
if (fp == "" || isDirectory (fp)) return "501 invalid file name\r\n";
if (!userMayAccess (fp, fsp->homeDir)) return "550 access denyed\r\n";
if (!fsp->controlConnection->sendData ((char *) "150 starting transfer\r\n")) return ""; // control connection closed
int bytesWritten = 0; int bytesRead = 0;
TcpConnection *dataConnection = NULL;
if (fsp->pasiveDataServer) while (!(dataConnection = fsp->pasiveDataServer->connection ()) && !fsp->pasiveDataServer->timeOut ()) delay (1); // wait until a connection arrives to non-threaded server or time-out occurs
if (fsp->activeDataClient) dataConnection = fsp->activeDataClient->connection (); // non-threaded client differs from non-threaded server - connection is established before constructor returns or not at all
if (dataConnection) {
File f = FFat.open (fp, FILE_WRITE);
if (f) {
#define BUFF_SIZE 2048
byte *buff = (byte *) malloc (BUFF_SIZE); // get 2048 B of memory from heap (not from the )
if (buff) {
int received;
do {
bytesRead += (received = dataConnection->recvData ((char *) buff, BUFF_SIZE));
int written = f.write (buff, received);
if (received && (written == received)) bytesWritten += written;
} while (received);
free (buff);
}
f.close ();
} else {
dmesg ("[ftpServer] could not open " + fp + " for writing.");
}
}
if (fsp->activeDataClient) { delete (fsp->activeDataClient); fsp->activeDataClient = NULL; }
if (fsp->pasiveDataServer) { delete (fsp->pasiveDataServer); fsp->pasiveDataServer = NULL; }
if (bytesRead && bytesWritten == bytesRead) return "226 transfer complete\r\n";
return "550 file could not be transfered\r\n";
}
inline String __QUIT__ (ftpSessionParameters *fsp) {
// close data connection if open
if (fsp->activeDataClient) delete (fsp->activeDataClient);
if (fsp->pasiveDataServer) delete (fsp->pasiveDataServer);
// report client we are closing contro connection
fsp->controlConnection->sendData ((char *) "221 closing control connection\r\n");
fsp->controlConnection->closeConnection ();
return "";
}
};
#endif