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