Arduino TinyFTP
Loading...
Searching...
No Matches
FTPBasicAPI.h
1#pragma once
2
3#include "Arduino.h"
4#include "FTPCommon.h"
5#include "FTPLogger.h"
6
7namespace ftp_client {
8
18 friend class FTPFile;
19
20 public:
21 FTPBasicAPI() { FTPLogger::writeLog(LOG_DEBUG, "FTPBasicAPI"); }
22
23 ~FTPBasicAPI() { FTPLogger::writeLog(LOG_DEBUG, "~FTPBasicAPI"); }
24
25 bool begin(Client *cmdPar, Client *dataPar, IPAddress &address, int port,
26 const char *username, const char *password) {
27 FTPLogger::writeLog(LOG_DEBUG, "FTPBasicAPI ", "open");
28 command_ptr = cmdPar;
29 data_ptr = dataPar;
30 remote_address = address;
31
32 if (!connect(address, port, command_ptr, true)) return false;
33 if (username != nullptr) {
34 const char *ok_result[] = {"331", "230", "530", nullptr};
35 if (!cmd("USER", username, ok_result)) return false;
36 }
37 if (password != nullptr) {
38 const char *ok_result[] = {"230", "202", nullptr};
39 if (!cmd("PASS", password, ok_result)) return false;
40 }
41
42 is_open = true;
43 const char *ok_result[] = {"200", nullptr};
44 cmd("OPTS", "UTF8", ok_result);
45
46 return true;
47 }
48
49 bool quit() {
50 FTPLogger::writeLog(LOG_DEBUG, "FTPBasicAPI", "quit");
51 const char *ok_result[] = {"221", "226", nullptr};
52 bool result = cmd("QUIT", nullptr, ok_result, false);
53 if (!result) {
54 result = cmd("BYE", nullptr, ok_result, false);
55 }
56 if (!result) {
57 result = cmd("DISCONNECT", nullptr, ok_result, false);
58 }
59 return result;
60 }
61
62 bool connected() { return is_open; }
63
64 operator bool() { return is_open; }
65
66 bool passv() {
67 FTPLogger::writeLog(LOG_DEBUG, "FTPBasicAPI", "passv");
68 bool ok = cmd("PASV", nullptr, "227");
69 if (ok) {
70 char buffer[80];
71 FTPLogger::writeLog(LOG_DEBUG, "FTPBasicAPI::passv", result_reply);
72 // determine data port
73 int start1 = CStringFunctions::findNthInStr(result_reply, ',', 4) + 1;
74 int p1 = atoi(result_reply + start1);
75
76 sprintf(buffer, "*** port1 -> %d ", p1);
77 FTPLogger::writeLog(LOG_DEBUG, "FTPBasicAPI::passv", buffer);
78
79 int start2 = CStringFunctions::findNthInStr(result_reply, ',', 5) + 1;
80 int p2 = atoi(result_reply + start2);
81
82 sprintf(buffer, "*** port2 -> %d ", p2);
83 FTPLogger::writeLog(LOG_DEBUG, "FTPBasicAPI::passv", buffer);
84
85 int dataPort = (p1 * 256) + p2;
86 sprintf(buffer, "*** data port: %d", dataPort);
87 FTPLogger::writeLog(LOG_DEBUG, "FTPBasicAPI::passv", buffer);
88
89 ok = connect(remote_address, dataPort, data_ptr) == 1;
90 }
91 return ok;
92 }
93
94 bool del(const char *file) {
95 FTPLogger::writeLog(LOG_DEBUG, "FTPBasicAPI", "del");
96 return cmd("DELE", file, "250");
97 }
98
99 bool mkdir(const char *dir) {
100 FTPLogger::writeLog(LOG_DEBUG, "FTPBasicAPI", "mkdir");
101 return cmd("MKD", dir, "257");
102 }
103
104 bool rmd(const char *dir) {
105 FTPLogger::writeLog(LOG_DEBUG, "FTPBasicAPI", "rd");
106 return cmd("RMD", dir, "250");
107 }
108
109 size_t size(const char *file) {
110 FTPLogger::writeLog(LOG_DEBUG, "FTPBasicAPI", "size");
111 if (cmd("SIZE", file, "213")) {
112 return atol(result_reply + 4);
113 }
114 return 0;
115 }
116
117 ObjectType objectType(const char *file) {
118 FTPLogger::writeLog(LOG_DEBUG, "FTPBasicAPI", "objectType");
119 const char *ok_result[] = {"213", "550", nullptr};
120 ObjectType result = TypeDirectory;
121 if (cmd("SIZE", file, ok_result)) {
122 if (strncmp(result_reply, "213", 3) == 0) {
123 result = TypeFile;
124 }
125 }
126 return result;
127 }
128
129 bool abort() {
130 bool rc = true;
131 if (current_operation == READ_OP || current_operation == WRITE_OP ||
132 current_operation == LS_OP) {
133 FTPLogger::writeLog(LOG_DEBUG, "FTPBasicAPI", "abort");
134 data_ptr->stop();
135
136 const char *ok[] = {"426", "226", "225", nullptr};
137 setCurrentOperation(NOP);
138 rc = cmd("ABOR", nullptr, ok);
139 delay(FTP_ABORT_DELAY_MS);
140 while (command_ptr->available() > 0) {
141 command_ptr->read();
142 }
143 }
144 return rc;
145 }
146
147 bool binary() {
148 FTPLogger::writeLog(LOG_DEBUG, "FTPBasicAPI", "binary");
149 const char *type_cmd = use_type ? "TYPE I" : "BIN";
150 return cmd(type_cmd, nullptr, "200");
151 }
152
153 bool ascii() {
154 FTPLogger::writeLog(LOG_DEBUG, "FTPBasicAPI", "ascii");
155 const char *type_cmd = use_type ? "TYPE A" : "ASC";
156 return cmd(type_cmd, nullptr, "200");
157 }
158
159 bool type(const char *txt) {
160 FTPLogger::writeLog(LOG_DEBUG, "FTPBasicAPI", "type");
161 return cmd("TYPE", txt, "200");
162 }
163
164 Stream *read(const char *file_name) {
165 if (current_operation != READ_OP) {
166 FTPLogger::writeLog(LOG_DEBUG, "FTPBasicAPI", "read");
167 const char *ok[] = {"150", "125", nullptr};
168 cmd("RETR", file_name, ok);
169 setCurrentOperation(READ_OP);
170 }
171 return data_ptr;
172 }
173
174 Stream *write(const char *file_name, FileMode mode) {
175 if (current_operation != WRITE_OP) {
176 FTPLogger::writeLog(LOG_DEBUG, "FTPBasicAPI", "write");
177 const char *ok_write[] = {"125", "150", nullptr};
178 cmd(mode == WRITE_APPEND_MODE ? "APPE" : "STOR", file_name, ok_write);
179 setCurrentOperation(WRITE_OP);
180 }
181 return data_ptr;
182 }
183
184 Stream *ls(const char *file_name) {
185 FTPLogger::writeLog(LOG_DEBUG, "FTPBasicAPI", "ls");
186 const char *ok[] = {"125", "150", nullptr};
187 cmd("NLST", file_name, ok);
188 setCurrentOperation(LS_OP);
189 return data_ptr;
190 }
191
192 void closeData() {
193 FTPLogger::writeLog(LOG_INFO, "FTPBasicAPI", "closeData");
194 data_ptr->stop();
195
196 // abort(); ?
197
198 }
199
200 void setCurrentOperation(CurrentOperation op) {
201 char msg[80];
202 sprintf(msg, "setCurrentOperation: %d", (int)op);
203 FTPLogger::writeLog(LOG_DEBUG, "FTPBasicAPI", msg);
204 current_operation = op;
205 }
206
207 CurrentOperation currentOperation() { return current_operation; }
208
209 void flush() {
210 FTPLogger::writeLog(LOG_DEBUG, "FTPBasicAPI", "flush");
211 data_ptr->flush();
212 }
213
214 bool checkResult(const char *expected[], const char *command,
215 bool wait_for_data = true) {
216 // consume all result lines
217 bool ok = false;
218 result_reply[0] = '\0';
219 Stream *stream_ptr = command_ptr;
220
221 char result_str[FTP_RESULT_BUFFER_SIZE];
222 if (wait_for_data || stream_ptr->available()) {
223 // wait for reply
224 while (stream_ptr->available() == 0) {
225 delay(100);
226 }
227 // read reply
228 // result_str = stream_ptr->readStringUntil('\n').c_str();
229 CStringFunctions::readln(*stream_ptr, result_str, FTP_RESULT_BUFFER_SIZE);
230
231 if (strlen(result_str) > 3) {
232 FTPLogger::writeLog(LOG_DEBUG, "FTPBasicAPI::checkResult", result_str);
233 strncpy(result_reply, result_str, sizeof(result_reply) - 1);
234 // if we did not expect anything
235 if (expected[0] == nullptr) {
236 ok = true;
237 FTPLogger::writeLog(LOG_DEBUG, "FTPBasicAPI::checkResult",
238 "success because of not expected result codes");
239 } else {
240 // check for valid codes
241 char msg[80];
242 for (int j = 0; expected[j] != nullptr; j++) {
243 sprintf(msg, "- checking with %s", expected[j]);
244 FTPLogger::writeLog(LOG_DEBUG, "FTPBasicAPI::checkResult", msg);
245 if (strncmp(result_str, expected[j], 3) == 0) {
246 sprintf(msg, " -> success with %s", expected[j]);
247 FTPLogger::writeLog(LOG_DEBUG, "FTPBasicAPI::checkResult", msg);
248 ok = true;
249 break;
250 }
251 }
252 }
253 } else {
254 // if we got am empty line and we dont need to wait we are still ok
255 if (!wait_for_data) ok = true;
256 }
257 } else {
258 ok = true;
259 }
260
261 // log error
262 if (!ok) {
263 FTPLogger::writeLog(LOG_ERROR, "FTPBasicAPI::checkResult", command);
264 FTPLogger::writeLog(LOG_ERROR, "FTPBasicAPI::checkResult", result_reply);
265 }
266 return ok;
267 }
268
269 bool cmd(const char *command, const char *par, const char *expected,
270 bool wait_for_data = true) {
271 const char *expected_array[] = {expected, nullptr};
272 return cmd(command, par, expected_array, wait_for_data);
273 }
274
275 bool cmd(const char *command_str, const char *par, const char *expected[],
276 bool wait_for_data = true) {
277 char command_buffer[FTP_COMMAND_BUFFER_SIZE];
278 Stream *stream_ptr = command_ptr;
279 if (par == nullptr) {
280 strncpy(command_buffer, command_str, FTP_COMMAND_BUFFER_SIZE);
281 stream_ptr->println(command_buffer);
282 } else {
283 snprintf(command_buffer, FTP_COMMAND_BUFFER_SIZE, "%s %s", command_str,
284 par);
285 stream_ptr->println(command_buffer);
286 }
287 FTPLogger::writeLog(LOG_DEBUG, "FTPBasicAPI::cmd", command_buffer);
288
289 return checkResult(expected, command_buffer, wait_for_data);
290 }
291
292 void setUseTypeCommand(bool useType) {
293 use_type = useType;
294 }
295
296 protected:
297 // currently running op -> do we need to cancel ?
298 CurrentOperation current_operation = NOP;
299 Client *command_ptr = nullptr; // Client for commands
300 Client *data_ptr = nullptr; // Client for upload and download of files
301 IPAddress remote_address;
302 bool is_open = false;
303 bool use_type = false;
304 char result_reply[100];
305
306 bool connect(IPAddress adr, int port, Client *client_ptr,
307 bool doCheckResult = false) {
308 char buffer[80];
309 bool ok = true;
310#if defined(ESP32) || defined(ESP8266)
311 sprintf(buffer, "connect %s:%d", adr.toString().c_str(), port);
312 FTPLogger::writeLog(LOG_DEBUG, "FTPBasicAPI::connect", buffer);
313#endif
314 // try to connect 10 times
315 if (client_ptr->connected()) client_ptr->stop(); // make sure we start with a clean state
316 for (int j = 0; j < 10; j++) {
317 ok = client_ptr->connect(adr, port);
318 if (ok) break;
319 delay(500);
320 }
321 // ok = client_ptr->connect(adr, port);
322 ok = client_ptr->connected();
323 if (ok && doCheckResult) {
324 const char *ok_result[] = {"220", "200", nullptr};
325 ok = checkResult(ok_result, "connect");
326
327 // there might be some more messages: e.g. in FileZilla: we just ignore
328 // them
329 while (command_ptr->available() > 0) {
330 command_ptr->read();
331 }
332 }
333 // log result
334 if (ok) {
335 FTPLogger::writeLog(LOG_DEBUG, "FTPBasicAPI::connected", buffer);
336 } else {
337 FTPLogger::writeLog(LOG_ERROR, "FTPBasicAPI::connected", buffer);
338 }
339 return ok;
340 }
341};
342
343} // namespace ftp_client
FTPBasicAPI Implementation of Low Level FTP protocol. In order to simplify the logic we always use Pa...
Definition FTPBasicAPI.h:17
FTPFile A single file which supports read and write operations. This class is implemented as an Ardui...
Definition FTPFile.h:16