这一次我自己在Linux下写了一个简单的FTP客户端,支持的操作只有那些最常用的。这个程序已经通过简单的一些测试,目前实现的操作有:ls, dir, pwd, cd, ascii, binary, passive, get, put, delete, system, mkdir, rmdir, quit, bye. 另外,我用的了steven那本《UNIX网络编程卷1》的动态库,因为里面包含了一些API的封装,我无须理会这些API的返回码,所以程序需要包含unp.h这个头文件和链接libunp.so这个动态库。

  这个FTP客户端程序主要分两个模块,一个是ftp_socket.c,负责socket方面的操作,另外一个是ftp.c,负责FTP的操作实现。有参考了网上开源的项目中PORT和PASV部分的处理,其他其实都挺简单的。核心代码不到900行,其中有一些地方没考虑得很全面,一些处理得不够优雅,以后慢慢再修改,在git上已经给程序打了一个tag。直接贴代码了:

typedef.h : 声明和定义了一些类型和操作

#ifndef TYPEDEF_H
#define TYPEEF_H

typedef enum _Ret
{
    FTP_RET_OK,
    FTP_RET_OOM,
    FTP_RET_STOP,
    FTP_RET_INVALID_PARAMS,
    FTP_RET_FAIL
} FTP_Ret;

#define DECLS_BEGIN
#define DECLS_END

#define and &&
#define or  ||

#define return_if_fail(p)    if (!(p))        \
    {printf("%s:%d Warning: "#p" failed.\n",\
            __func__, __LINE__); return; }
#define return_val_if_fail(p, ret)    if (!(p))    \
    {printf("%s:%d Warning: "#p" failed.\n",    \
            __func__, __LINE__); return (ret); }

#define SAFE_FREE(p)    if (p != NULL) {free(p); p = NULL;}

#endif

ftp_socket.h

#include "typedef.h"

#ifndef FTP_SOCKET_H
#define FTP_SOCKET_H

DECLS_BEGIN

#define INVALID_SOCKET  (~0)

#define FD_READ_BIT      0
#define FD_READ          (1 << FD_READ_BIT)
#define FD_WRITE_BIT     1
#define FD_WRITE         (1 << FD_WRITE_BIT)
#define FD_OOB_BIT       2
#define FD_OOB           (1 << FD_OOB_BIT)
#define FD_ACCEPT_BIT    3
#define FD_ACCEPT        (1 << FD_ACCEPT_BIT)
#define FD_CONNECT_BIT   4
#define FD_CONNECT       (1 << FD_CONNECT_BIT)
#define FD_CLOSE_BIT     5
#define FD_CLOSE         (1 << FD_CLOSE_BIT)

typedef int SOCKET_HANDLE;

typedef struct _FTP_Info
{
    char servIP[20];
    int  servPort;
    char userName[20];
    char userPassword[20];
} FTP_Info;

int ftp_socket_connect(SOCKET_HANDLE socketHandle, const char *ipAddress, const int port);
SOCKET_HANDLE ftp_socket_create(void);
int ftp_socket_select(SOCKET_HANDLE socketHandle, int event, int secTime);
FTP_Ret ftp_socket_close(SOCKET_HANDLE socketHandle);
FTP_Ret ftp_socket_listen(SOCKET_HANDLE socketHandle, int maxListen);
int ftp_socket_accept(SOCKET_HANDLE socketHandle);
FTP_Ret 
ftp_socket_bind_and_listen(SOCKET_HANDLE socketHandle, const char *ipAddress, const int port);

DECLS_END

#endif

ftp_socket.c: socket底层的处理

ftp_socket.c

#include "unp.h"
#include "ftp_socket.h"

int ftp_socket_connect(SOCKET_HANDLE socketHandle, const char *ipAddress, const int port)
{
    return_val_if_fail(socketHandle != INVALID_SOCKET, -1);
    return_val_if_fail(ipAddress != NULL, -1);

    struct sockaddr_in servAddr;

    bzero(&servAddr, sizeof(servAddr));
    servAddr.sin_family = AF_INET;
    servAddr.sin_port = htons(port);
    Inet_pton(AF_INET, ipAddress, &servAddr.sin_addr);

    return connect(socketHandle, (SA *)&servAddr, sizeof(servAddr));
}

