Browse Source

update errcode

ithewei 6 years ago
parent
commit
332618edc8
8 changed files with 561 additions and 102 deletions
  1. 1 0
      Makefile
  2. 4 8
      base/herr.c
  3. 93 68
      base/herr.h
  4. 6 5
      protocol/dns.c
  5. 249 0
      protocol/ftp.c
  6. 98 0
      protocol/ftp.h
  7. 22 21
      protocol/smtp.c
  8. 88 0
      unittest/ftp_test.c

+ 1 - 0
Makefile

@@ -76,6 +76,7 @@ unittest: prepare
 	$(CXX) -g -Wall -std=c++11 -I. -Ibase -Iutils    -o bin/ifconfig   unittest/ifconfig_test.cpp    utils/ifconfig.cpp
 	$(CC)  -g -Wall -std=c99   -I. -Ibase -Iprotocol -o bin/nslookup   unittest/nslookup_test.c      protocol/dns.c
 	$(CC)  -g -Wall -std=c99   -I. -Ibase -Iprotocol -o bin/ping       unittest/ping_test.c          protocol/icmp.c base/hsocket.c base/htime.c -DPRINT_DEBUG
+	$(CC)  -g -Wall -std=c99   -I. -Ibase -Iprotocol -o bin/ftp        unittest/ftp_test.c           protocol/ftp.c  base/hsocket.c
 	$(CC)  -g -Wall -std=c99   -I. -Ibase -Iutils -Iprotocol -o bin/sendmail  unittest/sendmail_test.c  protocol/smtp.c base/hsocket.c utils/base64.c
 
 # UNIX only

+ 4 - 8
base/herr.c

@@ -2,21 +2,17 @@
 
 #include <string.h> // for strerror
 
