FTP client library for mbed-os

Revision:
0:c2da359279a6
Child:
1:e069c405c934
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FTPClient.cpp	Wed Feb 07 07:01:38 2018 +0000
@@ -0,0 +1,392 @@
+/* FTP client library
+ * Copyright (c) 2018 Renesas Electronics Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mbed.h"
+#include "FTPClient.h"
+
+#define FTP_BUF_SIZE     (1460)
+
+FTPClient::FTPClient(NetworkInterface *net, const char* root) {
+    _ctr_open = false;
+    _login = false;
+    strcpy(_root, root);
+    p_network = net;
+    p_ftp_buf = new char[FTP_BUF_SIZE];
+}
+
+FTPClient::~FTPClient() {
+    delete [] p_ftp_buf;
+}
+
+bool FTPClient::open(const char* ip_addr, int port, const char* user, const char* pass) {
+    if (_ctr_open) {
+        FTPClientControlSock.close();
+    }
+
+    FTPClientControlSock.open(p_network);
+    if (FTPClientControlSock.connect(ip_addr, port) < 0) {
+        return false;
+    }
+    _ctr_open = true;
+
+    if (FTPClientControlSock.recv(p_ftp_buf, FTP_BUF_SIZE) <= 0) {
+        return false;
+    }
+    if (strncmp(p_ftp_buf, "220", 3) != 0) {
+        return false;
+    }
+
+    _login = false;
+    sprintf(p_ftp_buf, "user %s\r\n", user);
+    FTPClientControlSock.send(p_ftp_buf, strlen(p_ftp_buf));
+    if (FTPClientControlSock.recv(p_ftp_buf, FTP_BUF_SIZE) <= 0) {
+        return false;
+    }
+    if (strncmp(p_ftp_buf, "331", 3) != 0) {
+        return false;
+    }
+
+    sprintf(p_ftp_buf, "pass %s\r\n", pass);
+    FTPClientControlSock.send(p_ftp_buf, strlen(p_ftp_buf));
+    if (FTPClientControlSock.recv(p_ftp_buf, FTP_BUF_SIZE) <= 0) {
+        return false;
+    }
+    if (strncmp(p_ftp_buf, "230", 3) != 0) {
+        return false;
+    }
+    _login = true;
+    return true;
+}
+
+bool FTPClient::quit() {
+    if (!_login) {
+        return false;
+    }
+
+    sprintf(p_ftp_buf, "quit\r\n");
+    FTPClientControlSock.send(p_ftp_buf, strlen(p_ftp_buf));
+    if (FTPClientControlSock.recv(p_ftp_buf, FTP_BUF_SIZE) <= 0) {
+        return false;
+    }
+    if (strncmp(p_ftp_buf, "250", 3) != 0) {
+        return false;
+    }
+    _login = false;
+
+    FTPClientControlSock.close();
+    _ctr_open = false;
+    return true;
+}
+
+bool FTPClient::get(const char* file_name) {
+    FILE* fp;
+    int size;
+
+    if (!_login) {
+        return false;
+    }
+
+    if (!open_data_sock()) {
+        return false;
+    }
+
+    FTPClientDataSock.set_timeout(500);
+    sprintf(p_ftp_buf, "retr %s\r\n", file_name);
+    FTPClientControlSock.send(p_ftp_buf, strlen(p_ftp_buf));
+    if (FTPClientControlSock.recv(p_ftp_buf, FTP_BUF_SIZE) <= 0) {
+        return false;
+    }
+    if ((strncmp(p_ftp_buf, "150", 3) != 0) && (strncmp(p_ftp_buf, "125", 3) != 0)) {
+        return false;
+    }
+
+    sprintf(p_ftp_buf, "%s/%s", _root, file_name);
+    fp = fopen(p_ftp_buf, "w");
+    while (1) {
+        size = FTPClientDataSock.recv(p_ftp_buf, FTP_BUF_SIZE);
+        if (size > 0) {
+            fwrite(p_ftp_buf, size, sizeof(char), fp);
+        } else {
+            break;
+        }
+    }
+    fclose(fp);
+    FTPClientDataSock.close();
+
+    if (FTPClientControlSock.recv(p_ftp_buf, FTP_BUF_SIZE) <= 0) {
+        return false;
+    }
+    if (strncmp(p_ftp_buf, "226", 3) != 0) {
+        return false;
+    }
+    return true;
+}
+
+bool FTPClient::put(const char* file_name) {
+    FILE* fp;
+    int32_t remain_size;
+    int32_t send_size;
+
+    if (!_login) {
+        return false;
+    }
+
+    if (!open_data_sock()) {
+        return false;
+    }
+
+    FTPClientDataSock.set_timeout(osWaitForever);
+    sprintf(p_ftp_buf, "stor %s\r\n", file_name);
+    FTPClientControlSock.send(p_ftp_buf, strlen(p_ftp_buf));
+    if (FTPClientControlSock.recv(p_ftp_buf, FTP_BUF_SIZE) <= 0) {
+        return false;
+    }
+    if ((strncmp(p_ftp_buf, "150", 3) != 0) && (strncmp(p_ftp_buf, "125", 3) != 0)) {
+        return false;
+    }
+
+    sprintf(p_ftp_buf, "%s/%s", _root, file_name);
+    fp = fopen(p_ftp_buf, "r"); 
+    fseek(fp, 0, SEEK_END);
+    remain_size = ftell(fp);
+    fseek(fp, 0, SEEK_SET);
+
+    while (remain_size > 0) {
+        if (remain_size > FTP_BUF_SIZE) {
+            send_size = FTP_BUF_SIZE;
+        } else {
+            send_size = remain_size;
+        }
+        fread(p_ftp_buf, 1, send_size, fp);
+        FTPClientDataSock.send(p_ftp_buf, send_size);
+        remain_size -= send_size;
+    }
+    fclose(fp); 
+    FTPClientDataSock.close();
+
+    if (FTPClientControlSock.recv(p_ftp_buf, FTP_BUF_SIZE) <= 0) {
+        return false;
+    }
+    if (strncmp(p_ftp_buf, "226", 3) != 0) {
+        return false;
+    }
+    return true;
+}
+
+bool FTPClient::del(const char* file_name) {
+    if (!_login) {
+        return false;
+    }
+
+    sprintf(p_ftp_buf, "dele %s\r\n", file_name);
+    FTPClientControlSock.send(p_ftp_buf, strlen(p_ftp_buf));
+    if (FTPClientControlSock.recv(p_ftp_buf, FTP_BUF_SIZE) <= 0) {
+        return false;
+    }
+    if (strncmp(p_ftp_buf, "250", 3) != 0) {
+        return false;
+    }
+
+    return true;
+}
+
+bool FTPClient::mkdir(const char* dir_name) {
+    if (!_login) {
+        return false;
+    }
+
+    sprintf(p_ftp_buf, "xmkd %s\r\n", dir_name);
+    FTPClientControlSock.send(p_ftp_buf, strlen(p_ftp_buf));
+    if (FTPClientControlSock.recv(p_ftp_buf, FTP_BUF_SIZE) <= 0) {
+        return false;
+    }
+    if (strncmp(p_ftp_buf, "257", 3) != 0) {
+        return false;
+    }
+
+    return true;
+}
+
+bool FTPClient::cd(const char* dir_name) {
+    if (!_login) {
+        return false;
+    }
+
+    sprintf(p_ftp_buf, "cwd %s\r\n", dir_name);
+    FTPClientControlSock.send(p_ftp_buf, strlen(p_ftp_buf));
+    if (FTPClientControlSock.recv(p_ftp_buf, FTP_BUF_SIZE) <= 0) {
+        return false;
+    }
+    if (strncmp(p_ftp_buf, "250", 3) != 0) {
+        return false;
+    }
+
+    return true;
+}
+
+bool FTPClient::dir(char* list_buf, int buf_size) {
+    int size;
+    int idx = 0;
+    int remain_size = buf_size - 1;
+
+    if (list_buf == NULL) {
+        return false;
+    }
+    list_buf[0] = '\0';
+
+    if (!_login) {
+        return false;
+    }
+
+    if (!open_data_sock()) {
+        return false;
+    }
+
+    FTPClientDataSock.set_timeout(500);
+    sprintf(p_ftp_buf, "list\r\n");
+    FTPClientControlSock.send(p_ftp_buf, strlen(p_ftp_buf));
+    if (FTPClientControlSock.recv(p_ftp_buf, FTP_BUF_SIZE) <= 0) {
+        return false;
+    }
+    if ((strncmp(p_ftp_buf, "150", 3) != 0) && (strncmp(p_ftp_buf, "125", 3) != 0)) {
+        return false;
+    }
+
+    while (remain_size > 0) {
+        size = FTPClientDataSock.recv(p_ftp_buf, FTP_BUF_SIZE);
+        if (size > 0) {
+            if (size > remain_size) {
+                size = remain_size;
+            }
+            memcpy(&list_buf[idx], p_ftp_buf, size);
+            idx += size;
+            remain_size -= size;
+        } else {
+            break;
+        }
+    }
+    list_buf[idx] = '\0';
+    FTPClientDataSock.close();
+
+    if (FTPClientControlSock.recv(p_ftp_buf, FTP_BUF_SIZE) <= 0) {
+        return false;
+    }
+    if (strncmp(p_ftp_buf, "226", 3) != 0) {
+        return false;
+    }
+    return true;
+}
+
+bool FTPClient::ls(char* list_buf, int buf_size) {
+    int size;
+    int idx = 0;
+    int remain_size = buf_size - 1;
+
+    if (list_buf == NULL) {
+        return false;
+    }
+    list_buf[0] = '\0';
+
+    if (!_login) {
+        return false;
+    }
+
+    if (!open_data_sock()) {
+        return false;
+    }
+
+    FTPClientDataSock.set_timeout(500);
+    sprintf(p_ftp_buf, "nlst\r\n");
+    FTPClientControlSock.send(p_ftp_buf, strlen(p_ftp_buf));
+    if (FTPClientControlSock.recv(p_ftp_buf, FTP_BUF_SIZE) <= 0) {
+        return false;
+    }
+    if ((strncmp(p_ftp_buf, "150", 3) != 0) && (strncmp(p_ftp_buf, "125", 3) != 0)) {
+        return false;
+    }
+
+    while (remain_size > 0) {
+        size = FTPClientDataSock.recv(p_ftp_buf, FTP_BUF_SIZE);
+        if (size > 0) {
+            if (size > remain_size) {
+                size = remain_size;
+            }
+            memcpy(&list_buf[idx], p_ftp_buf, size);
+            idx += size;
+            remain_size -= size;
+        } else {
+            break;
+        }
+    }
+    list_buf[idx] = '\0';
+    FTPClientDataSock.close();
+
+    if (FTPClientControlSock.recv(p_ftp_buf, FTP_BUF_SIZE) <= 0) {
+        return false;
+    }
+    if (strncmp(p_ftp_buf, "226", 3) != 0) {
+        return false;
+    }
+    return true;
+}
+
+bool FTPClient::open_data_sock() {
+    uint8_t ip_addr[4];
+    int i;
+    int remote_port = 0;
+    char* token = NULL;
+    char* savept = NULL;
+
+    sprintf(p_ftp_buf, "pasv\r\n");
+    FTPClientControlSock.send(p_ftp_buf, strlen(p_ftp_buf));
+    if (FTPClientControlSock.recv(p_ftp_buf, FTP_BUF_SIZE) <= 0) {
+        return false;
+    }
+    if (strncmp(p_ftp_buf, "227", 3) != 0) {
+        return false;
+    }
+
+    token = strchr(p_ftp_buf, '(') + 1;
+    for (i = 0; i < 4; i++) {
+        token = strtok_r(token, ",", &savept);
+        ip_addr[i] = (uint8_t)atoi(token);
+        token = savept;
+        if (token == NULL) {
+            return false;
+        }
+    }
+
+    for (i = 0; i < 2; i++) {
+        token = strtok_r(token, ",)", &savept);
+        remote_port <<= 8;
+        remote_port += atoi(token);
+        token = savept;
+        if (token == NULL) {
+            return false;
+        }
+    }
+    sprintf(ftp_data_ip_addr, "%d.%d.%d.%d", ip_addr[0], ip_addr[1], ip_addr[2], ip_addr[3]);
+
+    FTPClientDataSock.open(p_network);
+    for (i = 0; i < 20; i++) {
+        if (FTPClientDataSock.connect(ftp_data_ip_addr, remote_port) >= 0) {
+            return true;
+        }
+        wait(1);
+    }
+    return false;
+}
+