FTP_Ret 
ftp_socket_bind_and_listen(SOCKET_HANDLE socketHandle, const char *ipAddress, const int port)
{
    return_val_if_fail(socketHandle != INVALID_SOCKET, FTP_RET_INVALID_PARAMS);
    return_val_if_fail(ipAddress != NULL, FTP_RET_INVALID_PARAMS);

    struct sockaddr_in servAddr;

    bzero(&servAddr, sizeof(servAddr));
    servAddr.sin_family = AF_INET;
    servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servAddr.sin_port = htons(port);

    Bind(socketHandle, (SA *)&servAddr, sizeof(servAddr));
    Listen(socketHandle, LISTENQ);

    return FTP_RET_OK;
}

FTP_Ret ftp_socket_listen(SOCKET_HANDLE socketHandle, int maxListen)
{
    return_val_if_fail(socketHandle != INVALID_SOCKET, FTP_RET_INVALID_PARAMS);

    Listen(socketHandle, maxListen);
    return FTP_RET_OK;
}

int ftp_socket_accept(SOCKET_HANDLE socketHandle)
{
    return_val_if_fail(socketHandle != INVALID_SOCKET, FTP_RET_INVALID_PARAMS);

    return accept(socketHandle, NULL, NULL);
}

SOCKET_HANDLE ftp_socket_create(void)
{
    return Socket(AF_INET, SOCK_STREAM, 0);
}

int ftp_socket_select(SOCKET_HANDLE socketHandle, int event, int secTime)
{
    return_val_if_fail(socketHandle != INVALID_SOCKET, FTP_RET_INVALID_PARAMS);

    struct timeval timeValue;
    fd_set readSet, writeSet;

    FD_ZERO(&readSet);
    if ( (event & FD_ACCEPT) or (event & FD_READ) or (event & FD_CLOSE) )
    {
        FD_SET(socketHandle, &readSet);
    }

    FD_ZERO(&writeSet);
    if ( (event & FD_CONNECT) or (event & FD_WRITE) )
    {
        FD_SET(socketHandle, &writeSet);
    }

    timeValue.tv_sec = secTime;
    timeValue.tv_usec = secTime * 1000;

    return select(socketHandle + 1, &readSet, &writeSet, NULL, &timeValue);
}

FTP_Ret ftp_socket_close(SOCKET_HANDLE socketHandle)
{
    return_val_if_fail(socketHandle != INVALID_SOCKET, FTP_RET_INVALID_PARAMS);

    close(socketHandle);
    socketHandle = INVALID_SOCKET;
    return FTP_RET_OK;
}

ftp.h: 只给出一个FTP进入点的接口,其他FTP操作都封装在ftp.c里面

ftp.h

#include "ftp_socket.h"

#ifndef FTP_H
#define FTP_H

DECLS_BEGIN

#define FTP_REPLY_SIZE 512 

typedef enum _mode
{
    FTP_MODE_PASV,
    FTP_MODE_PORT
} FTP_trans_mode;

typedef struct _FTP_obj
{
    SOCKET_HANDLE commandChannel;
    SOCKET_HANDLE dataChannel;
    unsigned int secTimeOut;

    int replyCode;
    char replyString[FTP_REPLY_SIZE];

    FTP_trans_mode transMode;
} FTP_Obj;

typedef FTP_Ret (*FtpCommandFunc)(FTP_Obj *ftpObj, const char *command);

FTP_Ret FTP_entry(const char *ipAddress, const int port);

DECLS_END

#endif

ftp.c : FTP核心代码,包含FTP的操作实现,这部分相对比较长:

ftp.c

#include <curses.h>
#include "ftp.h"
#include "unp.h"

#define FTP_PROMPT        "ftp> "
#define COMMAND_SIZE    256
#define MAX_PROMPT_SIZE 128
#define MAX_RECV_SIZE    1024 << 1