-#ifndef SYS_NERR
-#define SYS_NERR    133
-#endif
-
 // errcode => errmsg
 const char* get_errmsg(int err) {
-    if (err >= 0 && err <= SYS_NERR) {
+    if (err > 0 && err <= SYS_NERR) {
         return strerror(err);
     }
 
     switch (err) {
-#define CASE_ERR(macro, errcode, errmsg) \
+#define F(errcode, name, errmsg) \
     case errcode: return errmsg;
-    FOREACH_ERR(CASE_ERR)
-#undef CASE_ERR
+    FOREACH_ERR(F)
+#undef  F
     default:
         return "Undefined error";
     }

+ 93 - 68
base/herr.h

@@ -3,88 +3,113 @@
 
 #include <errno.h>
 
-// F(macro, errcode, errmsg)
-#define FOREACH_ERR_COMMON(F) \
-    F(ERR_OK,               0,      "ok")               \
-    F(ERR_UNKNOWN,          1000,   "unknown error")    \
-    F(ERR_NULL_PARAM,       1001,   "null param")       \
-    F(ERR_NULL_POINTER,     1002,   "null pointer")     \
-    F(ERR_NULL_DATA,        1003,   "null data")        \
-    \
-    F(ERR_INVALID_PARAM,    1010,   "invalid param")    \
-    F(ERR_INVALID_HANDLE,   1011,   "invalid handle")   \
-    F(ERR_INVALID_JSON,     1012,   "invalid json")     \
-    F(ERR_INVALID_XML,      1013,   "invalid xml")      \
-    F(ERR_INVALID_FMT,      1014,   "invalid format")   \
-    F(ERR_INVALID_PROTOCOL, 1015,   "invalid protocol") \
+#ifndef SYS_NERR
+#define SYS_NERR    133
+#endif
+
+// F(errcode, name, errmsg)
+// [1, 133]
+#define FOREACH_ERR_SYS(F)
+
+// [1xx~5xx]
+#define FOREACH_ERR_STATUS(F)
+
+// [1xxx]
+#define FOREACH_ERR_COMMON(F)   \
+    F(0,    OK,             "OK")               \
+    F(1000, UNKNOWN,        "Unknown error")    \
     \
-    F(ERR_MISMATCH,         1020,   "mismatch")         \
-    F(ERR_REQUEST,          1021,   "error request")    \
-    F(ERR_RESPONSE,         1022,   "error response")   \
-    F(ERR_PARSE,            1023,   "parse failed")     \
+    F(1001, NULL_PARAM,     "Null parameter")   \
+    F(1002, NULL_POINTER,   "Null pointer")     \
+    F(1003, NULL_DATA,      "Null data")        \
+    F(1004, NULL_HANDLE,    "Null handle")      \
     \
-    F(ERR_MALLOC,           1030,   "malloc failed")    \
-    F(ERR_FREE,             1031,   "free failed")      \
+    F(1011, INVALID_PARAM,      "Invalid parameter")\
+    F(1012, INVALID_POINTER,    "Invalid pointer")  \
+    F(1013, INVALID_DATA,       "Invalid data")     \
+    F(1014, INVALID_HANDLE,     "Invalid handle")   \
+    F(1015, INVALID_JSON,       "Invalid json")     \
+    F(1016, INVALID_XML,        "Invalid xml")      \
+    F(1017, INVALID_FMT,        "Invalid format")   \
+    F(1018, INVALID_PROTOCOL,   "Invalid protocol") \
+    F(1019, INVALID_PACKAGE,    "Invalid package")  \
     \
-    F(ERR_TASK_TIMEOUT,     1100,   "task timeout")     \
-    F(ERR_TASK_DEQUE_FULL,  1101,   "task deque full")  \
-    F(ERR_TASK_NOT_CREATE,  1102,   "task not create")  \
+    F(1021, OUT_OF_RANGE,   "Out of range")     \
+    F(1022, OVER_LIMIT,     "Over the limit")       \
+    F(1023, MISMATCH,       "Mismatch")         \
+    F(1024, PARSE,          "Parse failed")     \
     \
-    F(ERR_OPEN_FILE,        1200,   "open file failed") \
-    F(ERR_SAVE_FILE,        1201,   "save file failed") \
+    F(1030, OPEN_FILE,      "Open file failed") \
+    F(1031, SAVE_FILE,      "Save file failed") \
     \
-    F(ERR_OUT_OF_RANGE,     1300,   "out of range")     \
-    F(ERR_OVER_LIMIT,       1301,   "over limit")       \
+    F(1100, TASK_TIMEOUT,       "Task timeout")     \
+    F(1101, TASK_QUEUE_FULL,    "Task queue full")  \
+    F(1102, TASK_QUEUE_EMPTY,   "Task queue empty") \
     \
-    F(ERR_BUSY,             1400,   "busy")
+    F(1400, REQUEST,        "Bad request")      \
+    F(1401, RESPONSE,       "Bad response")     \
 
-#define FOREACH_ERR_NETWORK(F) \
-    F(ERR_ADAPTER_NOT_FOUND,    2001, "adapter not found")  \
-    F(ERR_SERVER_NOT_FOUND,     2002, "server not found")   \
-    F(ERR_SERVER_UNREACHEABLE,  2003, "server unreacheable")    \
-    F(ERR_SERVER_DISCONNECT,    2004, "server disconnect")      \
-    F(ERR_CONNECT_TIMEOUT,      2005, "connect timeout")        \
-    F(ERR_INVALID_PACKAGE,      2006, "invalid package")        \
-    F(ERR_SERVER_NOT_STARTUP,   2007, "server not startup")     \
-    F(ERR_CLIENT_DISCONNECT,    2008, "client disconnect")
+// [-1xxx]
+#define FOREACH_ERR_FUNC(F)   \
+    F(-1001,    MALLOC,     "malloc() error")   \
+    F(-1002,    REALLOC,    "realloc() error")  \
+    F(-1003,    CALLOC,     "calloc() error")   \
+    F(-1004,    FREE,       "free() error")     \
+    \
+    F(-1011,    SOCKET,     "socket() error")   \
+    F(-1012,    BIND,       "bind() error")     \
+    F(-1013,    LISTEN,     "listen() error")   \
+    F(-1014,    ACCEPT,     "accept() error")   \
+    F(-1015,    CONNECT,    "connect() error")  \
+    F(-1016,    RECV,       "recv() error")     \
+    F(-1017,    SEND,       "send() error")     \
+    F(-1018,    RECVFROM,   "recvfrom() error") \
+    F(-1019,    SENDTO,     "sendto() error")   \
+    F(-1020,    SETSOCKOPT, "setsockopt() error")   \
+    F(-1021,    GETSOCKOPT, "getsockopt() error")   \
 
+// [3xxx]
 #define FOREACH_ERR_SERVICE(F)  \
-    F(ERR_RESOURCE_NOT_FOUND,   3000, "resource not found") \
-    F(ERR_GROUP_NOT_FOUND,      3001, "group not found")    \
-    F(ERR_PERSON_NOT_FOUND,     3002, "person not found")   \
-    F(ERR_FACE_NOT_FOUND,       3003, "face not found")     \
-    F(ERR_DEVICE_NOT_FOUND,     3004, "device not found")   \
-    F(ERR_DEVICE_DISABLE,       3005, "device disable")
+    F(3000, RESOURCE_NOT_FOUND,     "resource not found")   \
+    F(3001, GROUP_NOT_FOUND,        "group not found")      \
+    F(3002, PERSON_NOT_FOUND,       "person not found")     \
+    F(3003, FACE_NOT_FOUND,         "face not found")       \
+    F(3004, DEVICE_NOT_FOUND,       "device not found")     \
+    \
+    F(3010, DEVICE_DISCONNECT,      "device disconnect")    \
+    F(3011, DEVICE_DISABLE,         "device disable")       \
+    F(3012, DEVICE_BUSY,            "device busy")          \
 
+// grpc [4000+]
 #define FOREACH_ERR_GRPC(F)     \
-    F(ERR_GRPC_FIRST,           4000, "grpc error") \
-    F(ERR_GRPC_STATUS_CANCELLED,4001, "grpc status cancelled")  \
-    F(ERR_GRPC_STATUS_UNKNOWN,  4002, "grpc status unknown")    \
-    F(ERR_GRPC_STATUS_INVALID_ARGUMENT,  4003, "grpc status invalid argument") \
-    F(ERR_GRPC_STATUS_DEADLINE, 4004, "grpc status deadline")   \
-    F(ERR_GRPC_STATUS_NOT_FOUND, 4005, "grpc status not found") \
-    F(ERR_GRPC_STATUS_ALREADY_EXISTS, 4006, "grpc status already exists")   \
-    F(ERR_GRPC_STATUS_PERMISSION_DENIED, 4007, "grpc status permission denied") \
-    F(ERR_GRPC_STATUS_RESOURCE_EXHAUSTED, 4008, "grpc status resource exhausted")    \
-    F(ERR_GRPC_STATUS_FAILED_PRECONDITION, 4009, "grpc status failed precondition") \
-    F(ERR_GRPC_STATUS_ABORTED, 4010, "grpc status aborted") \
-    F(ERR_GRPC_STATUS_OUT_OF_RANGE, 4011, "grpc status out of range")   \
-    F(ERR_GRPC_STATUS_UNIMPLEMENTED, 4012, "grpc status unimplemented") \
-    F(ERR_GRPC_STATUS_INTERNAL, 4013, "grpc status internal") \
-    F(ERR_GRPC_STATUS_UNAVAILABLE, 4014, "grpc service unavailable") \
-    F(ERR_GRPC_STATUS_DATA_LOSS, 4015, "grpc status data loss")
+    F(4000, GRPC_FIRST,                     "grpc no error")                \
+    F(4001, GRPC_STATUS_CANCELLED,          "grpc status: cancelled")       \
+    F(4002, GRPC_STATUS_UNKNOWN,            "grpc unknown error")           \
+    F(4003, GRPC_STATUS_INVALID_ARGUMENT,   "grpc status: invalid argument")\
+    F(4004, GRPC_STATUS_DEADLINE,           "grpc status: deadline")        \
+    F(4005, GRPC_STATUS_NOT_FOUND,          "grpc status: not found")       \
+    F(4006, GRPC_STATUS_ALREADY_EXISTS,     "grpc status: already exists")  \
+    F(4007, GRPC_STATUS_PERMISSION_DENIED,  "grpc status: permission denied")   \
+    F(4008, GRPC_STATUS_RESOURCE_EXHAUSTED, "grpc status: resource exhausted")  \
+    F(4009, GRPC_STATUS_FAILED_PRECONDITION,"grpc status: failed precondition") \
+    F(4010, GRPC_STATUS_ABORTED,            "grpc status: aborted")         \
+    F(4011, GRPC_STATUS_OUT_OF_RANGE,       "grpc status: out of range")    \
+    F(4012, GRPC_STATUS_UNIMPLEMENTED,      "grpc status: unimplemented")   \
+    F(4013, GRPC_STATUS_INTERNAL,           "grpc internal error")          \
+    F(4014, GRPC_STATUS_UNAVAILABLE,        "grpc service unavailable")     \
+    F(4015, GRPC_STATUS_DATA_LOSS,          "grpc status: data loss")       \
 
-#define FOREACH_ERR(F) \
-    FOREACH_ERR_COMMON(F) \
-    FOREACH_ERR_NETWORK(F)  \
+#define FOREACH_ERR(F)      \
+    FOREACH_ERR_COMMON(F)   \
+    FOREACH_ERR_FUNC(F)     \
     FOREACH_ERR_SERVICE(F)  \
-    FOREACH_ERR_GRPC(F)
+    FOREACH_ERR_GRPC(F)     \
 
-enum H_ERR {
-#define ENUM_ERR(macro, errcode, _) macro = errcode,
-    FOREACH_ERR(ENUM_ERR)
-#undef ENUM_ERR
-    ERR_LAST
+#undef ERR_OK // prevent conflict
+enum {
+#define F(errcode, name, errmsg) ERR_##name = errcode,
+    FOREACH_ERR(F)
+#undef  F
 };
 
 #ifdef __cplusplus

+ 6 - 5
protocol/dns.c

@@ -1,5 +1,6 @@
 #include "dns.h"
 #include "hsocket.h"
+#include "herr.h"
 
 void dns_free(dns_t* dns) {
     SAFE_FREE(dns->questions);
@@ -239,7 +240,7 @@ int dns_query(dns_t* query, dns_t* response, const char* nameserver) {
     int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
     if (sockfd < 0) {
         perror("socket");
-        return -10;
+        return ERR_SOCKET;
     }
     so_sndtimeo(sockfd, 5000);
     so_rcvtimeo(sockfd, 5000);
@@ -254,18 +255,18 @@ int dns_query(dns_t* query, dns_t* response, const char* nameserver) {
     addr.sin_port = htons(DNS_PORT);
     nsend = sendto(sockfd, buf, buflen, 0, (struct sockaddr*)&addr, addrlen);
     if (nsend != buflen) {
-        ret = -20;
+        ret = ERR_SENDTO;
         goto error;
     }
     nrecv = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&addr, &addrlen);
     if (nrecv <= 0) {
-        ret = -30;
+        ret = ERR_RECVFROM;
         goto error;
     }
 
     nparse = dns_unpack(buf, nrecv, response);
     if (nparse != nrecv) {
-        ret = -40;
+        ret = -ERR_INVALID_PACKAGE;
         goto error;
     }
 
@@ -304,7 +305,7 @@ int nslookup(const char* domain, uint32_t* addrs, int naddr, const char* nameser
     if (resp.hdr.transaction_id != query.hdr.transaction_id ||
         resp.hdr.qr != DNS_RESPONSE ||
         resp.hdr.rcode != 0) {
-        ret = -1;
+        ret = -ERR_MISMATCH;
         goto end;
     }
 

+ 249 - 0
protocol/ftp.c

@@ -0,0 +1,249 @@
+#include "ftp.h"
+#include "hsocket.h"
+#include "herr.h"
+
+const char* ftp_command_str(enum ftp_command cmd) {
+    switch (cmd) {
+#define X(name) case FTP_##name: return #name;
+    FTP_COMMAND_MAP(X)
+#undef  X
+    default: return "<unknown>";
+    }
+}
+
+const char* ftp_status_str(enum ftp_status status) {
+    switch (status) {
+#define XXX(code, name, string) case FTP_STATUS_##name: return #string;
+    FTP_STATUS_MAP(XXX)
+#undef  XXX
+    default: return "<unknown>";
+    }
+}
+
+int ftp_connect(ftp_handle_t* hftp, const char* host, int port) {
+    int sockfd = ConnectTimeout(host, port, DEFAULT_CONNECT_TIMEOUT);
+    if (sockfd < 0) {
+        return sockfd;
+    }
+    so_sndtimeo(sockfd, 5000);
+    so_rcvtimeo(sockfd, 5000);
+    hftp->sockfd = sockfd;
+    int ret = 0;
+    int status_code = 0;
+    memset(hftp->recvbuf, 0, FTP_RECV_BUFSIZE);
+    int nrecv = recv(sockfd, hftp->recvbuf, FTP_RECV_BUFSIZE, 0);
+    if (nrecv <= 0) {
+        ret = ERR_RECV;
+        goto error;
+    }
+    status_code = atoi(hftp->recvbuf);
+    if (status_code != FTP_STATUS_READY) {
+        ret = status_code;
+        goto error;
+    }
+    return 0;
+
+error:
+    closesocket(sockfd);
+    return ret;
+}
+
+int ftp_login(ftp_handle_t* hftp, const char* username, const char* password) {
+    int status_code = ftp_exec(hftp, "USER", username);
+    status_code = ftp_exec(hftp, "PASS", password);
+    return status_code == FTP_STATUS_LOGIN_OK ? 0 : status_code;
+}
+
+int ftp_quit(ftp_handle_t* hftp) {
+    ftp_exec(hftp, "QUIT", NULL);
+    closesocket(hftp->sockfd);
+    return 0;
+}
+
+int ftp_exec(ftp_handle_t* hftp, const char* cmd, const char* param) {
+    char buf[1024];
+    int len = 0;
+    if (param && *param) {
+        len = snprintf(buf, sizeof(buf), "%s %s\r\n", cmd, param);
+    }
+    else {
+        len = snprintf(buf, sizeof(buf), "%s\r\n", cmd);
+    }
+    int nsend, nrecv;
+    int ret = 0;
+    nsend = send(hftp->sockfd, buf, len, 0);
+    if (nsend != len) {
+        ret = ERR_SEND;
+        goto error;
+    }
+    //printf("> %s", buf);
+    memset(hftp->recvbuf, 0, FTP_RECV_BUFSIZE);
+    nrecv = recv(hftp->sockfd, hftp->recvbuf, FTP_RECV_BUFSIZE, 0);
+    if (nrecv <= 0) {
+        ret = ERR_RECV;
+        goto error;
+    }
+    //printf("< %s", hftp->recvbuf);
+    return atoi(hftp->recvbuf);
+error:
+    closesocket(hftp->sockfd);
+    return ret;
+}
+
+static int ftp_parse_pasv(const char* resp, char* host, int* port) {
+    // 227 Entering Passive Mode (127,0,0,1,4,51)
+    const char* str = strchr(resp, '(');
+    if (str == NULL) {
+        return ERR_RESPONSE;
+    }
+    int arr[6];
+    sscanf(str, "(%d,%d,%d,%d,%d,%d)",
+            &arr[0], &arr[1], &arr[2], &arr[3], &arr[4], &arr[5]);
+    sprintf(host, "%d.%d.%d.%d", arr[0], arr[1], arr[2], arr[3]);
+    *port = arr[4] << 8 | arr[5];
+    return 0;
+}
+
+int ftp_download_with_cb(ftp_handle_t* hftp, const char* filepath, ftp_download_cb cb) {
+    int status_code = ftp_exec(hftp, "PASV", NULL);
+    if (status_code != FTP_STATUS_PASV) {
+        return status_code;
+    }
+    char host[64];
+    int port = 0;
+    int ret = ftp_parse_pasv(hftp->recvbuf, host, &port);
+    if (ret != 0) {
+        return ret;
+    }
+    //ftp_exec(hftp, "RETR", filepath);
+    char request[1024];
+    int len = snprintf(request, sizeof(request), "RETR %s\r\n", filepath);
+    int nsend = send(hftp->sockfd, request, len, 0);
+    if (nsend != len) {
+        closesocket(hftp->sockfd);
+        return ERR_SEND;
+    }
+    //printf("> %s", request);
+    int sockfd = ConnectTimeout(host, port, DEFAULT_CONNECT_TIMEOUT);
+    if (sockfd < 0) {
+        return sockfd;
+    }
+    int nrecv = recv(hftp->sockfd, hftp->recvbuf, FTP_RECV_BUFSIZE, 0);
+    if (nrecv <= 0) {
+        closesocket(hftp->sockfd);
+        return ERR_RECV;
+    }
+    //printf("< %s", hftp->recvbuf);
+    // you can new thread to recv data
+    {
+        char recvbuf[1024];
+        int ntotal = 0;
+        while (1) {
+            nrecv = recv(sockfd, recvbuf, sizeof(recvbuf), 0);
+            if (cb) {
+                cb(hftp, recvbuf, nrecv);
+            }
+            if (nrecv <= 0) break;
+            ntotal += nrecv;
+        }
+    }
+    closesocket(sockfd);
+    nrecv = recv(hftp->sockfd, hftp->recvbuf, FTP_RECV_BUFSIZE, 0);
+    if (nrecv <= 0) {
+        closesocket(hftp->sockfd);
+        return ERR_RECV;
+    }
+    //printf("< %s", hftp->recvbuf);
+    status_code = atoi(hftp->recvbuf);
+    return status_code == FTP_STATUS_TRANSFER_COMPLETE ? 0 : status_code;
+}
+
+#include <stdio.h>
+
+// local => remote
+int ftp_upload(ftp_handle_t* hftp, const char* local_filepath, const char* remote_filepath) {
+    int status_code = ftp_exec(hftp, "PASV", NULL);
+    if (status_code != FTP_STATUS_PASV) {
+        return status_code;
+    }
+    char host[64];
+    int port = 0;
+    int ret = ftp_parse_pasv(hftp->recvbuf, host, &port);
+    if (ret != 0) {
+        return ret;
+    }
+    //ftp_exec(hftp, "STOR", remote_filepath);
+    char request[1024];
+    int len = snprintf(request, sizeof(request), "STOR %s\r\n", remote_filepath);
+    int nsend = send(hftp->sockfd, request, len, 0);
+    if (nsend != len) {
+        closesocket(hftp->sockfd);
+        return ERR_SEND;
+    }
+    //printf("> %s", request);
+    int sockfd = ConnectTimeout(host, port, DEFAULT_CONNECT_TIMEOUT);
+    if (sockfd < 0) {
+        return sockfd;
+    }
+    int nrecv = recv(hftp->sockfd, hftp->recvbuf, FTP_RECV_BUFSIZE, 0);
+    if (nrecv <= 0) {
+        closesocket(hftp->sockfd);
+        return ERR_RECV;
+    }
+    //printf("< %s", hftp->recvbuf);
+    {
+        // you can new thread to send data
+        FILE* fp = fopen(local_filepath, "r");
+        if (fp == NULL) {
+            closesocket(sockfd);
+            return ERR_OPEN_FILE;
+        }
+        char sendbuf[1024];
+        int nread, nsend;
+        int ntotal = 0;
+        while (1) {
+            nread = fread(sendbuf, 1, sizeof(sendbuf), fp);
+            if (nread == 0) break;
+            nsend = send(sockfd, sendbuf, nread, 0);
+            if (nsend != nread) break;
+            ntotal += nsend;
+        }
+        fclose(fp);
+    }
+    closesocket(sockfd);
+    nrecv = recv(hftp->sockfd, hftp->recvbuf, FTP_RECV_BUFSIZE, 0);
+    if (nrecv <= 0) {
+        closesocket(hftp->sockfd);
+        return ERR_RECV;
+    }
+    //printf("< %s", hftp->recvbuf);
+    status_code = atoi(hftp->recvbuf);
+    return status_code == FTP_STATUS_TRANSFER_COMPLETE ? 0 : status_code;
+}
+
+static int s_ftp_download_cb(ftp_handle_t* hftp, char* buf, int len) {
+    FILE* fp = (FILE*)hftp->userdata;
+    if (fp == NULL) return -1;
+    if (len <= 0) {
+        fclose(fp);
+        hftp->userdata = NULL;
+        return 0;
+    }
+    return fwrite(buf, 1, len, fp);
+}
+
+// remote => local
+int ftp_download(ftp_handle_t* hftp, const char* remote_filepath, const char* local_filepath) {
+    FILE* fp = fopen(local_filepath, "w");
+    if (fp == NULL) {
+        return ERR_OPEN_FILE;
+    }
+    hftp->userdata = (void*)fp;
+    int ret = ftp_download_with_cb(hftp, remote_filepath, s_ftp_download_cb);
+    // ensure fclose
+    if (hftp->userdata != NULL) {
+        fclose(fp);
+        hftp->userdata = NULL;
+    }
+    return ret;
+}

+ 98 - 0
protocol/ftp.h

@@ -0,0 +1,98 @@
+#ifndef HW_FTP_H_
+#define HW_FTP_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define FTP_COMMAND_PORT    21
+#define FTP_DATA_PORT       20
+
+// ftp_command
+// X(name)
+#define FTP_COMMAND_MAP(X) \
+    X(HELP)     \
+    X(USER)     \
+    X(PASS)     \
+    X(PWD)      \
+    X(CWD)      \
+    X(CDUP)     \
+    X(MKD)      \
+    X(RMD)      \
+    X(STAT)     \
+    X(SIZE)     \
+    X(DELE)     \
+    X(RNFR)     \
+    X(RNTO)     \
+    X(PORT)     \
+    X(PASV)     \
+    X(LIST)     \
+    X(NLST)     \
+    X(APPE)     \
+    X(RETR)     \
+    X(STOR)     \
+    X(QUIT)     \
+
+enum ftp_command {
+#define X(name) FTP_##name,
+    FTP_COMMAND_MAP(X)
+#undef  X
+};
+
+// ftp_status
+// XXX(code, name, string)
+#define FTP_STATUS_MAP(XXX) \
+    XXX(220,    READY,          Ready)  \
+    XXX(221,    BYE,            Bye)    \
+    XXX(226,    TRANSFER_COMPLETE,  Transfer complete)  \
+    XXX(227,    PASV,           Entering Passive Mode)  \
+    XXX(331,    PASS,           Password required)      \
+    XXX(230,    LOGIN_OK,       Login OK)   \
+    XXX(250,    OK,             OK)         \
+    XXX(500,    BAD_SYNTAX,     Bad syntax)         \
+    XXX(530,    NOT_LOGIN,      Not login)  \
+
+enum ftp_status {
+#define XXX(code, name, string) FTP_STATUS_##name = code,
+    FTP_STATUS_MAP(XXX)
+#undef  XXX
+};
+
+// more friendly macros
+#define FTP_MKDIR       FTP_MKD
+#define FTP_RMDIR       FTP_RMD
+#define FTP_APPEND      FTP_APPE
+#define FTP_REMOVE      FTP_DELE
+#define FTP_DOWNLOAD    FTP_RETR
+#define FTP_UPLOAD      FTP_STOR
+
+#define FTP_RECV_BUFSIZE    8192
+
+typedef struct ftp_handle_s {
+    int     sockfd;
+    char    recvbuf[FTP_RECV_BUFSIZE];
+    void*   userdata;
+} ftp_handle_t;
+
+const char* ftp_command_str(enum ftp_command cmd);
+const char* ftp_status_str(enum ftp_status status);
+
+int ftp_connect(ftp_handle_t* hftp, const char* host, int port);
+int ftp_login(ftp_handle_t* hftp, const char* username, const char* password);
+int ftp_quit(ftp_handle_t* hftp);
+
+int ftp_exec(ftp_handle_t* hftp, const char* cmd, const char* param);
+
+// local => remote
+int ftp_upload(ftp_handle_t* hftp, const char* local_filepath, const char* remote_filepath);
+// remote => local
+int ftp_download(ftp_handle_t* hftp, const char* remote_filepath, const char* local_filepath);
+
+typedef int (*ftp_download_cb)(ftp_handle_t* hftp, char* buf, int len);
+int ftp_download_with_cb(ftp_handle_t* hftp, const char* filepath, ftp_download_cb cb);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // HW_FTP_H_

+ 22 - 21
protocol/smtp.c

@@ -1,6 +1,7 @@
 #include "smtp.h"
 
 #include "hsocket.h"
+#include "herr.h"
 #include "base64.h"
 
 const char* smtp_command_str(enum smtp_command cmd) {
@@ -58,7 +59,7 @@ int sendmail(const char* smtp_server,
     int ret, nsend, nrecv;
     nrecv = recv(sockfd, buf, buflen, 0);
     if (nrecv <= 0) {
-        ret = -30;
+        ret = ERR_RECV;
         goto error;
     }
     status_code = atoi(buf);
@@ -70,12 +71,12 @@ int sendmail(const char* smtp_server,
     cmdlen = smtp_build_command(SMTP_EHLO, smtp_server, buf, buflen);
     nsend = send(sockfd, buf, cmdlen, 0);
     if (nsend != cmdlen) {
-        ret = -20;
+        ret = ERR_SEND;
         goto error;
     }
     nrecv = recv(sockfd, buf, buflen, 0);
     if (nrecv <= 0) {
-        ret = -30;
+        ret = ERR_RECV;
         goto error;
     }
     status_code = atoi(buf);
@@ -87,12 +88,12 @@ int sendmail(const char* smtp_server,
     cmdlen = smtp_build_command(SMTP_AUTH, "PLAIN", buf, buflen);
     nsend = send(sockfd, buf, cmdlen, 0);
     if (nsend != cmdlen) {
-        ret = -20;
+        ret = ERR_SEND;
         goto error;
     }
     nrecv = recv(sockfd, buf, buflen, 0);
     if (nrecv <= 0) {
-        ret = -30;
+        ret = ERR_RECV;
         goto error;
     }
     status_code = atoi(buf);
@@ -117,12 +118,12 @@ int sendmail(const char* smtp_server,
     cmdlen += 2;
     nsend = send(sockfd, buf, cmdlen, 0);
     if (nsend != cmdlen) {
-        ret = -20;
+        ret = ERR_SEND;
         goto error;
     }
     nrecv = recv(sockfd, buf, buflen, 0);
     if (nrecv <= 0) {
-        ret = -30;
+        ret = ERR_RECV;
         goto error;
     }
     status_code = atoi(buf);
@@ -134,12 +135,12 @@ int sendmail(const char* smtp_server,
     cmdlen = smtp_build_command(SMTP_MAIL, mail->from, buf, buflen);
     nsend = send(sockfd, buf, cmdlen, 0);
     if (nsend != cmdlen) {
-        ret = -20;
+        ret = ERR_SEND;
         goto error;
     }
     nrecv = recv(sockfd, buf, buflen, 0);
     if (nrecv <= 0) {
-        ret = -30;
+        ret = ERR_RECV;
         goto error;
     }
     status_code = atoi(buf);
@@ -151,12 +152,12 @@ int sendmail(const char* smtp_server,
     cmdlen = smtp_build_command(SMTP_RCPT, mail->to, buf, buflen);
     nsend = send(sockfd, buf, cmdlen, 0);
     if (nsend != cmdlen) {
-        ret = -20;
+        ret = ERR_SEND;
         goto error;
     }
     nrecv = recv(sockfd, buf, buflen, 0);
     if (nrecv <= 0) {
-        ret = -30;
+        ret = ERR_RECV;
         goto error;
     }
     status_code = atoi(buf);
@@ -168,12 +169,12 @@ int sendmail(const char* smtp_server,
     cmdlen = smtp_build_command(SMTP_DATA, NULL, buf, buflen);
     nsend = send(sockfd, buf, cmdlen, 0);
     if (nsend != cmdlen) {
-        ret = -20;
+        ret = ERR_SEND;
         goto error;
     }
     nrecv = recv(sockfd, buf, buflen, 0);
     if (nrecv <= 0) {
-        ret = -30;
+        ret = ERR_RECV;
         goto error;
     }
     status_code = atoi(buf);
@@ -186,39 +187,39 @@ int sendmail(const char* smtp_server,
     cmdlen = snprintf(buf, buflen, "From:%s\r\n", mail->from);
     nsend = send(sockfd, buf, cmdlen, 0);
     if (nsend != cmdlen) {
-        ret = -20;
+        ret = ERR_SEND;
         goto error;
     }
     // To:
     cmdlen = snprintf(buf, buflen, "To:%s\r\n", mail->to);
     nsend = send(sockfd, buf, cmdlen, 0);
     if (nsend != cmdlen) {
-        ret = -20;
+        ret = ERR_SEND;
         goto error;
     }
     // Subject:
     cmdlen = snprintf(buf, buflen, "Subject:%s\r\n\r\n", mail->subject);
     nsend = send(sockfd, buf, cmdlen, 0);
     if (nsend != cmdlen) {
-        ret = -20;
+        ret = ERR_SEND;
         goto error;
     }
     // body
     cmdlen = strlen(mail->body);
     nsend = send(sockfd, mail->body, cmdlen, 0);
     if (nsend != cmdlen) {
-        ret = -20;
+        ret = ERR_SEND;
         goto error;
     }
     // EOB
     nsend = send(sockfd, SMTP_EOB, SMTP_EOB_LEN, 0);
     if (nsend != SMTP_EOB_LEN) {
-        ret = -20;
+        ret = ERR_SEND;
         goto error;
     }
     nrecv = recv(sockfd, buf, buflen, 0);
     if (nrecv <= 0) {
-        ret = -30;
+        ret = ERR_SEND;
         goto error;
     }
     status_code = atoi(buf);
@@ -230,12 +231,12 @@ int sendmail(const char* smtp_server,
     cmdlen = smtp_build_command(SMTP_QUIT, NULL, buf, buflen);
     nsend = send(sockfd, buf, cmdlen, 0);
     if (nsend != cmdlen) {
-        ret = -20;
+        ret = ERR_SEND;
         goto error;
     }
     nrecv = recv(sockfd, buf, buflen, 0);
     if (nrecv <= 0) {
-        ret = -30;
+        ret = ERR_RECV;
         goto error;
     }
     /*

+ 88 - 0
unittest/ftp_test.c

@@ -0,0 +1,88 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ftp.h"
+
+void print_help() {
+    printf("Usage:\n\
+help\n\
+login <username> <password>\n\
+download <remote_filepath> <local_filepath>\n\
+upload   <local_filepath>  <remote_filepath>\n\
+quit\n");
+}
+
+int main(int argc, char** argv) {
+    if (argc < 2) {
+        printf("Usage: ftp host [port]\n");
+        return 0;
+    }
+    const char* host = argv[1];
+    int port = FTP_COMMAND_PORT;
+    if (argc >= 3) {
+        port = atoi(argv[2]);
+    }
+    int ret = 0;
+    ftp_handle_t hftp;
+    ret = ftp_connect(&hftp, host, port);
+    if (ret != 0) {
+        printf("ftp connect failed!\n");
+        return ret;
+    }
+    print_help();
+
+    char cmd[256] = {0};
+    char param1[256] = {0};
+    char param2[256] = {0};
+    while (1) {
+        printf("> ");
+        scanf("%s", cmd);
+        if (strncmp(cmd, "help", 4) == 0) {
+            print_help();
+        }
+        else if (strncmp(cmd, "login", 5) == 0) {
+            scanf("%s", param1);
+            scanf("%s", param2);
+            //printf("cmd=%s param1=%s param2=%s\n", cmd, param1, param2);
+            const char* username = param1;
+            const char* password = param2;
+            ret = ftp_login(&hftp, username, password);
+            printf("%s", hftp.recvbuf);
+            if (ret != 0) break;
+        }
+        else if (strncmp(cmd, "upload", 6) == 0) {
+            scanf("%s", param1);
+            scanf("%s", param2);
+            //printf("cmd=%s param1=%s param2=%s\n", cmd, param1, param2);
+            const char* localfile = param1;
+            const char* remotefile = param2;
+            ret = ftp_upload(&hftp, localfile, remotefile);
+            printf("%s", hftp.recvbuf);
+            if (ret != 0) break;
+        }
+        else if (strncmp(cmd, "download", 8) == 0) {
+            scanf("%s", param1);
+            scanf("%s", param2);
+            //printf("cmd=%s param1=%s param2=%s\n", cmd, param1, param2);
+            const char* remotefile = param1;
+            const char* localfile = param2;
+            ret = ftp_download(&hftp, remotefile, localfile);
+            printf("%s", hftp.recvbuf);
+            if (ret != 0) break;
+        }
+        else if (strncmp(cmd, "quit", 4) == 0) {
+            break;
+        }
+        else {
+            scanf("%s", param1);
+            //printf("cmd=%s param=%s\n", cmd, param1);
+            ret = ftp_exec(&hftp, cmd, param1);
+            printf("%s", hftp.recvbuf);
+        }
+    }
+    printf("QUIT\n");
+    ftp_quit(&hftp);
+    printf("%s", hftp.recvbuf);
+    return 0;
+}