FTP client library for mbed-os

FTPClient.cpp

Committer:
dkato
Date:
2020-02-28
Revision:
2:88b7399c8260
Parent:
1:e069c405c934

File content as of revision 2:88b7399c8260:

/* 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)

//#define ftp_debug_print  printf

#ifndef ftp_debug_print
static int ftp_debug_print(const char *format, ...) {
    return 0;
}
#endif

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) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        return false;
    }
    _ctr_open = true;

    if (FTPClientControlSock.recv(p_ftp_buf, FTP_BUF_SIZE) <= 0) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        return false;
    }
    if (strncmp(p_ftp_buf, "220", 3) != 0) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        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) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        return false;
    }
    if (strncmp(p_ftp_buf, "331", 3) != 0) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        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) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        return false;
    }
    if (strncmp(p_ftp_buf, "230", 3) != 0) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        return false;
    }

    sprintf(p_ftp_buf, "type I\r\n");
    FTPClientControlSock.send(p_ftp_buf, strlen(p_ftp_buf));
    if (FTPClientControlSock.recv(p_ftp_buf, FTP_BUF_SIZE) <= 0) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        return false;
    }

    _login = true;
    return true;
}

bool FTPClient::quit() {
    if (!_login) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        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) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        return false;
    }
    if (strncmp(p_ftp_buf, "250", 3) != 0) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        return false;
    }
    _login = false;

    FTPClientControlSock.close();
    _ctr_open = false;
    return true;
}

bool FTPClient::get(const char* file_name) {
    FILE* fp;
    int size;

    if (!_login) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        return false;
    }

    if (!open_data_sock()) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        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) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        return false;
    }
    if ((strncmp(p_ftp_buf, "150", 3) != 0) && (strncmp(p_ftp_buf, "125", 3) != 0)) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        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) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        return false;
    }
    if (strncmp(p_ftp_buf, "226", 3) != 0) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        return false;
    }
    return true;
}

bool FTPClient::put(const char* file_name) {
    FILE* fp;
    int32_t remain_size;
    int32_t send_size;

    if (!_login) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        return false;
    }

    if (!open_data_sock()) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        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) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        return false;
    }
    if ((strncmp(p_ftp_buf, "150", 3) != 0) && (strncmp(p_ftp_buf, "125", 3) != 0)) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        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) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        return false;
    }
    if (strncmp(p_ftp_buf, "226", 3) != 0) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        return false;
    }
    return true;
}

bool FTPClient::del(const char* file_name) {
    if (!_login) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        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) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        return false;
    }
    if (strncmp(p_ftp_buf, "250", 3) != 0) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        return false;
    }

    return true;
}

bool FTPClient::mkdir(const char* dir_name) {
    if (!_login) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        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) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        return false;
    }
    if (strncmp(p_ftp_buf, "257", 3) != 0) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        return false;
    }

    return true;
}

bool FTPClient::cd(const char* dir_name) {
    if (!_login) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        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) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        return false;
    }
    if (strncmp(p_ftp_buf, "250", 3) != 0) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        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) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        return false;
    }
    list_buf[0] = '\0';

    if (!_login) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        return false;
    }

    if (!open_data_sock()) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        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) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        return false;
    }
    if ((strncmp(p_ftp_buf, "150", 3) != 0) && (strncmp(p_ftp_buf, "125", 3) != 0)) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        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) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        return false;
    }
    if (strncmp(p_ftp_buf, "226", 3) != 0) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        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) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        return false;
    }
    list_buf[0] = '\0';

    if (!_login) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        return false;
    }

    if (!open_data_sock()) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        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) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        return false;
    }
    if ((strncmp(p_ftp_buf, "150", 3) != 0) && (strncmp(p_ftp_buf, "125", 3) != 0)) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        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) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        return false;
    }
    if (strncmp(p_ftp_buf, "226", 3) != 0) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        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) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        return false;
    }
    if (strncmp(p_ftp_buf, "227", 3) != 0) {
        ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
        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) {
            ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
            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) {
            ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
            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);
    }
    ftp_debug_print("ERROR: %s(%d)\r\n", __FILE__, __LINE__);
    return false;
}