static bool ftp_set_nonblock(SOCKET_HANDLE socketHandle)
{
    int flag = fcntl(socketHandle, F_GETFL);
    flag |= O_NONBLOCK;
    if (fcntl(socketHandle, F_SETFL, flag) == -1)
    {
        return false;
    }
    return true;
}

static int ftp_send_buffer(SOCKET_HANDLE socketHandle, const char *buffer, size_t size)
{
    return write(socketHandle, buffer, size);
}

static FTP_Ret ftp_send_command(SOCKET_HANDLE socketHandle, const char *command)
{
    return_val_if_fail(socketHandle != INVALID_SOCKET, FTP_RET_INVALID_PARAMS);
    return_val_if_fail(command != NULL, FTP_RET_INVALID_PARAMS);

    char sendCommand[COMMAND_SIZE] = {0x00};

    sprintf(sendCommand, "%s\r\n", command);
    Writen(socketHandle, sendCommand, strlen(sendCommand));

    return FTP_RET_OK;
}

static long 
ftp_recv_command(SOCKET_HANDLE socketHandle, char *recvBuf, unsigned long recvMaxSize)
{
    return_val_if_fail(socketHandle != INVALID_SOCKET, -1);
    return_val_if_fail(recvBuf != NULL, -1);

    long recvSize = 0;
    long totalSize = 0;
    do
    {
        recvSize = recv(socketHandle, recvBuf + totalSize, recvMaxSize - totalSize, 0);
        totalSize += recvSize;
    } while (recvSize > 0);

    return totalSize;
}

static FTP_Ret ftp_init_obj(FTP_Obj *ftpObj)
{
    return_val_if_fail(ftpObj != NULL, FTP_RET_INVALID_PARAMS);

    ftpObj->commandChannel = INVALID_SOCKET;
    ftpObj->dataChannel       = INVALID_SOCKET;
    ftpObj->secTimeOut       = 60;
    ftpObj->replyCode       = -1;
    ftpObj->transMode      = FTP_MODE_PASV;
    memset(ftpObj->replyString, 0x00, sizeof(ftpObj->replyString));

    return FTP_RET_OK;
}

static FTP_Ret ftp_get_reply(FTP_Obj *ftpObj)
{
    return_val_if_fail(ftpObj != NULL, FTP_RET_INVALID_PARAMS);
    return_val_if_fail(ftpObj->commandChannel != INVALID_SOCKET, FTP_RET_FAIL);

    char recvBuf[MAX_RECV_SIZE] = {0x00};

    ftpObj->replyCode = -1;
    memset(ftpObj->replyString, 0x00, sizeof(ftpObj->replyString));

    while (ftp_socket_select(ftpObj->commandChannel, FD_READ, ftpObj->secTimeOut) > 0)
    {
        ftp_set_nonblock(ftpObj->commandChannel);
        if (ftp_recv_command(ftpObj->commandChannel, recvBuf, MAX_RECV_SIZE) <= 0)
        {
            fprintf(stderr, "Recv reply message failed.\n");
            return FTP_RET_FAIL;
        }
        else
        {
            printf("%s", recvBuf);
            strncpy(ftpObj->replyString, recvBuf, strlen(recvBuf));
            return FTP_RET_OK;
        }
    }

    return FTP_RET_FAIL;
}

static FTP_Ret ftp_send_and_recv_reply(FTP_Obj *ftpObj, const char *command)
{
    return_val_if_fail(ftpObj != NULL, FTP_RET_INVALID_PARAMS);
    return_val_if_fail(command != NULL, FTP_RET_INVALID_PARAMS);

    ftp_send_command(ftpObj->commandChannel, command);
    memset(ftpObj->replyString, 0x00, sizeof(ftpObj->replyString));
    return ftp_get_reply(ftpObj);
}

