1
0

ftp.c 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. #include "ftp.h"
  2. #include "hsocket.h"
  3. #include "herr.h"
  4. const char* ftp_command_str(enum ftp_command cmd) {
  5. switch (cmd) {
  6. #define X(name) case FTP_##name: return #name;
  7. FTP_COMMAND_MAP(X)
  8. #undef X
  9. default: return "<unknown>";
  10. }
  11. }
  12. const char* ftp_status_str(enum ftp_status status) {
  13. switch (status) {
  14. #define XXX(code, name, string) case FTP_STATUS_##name: return #string;
  15. FTP_STATUS_MAP(XXX)
  16. #undef XXX
  17. default: return "<unknown>";
  18. }
  19. }
  20. int ftp_connect(ftp_handle_t* hftp, const char* host, int port) {
  21. int sockfd = ConnectTimeout(host, port, DEFAULT_CONNECT_TIMEOUT);
  22. if (sockfd < 0) {
  23. return sockfd;
  24. }
  25. so_sndtimeo(sockfd, 5000);
  26. so_rcvtimeo(sockfd, 5000);
  27. hftp->sockfd = sockfd;
  28. int ret = 0;
  29. int status_code = 0;
  30. memset(hftp->recvbuf, 0, FTP_RECV_BUFSIZE);
  31. int nrecv = recv(sockfd, hftp->recvbuf, FTP_RECV_BUFSIZE, 0);
  32. if (nrecv <= 0) {
  33. ret = ERR_RECV;
  34. goto error;
  35. }
  36. status_code = atoi(hftp->recvbuf);
  37. if (status_code != FTP_STATUS_READY) {
  38. ret = status_code;
  39. goto error;
  40. }
  41. return 0;
  42. error:
  43. closesocket(sockfd);
  44. return ret;
  45. }
  46. int ftp_login(ftp_handle_t* hftp, const char* username, const char* password) {
  47. int status_code = ftp_exec(hftp, "USER", username);
  48. status_code = ftp_exec(hftp, "PASS", password);
  49. return status_code == FTP_STATUS_LOGIN_OK ? 0 : status_code;
  50. }
  51. int ftp_quit(ftp_handle_t* hftp) {
  52. ftp_exec(hftp, "QUIT", NULL);
  53. closesocket(hftp->sockfd);
  54. return 0;
  55. }
  56. int ftp_exec(ftp_handle_t* hftp, const char* cmd, const char* param) {
  57. char buf[1024];
  58. int len = 0;
  59. if (param && *param) {
  60. len = snprintf(buf, sizeof(buf), "%s %s\r\n", cmd, param);
  61. }
  62. else {
  63. len = snprintf(buf, sizeof(buf), "%s\r\n", cmd);
  64. }
  65. int nsend, nrecv;
  66. int ret = 0;
  67. nsend = send(hftp->sockfd, buf, len, 0);
  68. if (nsend != len) {
  69. ret = ERR_SEND;
  70. goto error;
  71. }
  72. //printf("> %s", buf);
  73. memset(hftp->recvbuf, 0, FTP_RECV_BUFSIZE);
  74. nrecv = recv(hftp->sockfd, hftp->recvbuf, FTP_RECV_BUFSIZE, 0);
  75. if (nrecv <= 0) {
  76. ret = ERR_RECV;
  77. goto error;
  78. }
  79. //printf("< %s", hftp->recvbuf);
  80. return atoi(hftp->recvbuf);
  81. error:
  82. closesocket(hftp->sockfd);
  83. return ret;
  84. }
  85. static int ftp_parse_pasv(const char* resp, char* host, int* port) {
  86. // 227 Entering Passive Mode (127,0,0,1,4,51)
  87. const char* str = strchr(resp, '(');
  88. if (str == NULL) {
  89. return ERR_RESPONSE;
  90. }
  91. int arr[6];
  92. sscanf(str, "(%d,%d,%d,%d,%d,%d)",
  93. &arr[0], &arr[1], &arr[2], &arr[3], &arr[4], &arr[5]);
  94. sprintf(host, "%d.%d.%d.%d", arr[0], arr[1], arr[2], arr[3]);
  95. *port = arr[4] << 8 | arr[5];
  96. return 0;
  97. }
  98. int ftp_download_with_cb(ftp_handle_t* hftp, const char* filepath, ftp_download_cb cb) {
  99. int status_code = ftp_exec(hftp, "PASV", NULL);
  100. if (status_code != FTP_STATUS_PASV) {
  101. return status_code;
  102. }
  103. char host[64];
  104. int port = 0;
  105. int ret = ftp_parse_pasv(hftp->recvbuf, host, &port);
  106. if (ret != 0) {
  107. return ret;
  108. }
  109. //ftp_exec(hftp, "RETR", filepath);
  110. char request[1024];
  111. int len = snprintf(request, sizeof(request), "RETR %s\r\n", filepath);
  112. int nsend = send(hftp->sockfd, request, len, 0);
  113. if (nsend != len) {
  114. closesocket(hftp->sockfd);
  115. return ERR_SEND;
  116. }
  117. //printf("> %s", request);
  118. int sockfd = ConnectTimeout(host, port, DEFAULT_CONNECT_TIMEOUT);
  119. if (sockfd < 0) {
  120. return sockfd;
  121. }
  122. int nrecv = recv(hftp->sockfd, hftp->recvbuf, FTP_RECV_BUFSIZE, 0);
  123. if (nrecv <= 0) {
  124. closesocket(hftp->sockfd);
  125. return ERR_RECV;
  126. }
  127. //printf("< %s", hftp->recvbuf);
  128. {
  129. // you can create thread to recv data
  130. char recvbuf[1024];
  131. int ntotal = 0;
  132. while (1) {
  133. nrecv = recv(sockfd, recvbuf, sizeof(recvbuf), 0);
  134. if (cb) {
  135. cb(hftp, recvbuf, nrecv);
  136. }
  137. if (nrecv <= 0) break;
  138. ntotal += nrecv;
  139. }
  140. }
  141. closesocket(sockfd);
  142. nrecv = recv(hftp->sockfd, hftp->recvbuf, FTP_RECV_BUFSIZE, 0);
  143. if (nrecv <= 0) {
  144. closesocket(hftp->sockfd);
  145. return ERR_RECV;
  146. }
  147. //printf("< %s", hftp->recvbuf);
  148. status_code = atoi(hftp->recvbuf);
  149. return status_code == FTP_STATUS_TRANSFER_COMPLETE ? 0 : status_code;
  150. }
  151. // local => remote
  152. int ftp_upload(ftp_handle_t* hftp, const char* local_filepath, const char* remote_filepath) {
  153. int status_code = ftp_exec(hftp, "PASV", NULL);
  154. if (status_code != FTP_STATUS_PASV) {
  155. return status_code;
  156. }
  157. char host[64];
  158. int port = 0;
  159. int ret = ftp_parse_pasv(hftp->recvbuf, host, &port);
  160. if (ret != 0) {
  161. return ret;
  162. }
  163. //ftp_exec(hftp, "STOR", remote_filepath);
  164. char request[1024];
  165. int len = snprintf(request, sizeof(request), "STOR %s\r\n", remote_filepath);
  166. int nsend = send(hftp->sockfd, request, len, 0);
  167. if (nsend != len) {
  168. closesocket(hftp->sockfd);
  169. return ERR_SEND;
  170. }
  171. //printf("> %s", request);
  172. int sockfd = ConnectTimeout(host, port, DEFAULT_CONNECT_TIMEOUT);
  173. if (sockfd < 0) {
  174. return sockfd;
  175. }
  176. int nrecv = recv(hftp->sockfd, hftp->recvbuf, FTP_RECV_BUFSIZE, 0);
  177. if (nrecv <= 0) {
  178. closesocket(hftp->sockfd);
  179. return ERR_RECV;
  180. }
  181. //printf("< %s", hftp->recvbuf);
  182. {
  183. // you can create thread to send data
  184. FILE* fp = fopen(local_filepath, "r");
  185. if (fp == NULL) {
  186. closesocket(sockfd);
  187. return ERR_OPEN_FILE;
  188. }
  189. char sendbuf[1024];
  190. int nread, nsend;
  191. int ntotal = 0;
  192. while (1) {
  193. nread = fread(sendbuf, 1, sizeof(sendbuf), fp);
  194. if (nread == 0) break;
  195. nsend = send(sockfd, sendbuf, nread, 0);
  196. if (nsend != nread) break;
  197. ntotal += nsend;
  198. }
  199. fclose(fp);
  200. }
  201. closesocket(sockfd);
  202. nrecv = recv(hftp->sockfd, hftp->recvbuf, FTP_RECV_BUFSIZE, 0);
  203. if (nrecv <= 0) {
  204. closesocket(hftp->sockfd);
  205. return ERR_RECV;
  206. }
  207. //printf("< %s", hftp->recvbuf);
  208. status_code = atoi(hftp->recvbuf);
  209. return status_code == FTP_STATUS_TRANSFER_COMPLETE ? 0 : status_code;
  210. }
  211. static int s_ftp_download_cb(ftp_handle_t* hftp, char* buf, int len) {
  212. FILE* fp = (FILE*)hftp->userdata;
  213. if (fp == NULL) return -1;
  214. if (len <= 0) {
  215. fclose(fp);
  216. hftp->userdata = NULL;
  217. return 0;
  218. }
  219. return fwrite(buf, 1, len, fp);
  220. }
  221. // remote => local
  222. int ftp_download(ftp_handle_t* hftp, const char* remote_filepath, const char* local_filepath) {
  223. FILE* fp = fopen(local_filepath, "w");
  224. if (fp == NULL) {
  225. return ERR_OPEN_FILE;
  226. }
  227. hftp->userdata = (void*)fp;
  228. int ret = ftp_download_with_cb(hftp, remote_filepath, s_ftp_download_cb);
  229. // ensure fclose
  230. if (hftp->userdata != NULL) {
  231. fclose(fp);
  232. hftp->userdata = NULL;
  233. }
  234. return ret;
  235. }