static FTP_Obj *ftp_connect_server(const char *ipAddress, const int port)
{
    return_val_if_fail(ipAddress != NULL, NULL);

    FTP_Obj *ftpObj = (FTP_Obj *)malloc(sizeof(FTP_Obj));
    return_val_if_fail(ftpObj != NULL, NULL);

    ftp_init_obj(ftpObj);

    ftpObj->commandChannel = ftp_socket_create();
    if (ftpObj->commandChannel == INVALID_SOCKET)
    {
        fprintf(stderr, "creating socket failed.\n");
        goto error;
    }
    else
    {
        int ret = ftp_socket_connect(ftpObj->commandChannel, ipAddress, port);
        if (ret != 0)
        {
            fprintf(stderr, "connect to %s failed.\n", ipAddress);
            goto error;
        }
        else
        {
            fprintf(stdout, "connected to %s.\n", ipAddress);
            if (ftp_get_reply(ftpObj) == FTP_RET_OK)
            {
                return ftpObj;
            }
            else
            {
                goto error;
            }
        }
    }

error:
    SAFE_FREE(ftpObj);
    return NULL;
}

static void ftp_login_enter_name(char *userName, const int maxSize, const char *ipAddress)
{
    char enterNamePrompt[MAX_PROMPT_SIZE] = {0x00};
    sprintf(enterNamePrompt, "Name (%s): ", ipAddress);

    printf("%s", enterNamePrompt);
    Fgets(userName, maxSize, stdin);
    userName[strlen(userName) - 1] = '\0';
}

static void ftp_login_enter_password(char *userPassword, const int maxSize)
{
    char enterPwPrompt[MAX_PROMPT_SIZE] = {0x00};
    sprintf(enterPwPrompt, "Password: ");

    printf("%s", enterPwPrompt);
    Fgets(userPassword, maxSize, stdin);
    userPassword[strlen(userPassword) - 1] = '\0';
}

static FTP_Ret ftp_login(FTP_Obj *ftpObj, const char *ipAddress)
{
    return_val_if_fail(ftpObj != NULL, FTP_RET_INVALID_PARAMS);
    return_val_if_fail(ipAddress != NULL, FTP_RET_INVALID_PARAMS);
    
    char command[COMMAND_SIZE];
    char userName[20] = {0x00};
    char userPassword[20] = {0x00};

    ftp_login_enter_name(userName, 20, ipAddress);
    memset(command, 0x00, sizeof(command));
    sprintf(command, "USER %s", userName);
    if (ftp_send_and_recv_reply(ftpObj, command) == FTP_RET_OK)
    {
        if (strncmp(ftpObj->replyString, "331", 3) != 0)
        {
            return FTP_RET_FAIL;
        }

        ftp_login_enter_password(userPassword, 20);
        memset(command, 0x00, sizeof(command));
        sprintf(command, "PASS %s", userPassword);
        if (ftp_send_and_recv_reply(ftpObj, command) == FTP_RET_OK)
        {
            if (strncmp(ftpObj->replyString, "230", 3) != 0)
            {
                return FTP_RET_FAIL;
            }
        }
    }
    else
    {
        return FTP_RET_FAIL;
    }
    return FTP_RET_OK;
}

static FTP_Ret ftp_syst(FTP_Obj *ftpObj, const char *command)
{
    return_val_if_fail(ftpObj != NULL, FTP_RET_INVALID_PARAMS);
    return ftp_send_and_recv_reply(ftpObj, "SYST");
}

static FTP_Ret ftp_set_transfer_type(FTP_Obj *ftpObj, const char *command)
{
    return_val_if_fail(ftpObj != NULL, FTP_RET_INVALID_PARAMS);
    return ftp_send_and_recv_reply(ftpObj, command);
}

static FTP_Ret ftp_set_transfer_mode(FTP_Obj *ftpObj, const char *command)
{
    return_val_if_fail(ftpObj != NULL, FTP_RET_INVALID_PARAMS);
    return ftp_send_and_recv_reply(ftpObj, command);
}

static bool ftp_pasv(FTP_Obj *ftpObj)
{
    return_val_if_fail(ftpObj != NULL, false);

    char pasv[32] = {0x00};
    char hostAddr[16] = {0x00};
    unsigned int addr_1, addr_2, addr_3, addr_4, port_1, port_2;

    ftpObj->dataChannel = ftp_socket_create();
    if (ftp_send_and_recv_reply(ftpObj, "PASV") != FTP_RET_OK)
    {
        return false;
    }
    strcpy(pasv, strchr(ftpObj->replyString, '(') );
    sscanf(pasv, "(%d, %d, %d, %d, %d, %d)", 
            &addr_1, &addr_2, &addr_3, &addr_4, &port_1, &port_2);
    sprintf(hostAddr, "%d.%d.%d.%d", addr_1, addr_2, addr_3, addr_4);
    if (ftp_socket_connect(ftpObj->dataChannel, hostAddr, (port_1 << 8) | port_2) != 0)
    {
        return false;
    }

    return true;
}

#define UC(arg)    ( ( (int)arg) & 0xff)
static bool ftp_port(FTP_Obj *ftpObj)
{
    return_val_if_fail(ftpObj != NULL, false);

    char *addr, *port;
    char command[256] = {0x00};
    struct sockaddr_in dataAddr;
    size_t len = sizeof(dataAddr);

    if (getsockname(ftpObj->commandChannel, (struct sockaddr *)&dataAddr, &len) != 0)
    {
        return false;
    }
    ftpObj->dataChannel = ftp_socket_create();
    if (ftpObj->dataChannel == INVALID_SOCKET)
    {
        return false;
    }
    if (bind(ftpObj->dataChannel, (struct sockaddr *)&dataAddr, len) != 0)
    {
        return false;
    }
    if (getsockname(ftpObj->dataChannel, (struct sockaddr *)&dataAddr, &len) != 0)
    {
        return false;
    }
    if (ftp_socket_listen(ftpObj->dataChannel, 1) != 0)
    {
        return false;
    }
    addr = (char *)&dataAddr.sin_addr;
    port = (char *)&dataAddr.sin_port;
    sprintf(command, "PORT %d, %d, %d, %d, %d, %d",
            UC(addr[0]), UC(addr[1]), UC(addr[2]), UC(addr[3]),
            UC(port[0]), UC(port[1]) );
    if (ftp_send_and_recv_reply(ftpObj, command) != FTP_RET_OK)
    {
        return false;
    }

    return true;
}

static bool ftp_init_data_channel(FTP_Obj *ftpObj)
{
    return_val_if_fail(ftpObj != NULL, false);

    if (ftpObj->dataChannel != INVALID_SOCKET)
    {
        ftp_socket_close(ftpObj->dataChannel);
    }
    if (ftpObj->transMode == FTP_MODE_PASV)
    {
        return ftp_pasv(ftpObj);
    }
    else
    {
        return ftp_port(ftpObj);
    }
    return false;
}

static bool ftp_build_data_channel(FTP_Obj *ftpObj)
{
    return_val_if_fail(ftpObj != NULL, false);

    if (ftpObj->transMode == FTP_MODE_PASV)
    {
        return true;
    }
    if (ftp_socket_select(ftpObj->dataChannel, FD_ACCEPT, ftpObj->secTimeOut) <= 0)
    {
        return false;
    }
    SOCKET_HANDLE socketHandle = ftp_socket_accept(ftpObj->dataChannel);
    if (socketHandle == INVALID_SOCKET)
    {
        return false;
    }
    else
    {
        ftp_socket_close(ftpObj->dataChannel);
        ftpObj->dataChannel = socketHandle;
        ftp_set_nonblock(ftpObj->dataChannel);    // set nonblock
        return true;
    }
    return false;
}

#define MAX_RECV_DATA_SIZE 1024 * 100
static char* ftp_recv_data_channnel(FTP_Obj *ftpObj, const char *command)
{
    if (ftp_init_data_channel(ftpObj) != true)
    {
        ftp_socket_close(ftpObj->dataChannel);
        return NULL;
    }
    if (ftp_send_and_recv_reply(ftpObj, command) != FTP_RET_OK)
    {
        return NULL;
    }
    if (ftp_build_data_channel(ftpObj) != true)
    {
        return NULL;
    }

    char *recvBuf = (char *)malloc(sizeof(char) * MAX_RECV_DATA_SIZE);
    if (recvBuf == NULL)
    {
        return NULL;
    }
    memset(recvBuf, 0x00, MAX_RECV_DATA_SIZE);
    if (ftp_recv_command(ftpObj->dataChannel, recvBuf, MAX_RECV_DATA_SIZE) < 0)
    {
        return NULL;
    }
    return recvBuf;
}

static FTP_Ret ftp_list(FTP_Obj *ftpObj, const char *command)
{
    return_val_if_fail(ftpObj != NULL, FTP_RET_INVALID_PARAMS);
    return_val_if_fail(command != NULL, FTP_RET_INVALID_PARAMS);

    char *recvBuf = ftp_recv_data_channnel(ftpObj, command);
    if (recvBuf != NULL)
    {
        printf("%s", recvBuf);
        SAFE_FREE(recvBuf);
        ftp_socket_close(ftpObj->dataChannel);

        if (ftp_get_reply(ftpObj) == FTP_RET_OK)
        {
            return FTP_RET_OK;
        }
    }
    return FTP_RET_FAIL;
}

static long ftp_get_file_size(const char *replyString)
{
    return_val_if_fail(replyString != NULL, -1);

    char sizeBuf[10] = {0x00};
    char *ptr = strchr(replyString, '(');

    int index;
    for (index = 0, ptr += 1; index < 10; ++index, ++ptr)
    {
        if (*ptr != ' ')
        {
            sizeBuf[index] = *ptr;
        }
        else
        {
            break;
        }
    }
    return atol(sizeBuf);
}

static bool ftp_get_file_name(const char *replyString, char *fileName)
{
    return_val_if_fail(replyString != NULL, -1);

    char *ptr = strstr(replyString, "for");
    if (ptr != NULL)
    {
        int index;

        // (ptr += 4) : ignore the "for " substring
        for (index = 0, ptr += 4; *ptr != '\0'; ++ptr, ++index)
        {
            if (*ptr != ' ')
            {
                fileName[index] = *ptr;
            }
            else
            {
                return true;
            }
        }
    }
    return false;
}

static FTP_Ret ftp_get_file(FTP_Obj *ftpObj, const char *command)
{
    return_val_if_fail(ftpObj != NULL, FTP_RET_INVALID_PARAMS);
    return_val_if_fail(command != NULL, FTP_RET_INVALID_PARAMS);
    
    char *recvBuf = ftp_recv_data_channnel(ftpObj, command);
    if (recvBuf != NULL)
    {
        char fileName[256] = {0x00};
        long fileSize = ftp_get_file_size(ftpObj->replyString);
        if (ftp_get_file_name(ftpObj->replyString, fileName) == true)
        {
            int fd = open(fileName, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IRGRP);
            if (fileSize != 0)
            {
                long writeSize = write(fd, recvBuf, fileSize);
                if (writeSize != fileSize)
                {
                    return FTP_RET_FAIL;
                }
            }
            close(fd);
        }

        SAFE_FREE(recvBuf);
        ftp_socket_close(ftpObj->dataChannel);

        if (ftp_get_reply(ftpObj) == FTP_RET_OK)
        {
            return FTP_RET_OK;
        }
    }
    return FTP_RET_FAIL;
}

static bool ftp_put_file_name(const char *command, char *fileName)
{
    char *ptr = strchr(command, ' ');
    if (ptr != NULL)
    {
        int index = 0;
        for (ptr += 1; *ptr != '\0'; ++index, ++ptr)
        {
            fileName[index] = *ptr;
        }
        return true;
    }
    return false;
}

static long ftp_put_file_size(const char *fileName)
{
    struct stat buf;

    if (stat(fileName, &buf) == -1)
    {
        return -1;
    }
    return buf.st_size;
}

static FTP_Ret ftp_put_file(FTP_Obj *ftpObj, const char *command)
{
    return_val_if_fail(ftpObj != NULL, FTP_RET_INVALID_PARAMS);
    return_val_if_fail(command != NULL, FTP_RET_INVALID_PARAMS);

    if (ftpObj->commandChannel == INVALID_SOCKET)
    {
        return FTP_RET_FAIL;
    }
    if (ftp_init_data_channel(ftpObj) != true)
    {
        ftp_socket_close(ftpObj->dataChannel);
        return FTP_RET_FAIL;
    }
    if (ftp_send_and_recv_reply(ftpObj, command) != FTP_RET_OK)
    {
        return FTP_RET_FAIL;
    }
    if (ftp_build_data_channel(ftpObj) != true)
    {
        return FTP_RET_FAIL;
    }

    char fileName[256] = {0x00};
    if (ftp_put_file_name(command, fileName) == true)
    {
        long size = ftp_put_file_size(fileName);
        printf("----->put: %s: %ld\n", fileName, size);
        int fd = open(fileName, O_RDONLY);
        if (fd != -1)
        {
            char *recvBuf = (char *)malloc(sizeof(char) * MAX_RECV_DATA_SIZE);
            if (recvBuf != NULL)
            {
                ssize_t readSize = read(fd, recvBuf, size);
                if (readSize <= 0)
                {
                    return FTP_RET_FAIL;
                }
                if (ftp_send_buffer(ftpObj->dataChannel, recvBuf, readSize) != -1)
                {
                    close(fd);
                    ftp_socket_close(ftpObj->dataChannel);
                }
                return ftp_get_reply(ftpObj);
            }
        }
    }
    return FTP_RET_FAIL;
}

static FTP_Ret ftp_delete_file(FTP_Obj *ftpObj, const char *command)
{
    return_val_if_fail(ftpObj != NULL, FTP_RET_INVALID_PARAMS);
    return_val_if_fail(command != NULL, FTP_RET_INVALID_PARAMS);
    
    return ftp_send_and_recv_reply(ftpObj, command);
}

static FTP_Ret ftp_make_directory(FTP_Obj *ftpObj, const char *command)
{
    return_val_if_fail(ftpObj != NULL, FTP_RET_INVALID_PARAMS);
    return_val_if_fail(command != NULL, FTP_RET_INVALID_PARAMS);
    
    return ftp_send_and_recv_reply(ftpObj, command);
}

static FTP_Ret ftp_remove_directory(FTP_Obj *ftpObj, const char *command)
{
    return_val_if_fail(ftpObj != NULL, FTP_RET_INVALID_PARAMS);
    return_val_if_fail(command != NULL, FTP_RET_INVALID_PARAMS);
    
    return ftp_send_and_recv_reply(ftpObj, command);
}

static FTP_Ret ftp_change_directory(FTP_Obj *ftpObj, const char *command)
{
    return_val_if_fail(ftpObj != NULL, FTP_RET_INVALID_PARAMS);
    return_val_if_fail(command != NULL, FTP_RET_INVALID_PARAMS);
    
    return ftp_send_and_recv_reply(ftpObj, command);
}

static FTP_Ret ftp_pwd(FTP_Obj *ftpObj, const char *command)
{
    return_val_if_fail(ftpObj != NULL, FTP_RET_INVALID_PARAMS);
    return_val_if_fail(command != NULL, FTP_RET_INVALID_PARAMS);
    
    return ftp_send_and_recv_reply(ftpObj, "PWD");
}

static FTP_Ret ftp_close(FTP_Obj *ftpObj, const char *command)
{
    return_val_if_fail(ftpObj != NULL, FTP_RET_INVALID_PARAMS);
    return_val_if_fail(command != NULL, FTP_RET_INVALID_PARAMS);

    if (ftpObj->commandChannel != INVALID_SOCKET)
    {
        FTP_Ret ret = ftp_send_and_recv_reply(ftpObj, command);
        if (ret == FTP_RET_OK)
        {
            ftp_socket_close(ftpObj->commandChannel);
        }
        else
        {
            return FTP_RET_FAIL;
        }
    }
    if (ftpObj->dataChannel != INVALID_SOCKET)
    {
        ftp_socket_close(ftpObj->dataChannel);
    }
    SAFE_FREE(ftpObj);

    return FTP_RET_OK;
}

static bool ftp_command_filter(const char *command, char *filter)
{
    int index = 0;
    for ( ; *command != '\0'; ++index, ++command)
    {
        if (*command != ' ')
        {
            filter[index] = *command;
        }
        else
        {
            return true;
        }
    }
    return false;
}

static bool ftp_replay_command(char *sendCommand, const char *commandName)
{
    char temp[COMMAND_SIZE] = {0x00};
    char *ptr = strchr(sendCommand, ' ');
    if (ptr == NULL)
    {
        strcpy(sendCommand, commandName);
        return true;
    }

    strcpy(temp, commandName);
    strcat(temp, ptr);
    if (strlen(temp) <= COMMAND_SIZE)
    {
        strcpy(sendCommand, temp);
        return true;
    }
    return false;
}

typedef struct _ftp_do_command
{
    char command[COMMAND_SIZE];
    FtpCommandFunc ftp_do_command;
} ftp_command_tag;

const FtpCommandFunc ftp_command_func[] =
{
    ftp_list,
    ftp_list,
    ftp_pwd,
    ftp_change_directory,
    ftp_set_transfer_type,
    ftp_set_transfer_type,
    ftp_set_transfer_mode,
    ftp_get_file,
    ftp_put_file,
    ftp_delete_file,
    ftp_syst,
    ftp_make_directory,
    ftp_remove_directory,
    ftp_close,
    ftp_close,
};

const char *command_name[] = 
{
    "ls", "dir", "pwd", "cd", "ascii", "binary",
    "passive", "get", "put", "delete", "system",
    "mkdir", "rmdir", "quit", "bye",
};

const char *replace_command[] = 
{
    "LIST", "LIST", "PWD", "CWD", "TYPE A", "TYPE I",
    "passive", "RETR", "STOR", "DELE", "SYST",
    "MKD", "RMD", "QUIT", "QUIT",
};

static int ftp_return_command_index(const char *command)
{
    int index;
    int number = sizeof(command_name) / sizeof(char *);
    for (index = 0; index < number; ++index)
    {
        if (strcmp(command, command_name[index]) == 0)
        {
            return index;
        }
    }
    return -1;
}

FTP_Ret FTP_entry(const char *ipAddress, const int port)
{
    FTP_Obj *ftpObj = ftp_connect_server(ipAddress, port);
    if (ftpObj != NULL)
    {
        ftp_login(ftpObj, ipAddress);
        ftp_syst(ftpObj, NULL);
        ftp_set_transfer_mode(ftpObj, "TYPE I");
        while (1)
        {
            char enterCommand[256] = {0x00};

            Writen(1, FTP_PROMPT, sizeof(FTP_PROMPT));
            memset(enterCommand, 0x00, sizeof(enterCommand));
            Fgets(enterCommand, 256, stdin);
            enterCommand[strlen(enterCommand) - 1] = '\0';

            char filter[256] = {0x00};
            ftp_command_filter(enterCommand, filter);

            int index = ftp_return_command_index(filter);
            if (index >= 0)
            {
                ftp_replay_command(enterCommand, replace_command[index]);
                printf("-------->%s\n", enterCommand);
                ftp_command_func[index](ftpObj, enterCommand);
            }

            if (strcmp(enterCommand, "QUIT") == 0)
            {
                break;
            }
            else if (strcmp(enterCommand, "") == 0)
            {
                continue;
            }
        }
    }
    return FTP_RET_OK;
}

main.c:

main.c

#include "ftp.h"
#include "unp.h"

#define DEBUG    0
#define PORT    21

static void ftp_test(const char *ipAddress)
{
    FTP_entry(ipAddress, PORT);

    return;
}

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        fprintf(stderr, "Usage: ./ftp_client <IP>\n");
        return -1;
    }
    ftp_test(argv[1]);

    return 0;
}

Makefile:

Makefile

CC=gcc
CFILES=main.c ftp.c ftp_socket.c
OBJ+=$(CFILES:.c=.o) 
EXE=ftp_client

all:   $(EXE)
$(EXE)    :    $(OBJ) $(LIBFILES)
    $(CC) -Wall $(CFILES:.c=.o) -o $@ -lunp -lcurses

clean:
    rm -f  *.o *.gdb $(EXE)

.SUFFIXES: .cpp  .c .o

.c.o:
    $(CC) -Wall -o $*.o -c $(CCFLAGS) $*.c

END

原文地址:http://www.cnblogs.com/python_newbie/archive/2010/08/01/1789954.html

Logo

更多推荐