PageRenderTime 62ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/ext/ftp/ftp.c

http://github.com/php/php-src
C | 2314 lines | 2154 code | 82 blank | 78 comment | 128 complexity | 9a6593a4f53ee80011f4bb3848b5d77e MD5 | raw file
Possible License(s): BSD-2-Clause, BSD-3-Clause, MPL-2.0-no-copyleft-exception, LGPL-2.1
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Copyright (c) The PHP Group |
  4. +----------------------------------------------------------------------+
  5. | This source file is subject to version 3.01 of the PHP license, |
  6. | that is bundled with this package in the file LICENSE, and is |
  7. | available through the world-wide-web at the following url: |
  8. | http://www.php.net/license/3_01.txt |
  9. | If you did not receive a copy of the PHP license and are unable to |
  10. | obtain it through the world-wide-web, please send a note to |
  11. | license@php.net so we can mail you a copy immediately. |
  12. +----------------------------------------------------------------------+
  13. | Authors: Andrew Skalski <askalski@chek.com> |
  14. | Stefan Esser <sesser@php.net> (resume functions) |
  15. +----------------------------------------------------------------------+
  16. */
  17. #ifdef HAVE_CONFIG_H
  18. #include "config.h"
  19. #endif
  20. #include "php.h"
  21. #if HAVE_FTP
  22. #include <stdio.h>
  23. #include <ctype.h>
  24. #include <stdlib.h>
  25. #ifdef HAVE_UNISTD_H
  26. #include <unistd.h>
  27. #endif
  28. #include <fcntl.h>
  29. #include <string.h>
  30. #include <time.h>
  31. #ifdef PHP_WIN32
  32. #include <winsock2.h>
  33. #else
  34. #ifdef HAVE_SYS_TYPES_H
  35. #include <sys/types.h>
  36. #endif
  37. #include <sys/socket.h>
  38. #include <netinet/in.h>
  39. #include <arpa/inet.h>
  40. #include <netdb.h>
  41. #endif
  42. #include <errno.h>
  43. #if HAVE_SYS_TIME_H
  44. #include <sys/time.h>
  45. #endif
  46. #ifdef HAVE_SYS_SELECT_H
  47. #include <sys/select.h>
  48. #endif
  49. #ifdef HAVE_FTP_SSL
  50. #include <openssl/ssl.h>
  51. #include <openssl/err.h>
  52. #endif
  53. #include "ftp.h"
  54. #include "ext/standard/fsock.h"
  55. /* sends an ftp command, returns true on success, false on error.
  56. * it sends the string "cmd args\r\n" if args is non-null, or
  57. * "cmd\r\n" if args is null
  58. */
  59. static int ftp_putcmd( ftpbuf_t *ftp,
  60. const char *cmd,
  61. const size_t cmd_len,
  62. const char *args,
  63. const size_t args_len);
  64. /* wrapper around send/recv to handle timeouts */
  65. static int my_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len);
  66. static int my_recv(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len);
  67. static int my_accept(ftpbuf_t *ftp, php_socket_t s, struct sockaddr *addr, socklen_t *addrlen);
  68. /* reads a line the socket , returns true on success, false on error */
  69. static int ftp_readline(ftpbuf_t *ftp);
  70. /* reads an ftp response, returns true on success, false on error */
  71. static int ftp_getresp(ftpbuf_t *ftp);
  72. /* sets the ftp transfer type */
  73. static int ftp_type(ftpbuf_t *ftp, ftptype_t type);
  74. /* opens up a data stream */
  75. static databuf_t* ftp_getdata(ftpbuf_t *ftp);
  76. /* accepts the data connection, returns updated data buffer */
  77. static databuf_t* data_accept(databuf_t *data, ftpbuf_t *ftp);
  78. /* closes the data connection, returns NULL */
  79. static databuf_t* data_close(ftpbuf_t *ftp, databuf_t *data);
  80. /* generic file lister */
  81. static char** ftp_genlist(ftpbuf_t *ftp, const char *cmd, const size_t cmd_len, const char *path, const size_t path_len);
  82. #ifdef HAVE_FTP_SSL
  83. /* shuts down a TLS/SSL connection */
  84. static void ftp_ssl_shutdown(ftpbuf_t *ftp, php_socket_t fd, SSL *ssl_handle);
  85. #endif
  86. /* IP and port conversion box */
  87. union ipbox {
  88. struct in_addr ia[2];
  89. unsigned short s[4];
  90. unsigned char c[8];
  91. };
  92. /* {{{ ftp_open
  93. */
  94. ftpbuf_t*
  95. ftp_open(const char *host, short port, zend_long timeout_sec)
  96. {
  97. ftpbuf_t *ftp;
  98. socklen_t size;
  99. struct timeval tv;
  100. /* alloc the ftp structure */
  101. ftp = ecalloc(1, sizeof(*ftp));
  102. tv.tv_sec = timeout_sec;
  103. tv.tv_usec = 0;
  104. ftp->fd = php_network_connect_socket_to_host(host,
  105. (unsigned short) (port ? port : 21), SOCK_STREAM,
  106. 0, &tv, NULL, NULL, NULL, 0, STREAM_SOCKOP_NONE);
  107. if (ftp->fd == -1) {
  108. goto bail;
  109. }
  110. /* Default Settings */
  111. ftp->timeout_sec = timeout_sec;
  112. ftp->nb = 0;
  113. size = sizeof(ftp->localaddr);
  114. memset(&ftp->localaddr, 0, size);
  115. if (getsockname(ftp->fd, (struct sockaddr*) &ftp->localaddr, &size) != 0) {
  116. php_error_docref(NULL, E_WARNING, "getsockname failed: %s (%d)", strerror(errno), errno);
  117. goto bail;
  118. }
  119. if (!ftp_getresp(ftp) || ftp->resp != 220) {
  120. goto bail;
  121. }
  122. return ftp;
  123. bail:
  124. if (ftp->fd != -1) {
  125. closesocket(ftp->fd);
  126. }
  127. efree(ftp);
  128. return NULL;
  129. }
  130. /* }}} */
  131. /* {{{ ftp_close
  132. */
  133. ftpbuf_t*
  134. ftp_close(ftpbuf_t *ftp)
  135. {
  136. if (ftp == NULL) {
  137. return NULL;
  138. }
  139. if (ftp->data) {
  140. data_close(ftp, ftp->data);
  141. }
  142. if (ftp->stream && ftp->closestream) {
  143. php_stream_close(ftp->stream);
  144. }
  145. if (ftp->fd != -1) {
  146. #ifdef HAVE_FTP_SSL
  147. if (ftp->ssl_active) {
  148. ftp_ssl_shutdown(ftp, ftp->fd, ftp->ssl_handle);
  149. }
  150. #endif
  151. closesocket(ftp->fd);
  152. }
  153. ftp_gc(ftp);
  154. efree(ftp);
  155. return NULL;
  156. }
  157. /* }}} */
  158. /* {{{ ftp_gc
  159. */
  160. void
  161. ftp_gc(ftpbuf_t *ftp)
  162. {
  163. if (ftp == NULL) {
  164. return;
  165. }
  166. if (ftp->pwd) {
  167. efree(ftp->pwd);
  168. ftp->pwd = NULL;
  169. }
  170. if (ftp->syst) {
  171. efree(ftp->syst);
  172. ftp->syst = NULL;
  173. }
  174. }
  175. /* }}} */
  176. /* {{{ ftp_quit
  177. */
  178. int
  179. ftp_quit(ftpbuf_t *ftp)
  180. {
  181. if (ftp == NULL) {
  182. return 0;
  183. }
  184. if (!ftp_putcmd(ftp, "QUIT", sizeof("QUIT")-1, NULL, (size_t) 0)) {
  185. return 0;
  186. }
  187. if (!ftp_getresp(ftp) || ftp->resp != 221) {
  188. return 0;
  189. }
  190. if (ftp->pwd) {
  191. efree(ftp->pwd);
  192. ftp->pwd = NULL;
  193. }
  194. return 1;
  195. }
  196. /* }}} */
  197. /* {{{ ftp_login
  198. */
  199. int
  200. ftp_login(ftpbuf_t *ftp, const char *user, const size_t user_len, const char *pass, const size_t pass_len)
  201. {
  202. #ifdef HAVE_FTP_SSL
  203. SSL_CTX *ctx = NULL;
  204. long ssl_ctx_options = SSL_OP_ALL;
  205. int err, res;
  206. zend_bool retry;
  207. #endif
  208. if (ftp == NULL) {
  209. return 0;
  210. }
  211. #ifdef HAVE_FTP_SSL
  212. if (ftp->use_ssl && !ftp->ssl_active) {
  213. if (!ftp_putcmd(ftp, "AUTH", sizeof("AUTH")-1, "TLS", sizeof("TLS")-1)) {
  214. return 0;
  215. }
  216. if (!ftp_getresp(ftp)) {
  217. return 0;
  218. }
  219. if (ftp->resp != 234) {
  220. if (!ftp_putcmd(ftp, "AUTH", sizeof("AUTH")-1, "SSL", sizeof("SSL")-1)) {
  221. return 0;
  222. }
  223. if (!ftp_getresp(ftp)) {
  224. return 0;
  225. }
  226. if (ftp->resp != 334) {
  227. return 0;
  228. } else {
  229. ftp->old_ssl = 1;
  230. ftp->use_ssl_for_data = 1;
  231. }
  232. }
  233. ctx = SSL_CTX_new(SSLv23_client_method());
  234. if (ctx == NULL) {
  235. php_error_docref(NULL, E_WARNING, "Failed to create the SSL context");
  236. return 0;
  237. }
  238. #if OPENSSL_VERSION_NUMBER >= 0x0090605fL
  239. ssl_ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
  240. #endif
  241. SSL_CTX_set_options(ctx, ssl_ctx_options);
  242. /* allow SSL to re-use sessions */
  243. SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_BOTH);
  244. ftp->ssl_handle = SSL_new(ctx);
  245. SSL_CTX_free(ctx);
  246. if (ftp->ssl_handle == NULL) {
  247. php_error_docref(NULL, E_WARNING, "Failed to create the SSL handle");
  248. return 0;
  249. }
  250. SSL_set_fd(ftp->ssl_handle, ftp->fd);
  251. do {
  252. res = SSL_connect(ftp->ssl_handle);
  253. err = SSL_get_error(ftp->ssl_handle, res);
  254. /* TODO check if handling other error codes would make sense */
  255. switch (err) {
  256. case SSL_ERROR_NONE:
  257. retry = 0;
  258. break;
  259. case SSL_ERROR_ZERO_RETURN:
  260. retry = 0;
  261. SSL_shutdown(ftp->ssl_handle);
  262. break;
  263. case SSL_ERROR_WANT_READ:
  264. case SSL_ERROR_WANT_WRITE: {
  265. php_pollfd p;
  266. int i;
  267. p.fd = ftp->fd;
  268. p.events = (err == SSL_ERROR_WANT_READ) ? (POLLIN|POLLPRI) : POLLOUT;
  269. p.revents = 0;
  270. i = php_poll2(&p, 1, 300);
  271. retry = i > 0;
  272. }
  273. break;
  274. default:
  275. php_error_docref(NULL, E_WARNING, "SSL/TLS handshake failed");
  276. SSL_shutdown(ftp->ssl_handle);
  277. SSL_free(ftp->ssl_handle);
  278. return 0;
  279. }
  280. } while (retry);
  281. ftp->ssl_active = 1;
  282. if (!ftp->old_ssl) {
  283. /* set protection buffersize to zero */
  284. if (!ftp_putcmd(ftp, "PBSZ", sizeof("PBSZ")-1, "0", sizeof("0")-1)) {
  285. return 0;
  286. }
  287. if (!ftp_getresp(ftp)) {
  288. return 0;
  289. }
  290. /* enable data conn encryption */
  291. if (!ftp_putcmd(ftp, "PROT", sizeof("PROT")-1, "P", sizeof("P")-1)) {
  292. return 0;
  293. }
  294. if (!ftp_getresp(ftp)) {
  295. return 0;
  296. }
  297. ftp->use_ssl_for_data = (ftp->resp >= 200 && ftp->resp <=299);
  298. }
  299. }
  300. #endif
  301. if (!ftp_putcmd(ftp, "USER", sizeof("USER")-1, user, user_len)) {
  302. return 0;
  303. }
  304. if (!ftp_getresp(ftp)) {
  305. return 0;
  306. }
  307. if (ftp->resp == 230) {
  308. return 1;
  309. }
  310. if (ftp->resp != 331) {
  311. return 0;
  312. }
  313. if (!ftp_putcmd(ftp, "PASS", sizeof("PASS")-1, pass, pass_len)) {
  314. return 0;
  315. }
  316. if (!ftp_getresp(ftp)) {
  317. return 0;
  318. }
  319. return (ftp->resp == 230);
  320. }
  321. /* }}} */
  322. /* {{{ ftp_reinit
  323. */
  324. int
  325. ftp_reinit(ftpbuf_t *ftp)
  326. {
  327. if (ftp == NULL) {
  328. return 0;
  329. }
  330. ftp_gc(ftp);
  331. ftp->nb = 0;
  332. if (!ftp_putcmd(ftp, "REIN", sizeof("REIN")-1, NULL, (size_t) 0)) {
  333. return 0;
  334. }
  335. if (!ftp_getresp(ftp) || ftp->resp != 220) {
  336. return 0;
  337. }
  338. return 1;
  339. }
  340. /* }}} */
  341. /* {{{ ftp_syst
  342. */
  343. const char*
  344. ftp_syst(ftpbuf_t *ftp)
  345. {
  346. char *syst, *end;
  347. if (ftp == NULL) {
  348. return NULL;
  349. }
  350. /* default to cached value */
  351. if (ftp->syst) {
  352. return ftp->syst;
  353. }
  354. if (!ftp_putcmd(ftp, "SYST", sizeof("SYST")-1, NULL, (size_t) 0)) {
  355. return NULL;
  356. }
  357. if (!ftp_getresp(ftp) || ftp->resp != 215) {
  358. return NULL;
  359. }
  360. syst = ftp->inbuf;
  361. while (*syst == ' ') {
  362. syst++;
  363. }
  364. if ((end = strchr(syst, ' '))) {
  365. *end = 0;
  366. }
  367. ftp->syst = estrdup(syst);
  368. if (end) {
  369. *end = ' ';
  370. }
  371. return ftp->syst;
  372. }
  373. /* }}} */
  374. /* {{{ ftp_pwd
  375. */
  376. const char*
  377. ftp_pwd(ftpbuf_t *ftp)
  378. {
  379. char *pwd, *end;
  380. if (ftp == NULL) {
  381. return NULL;
  382. }
  383. /* default to cached value */
  384. if (ftp->pwd) {
  385. return ftp->pwd;
  386. }
  387. if (!ftp_putcmd(ftp, "PWD", sizeof("PWD")-1, NULL, (size_t) 0)) {
  388. return NULL;
  389. }
  390. if (!ftp_getresp(ftp) || ftp->resp != 257) {
  391. return NULL;
  392. }
  393. /* copy out the pwd from response */
  394. if ((pwd = strchr(ftp->inbuf, '"')) == NULL) {
  395. return NULL;
  396. }
  397. if ((end = strrchr(++pwd, '"')) == NULL) {
  398. return NULL;
  399. }
  400. ftp->pwd = estrndup(pwd, end - pwd);
  401. return ftp->pwd;
  402. }
  403. /* }}} */
  404. /* {{{ ftp_exec
  405. */
  406. int
  407. ftp_exec(ftpbuf_t *ftp, const char *cmd, const size_t cmd_len)
  408. {
  409. if (ftp == NULL) {
  410. return 0;
  411. }
  412. if (!ftp_putcmd(ftp, "SITE EXEC", sizeof("SITE EXEC")-1, cmd, cmd_len)) {
  413. return 0;
  414. }
  415. if (!ftp_getresp(ftp) || ftp->resp != 200) {
  416. return 0;
  417. }
  418. return 1;
  419. }
  420. /* }}} */
  421. /* {{{ ftp_raw
  422. */
  423. void
  424. ftp_raw(ftpbuf_t *ftp, const char *cmd, const size_t cmd_len, zval *return_value)
  425. {
  426. if (ftp == NULL || cmd == NULL) {
  427. RETURN_NULL();
  428. }
  429. if (!ftp_putcmd(ftp, cmd, cmd_len, NULL, (size_t) 0)) {
  430. RETURN_NULL();
  431. }
  432. array_init(return_value);
  433. while (ftp_readline(ftp)) {
  434. add_next_index_string(return_value, ftp->inbuf);
  435. if (isdigit(ftp->inbuf[0]) && isdigit(ftp->inbuf[1]) && isdigit(ftp->inbuf[2]) && ftp->inbuf[3] == ' ') {
  436. return;
  437. }
  438. }
  439. }
  440. /* }}} */
  441. /* {{{ ftp_chdir
  442. */
  443. int
  444. ftp_chdir(ftpbuf_t *ftp, const char *dir, const size_t dir_len)
  445. {
  446. if (ftp == NULL) {
  447. return 0;
  448. }
  449. if (ftp->pwd) {
  450. efree(ftp->pwd);
  451. ftp->pwd = NULL;
  452. }
  453. if (!ftp_putcmd(ftp, "CWD", sizeof("CWD")-1, dir, dir_len)) {
  454. return 0;
  455. }
  456. if (!ftp_getresp(ftp) || ftp->resp != 250) {
  457. return 0;
  458. }
  459. return 1;
  460. }
  461. /* }}} */
  462. /* {{{ ftp_cdup
  463. */
  464. int
  465. ftp_cdup(ftpbuf_t *ftp)
  466. {
  467. if (ftp == NULL) {
  468. return 0;
  469. }
  470. if (ftp->pwd) {
  471. efree(ftp->pwd);
  472. ftp->pwd = NULL;
  473. }
  474. if (!ftp_putcmd(ftp, "CDUP", sizeof("CDUP")-1, NULL, (size_t) 0)) {
  475. return 0;
  476. }
  477. if (!ftp_getresp(ftp) || ftp->resp != 250) {
  478. return 0;
  479. }
  480. return 1;
  481. }
  482. /* }}} */
  483. /* {{{ ftp_mkdir
  484. */
  485. zend_string*
  486. ftp_mkdir(ftpbuf_t *ftp, const char *dir, const size_t dir_len)
  487. {
  488. char *mkd, *end;
  489. zend_string *ret;
  490. if (ftp == NULL) {
  491. return NULL;
  492. }
  493. if (!ftp_putcmd(ftp, "MKD", sizeof("MKD")-1, dir, dir_len)) {
  494. return NULL;
  495. }
  496. if (!ftp_getresp(ftp) || ftp->resp != 257) {
  497. return NULL;
  498. }
  499. /* copy out the dir from response */
  500. if ((mkd = strchr(ftp->inbuf, '"')) == NULL) {
  501. return zend_string_init(dir, dir_len, 0);
  502. }
  503. if ((end = strrchr(++mkd, '"')) == NULL) {
  504. return NULL;
  505. }
  506. *end = 0;
  507. ret = zend_string_init(mkd, end - mkd, 0);
  508. *end = '"';
  509. return ret;
  510. }
  511. /* }}} */
  512. /* {{{ ftp_rmdir
  513. */
  514. int
  515. ftp_rmdir(ftpbuf_t *ftp, const char *dir, const size_t dir_len)
  516. {
  517. if (ftp == NULL) {
  518. return 0;
  519. }
  520. if (!ftp_putcmd(ftp, "RMD", sizeof("RMD")-1, dir, dir_len)) {
  521. return 0;
  522. }
  523. if (!ftp_getresp(ftp) || ftp->resp != 250) {
  524. return 0;
  525. }
  526. return 1;
  527. }
  528. /* }}} */
  529. /* {{{ ftp_chmod
  530. */
  531. int
  532. ftp_chmod(ftpbuf_t *ftp, const int mode, const char *filename, const int filename_len)
  533. {
  534. char *buffer;
  535. size_t buffer_len;
  536. if (ftp == NULL || filename_len <= 0) {
  537. return 0;
  538. }
  539. buffer_len = spprintf(&buffer, 0, "CHMOD %o %s", mode, filename);
  540. if (!buffer) {
  541. return 0;
  542. }
  543. if (!ftp_putcmd(ftp, "SITE", sizeof("SITE")-1, buffer, buffer_len)) {
  544. efree(buffer);
  545. return 0;
  546. }
  547. efree(buffer);
  548. if (!ftp_getresp(ftp) || ftp->resp != 200) {
  549. return 0;
  550. }
  551. return 1;
  552. }
  553. /* }}} */
  554. /* {{{ ftp_alloc
  555. */
  556. int
  557. ftp_alloc(ftpbuf_t *ftp, const zend_long size, zend_string **response)
  558. {
  559. char buffer[64];
  560. int buffer_len;
  561. if (ftp == NULL || size <= 0) {
  562. return 0;
  563. }
  564. buffer_len = snprintf(buffer, sizeof(buffer) - 1, ZEND_LONG_FMT, size);
  565. if (buffer_len < 0) {
  566. return 0;
  567. }
  568. if (!ftp_putcmd(ftp, "ALLO", sizeof("ALLO")-1, buffer, buffer_len)) {
  569. return 0;
  570. }
  571. if (!ftp_getresp(ftp)) {
  572. return 0;
  573. }
  574. if (response) {
  575. *response = zend_string_init(ftp->inbuf, strlen(ftp->inbuf), 0);
  576. }
  577. if (ftp->resp < 200 || ftp->resp >= 300) {
  578. return 0;
  579. }
  580. return 1;
  581. }
  582. /* }}} */
  583. /* {{{ ftp_nlist
  584. */
  585. char**
  586. ftp_nlist(ftpbuf_t *ftp, const char *path, const size_t path_len)
  587. {
  588. return ftp_genlist(ftp, "NLST", sizeof("NLST")-1, path, path_len);
  589. }
  590. /* }}} */
  591. /* {{{ ftp_list
  592. */
  593. char**
  594. ftp_list(ftpbuf_t *ftp, const char *path, const size_t path_len, int recursive)
  595. {
  596. return ftp_genlist(ftp, ((recursive) ? "LIST -R" : "LIST"), ((recursive) ? sizeof("LIST -R")-1 : sizeof("LIST")-1), path, path_len);
  597. }
  598. /* }}} */
  599. /* {{{ ftp_mlsd
  600. */
  601. char**
  602. ftp_mlsd(ftpbuf_t *ftp, const char *path, const size_t path_len)
  603. {
  604. return ftp_genlist(ftp, "MLSD", sizeof("MLSD")-1, path, path_len);
  605. }
  606. /* }}} */
  607. /* {{{ ftp_mlsd_parse_line
  608. */
  609. int
  610. ftp_mlsd_parse_line(HashTable *ht, const char *input) {
  611. zval zstr;
  612. const char *end = input + strlen(input);
  613. const char *sp = memchr(input, ' ', end - input);
  614. if (!sp) {
  615. php_error_docref(NULL, E_WARNING, "Missing pathname in MLSD response");
  616. return FAILURE;
  617. }
  618. /* Extract pathname */
  619. ZVAL_STRINGL(&zstr, sp + 1, end - sp - 1);
  620. zend_hash_str_update(ht, "name", sizeof("name")-1, &zstr);
  621. end = sp;
  622. while (input < end) {
  623. const char *semi, *eq;
  624. /* Find end of fact */
  625. semi = memchr(input, ';', end - input);
  626. if (!semi) {
  627. php_error_docref(NULL, E_WARNING, "Malformed fact in MLSD response");
  628. return FAILURE;
  629. }
  630. /* Separate fact key and value */
  631. eq = memchr(input, '=', semi - input);
  632. if (!eq) {
  633. php_error_docref(NULL, E_WARNING, "Malformed fact in MLSD response");
  634. return FAILURE;
  635. }
  636. ZVAL_STRINGL(&zstr, eq + 1, semi - eq - 1);
  637. zend_hash_str_update(ht, input, eq - input, &zstr);
  638. input = semi + 1;
  639. }
  640. return SUCCESS;
  641. }
  642. /* }}} */
  643. /* {{{ ftp_type
  644. */
  645. int
  646. ftp_type(ftpbuf_t *ftp, ftptype_t type)
  647. {
  648. const char *typechar;
  649. if (ftp == NULL) {
  650. return 0;
  651. }
  652. if (type == ftp->type) {
  653. return 1;
  654. }
  655. if (type == FTPTYPE_ASCII) {
  656. typechar = "A";
  657. } else if (type == FTPTYPE_IMAGE) {
  658. typechar = "I";
  659. } else {
  660. return 0;
  661. }
  662. if (!ftp_putcmd(ftp, "TYPE", sizeof("TYPE")-1, typechar, 1)) {
  663. return 0;
  664. }
  665. if (!ftp_getresp(ftp) || ftp->resp != 200) {
  666. return 0;
  667. }
  668. ftp->type = type;
  669. return 1;
  670. }
  671. /* }}} */
  672. /* {{{ ftp_pasv
  673. */
  674. int
  675. ftp_pasv(ftpbuf_t *ftp, int pasv)
  676. {
  677. char *ptr;
  678. union ipbox ipbox;
  679. unsigned long b[6];
  680. socklen_t n;
  681. struct sockaddr *sa;
  682. struct sockaddr_in *sin;
  683. if (ftp == NULL) {
  684. return 0;
  685. }
  686. if (pasv && ftp->pasv == 2) {
  687. return 1;
  688. }
  689. ftp->pasv = 0;
  690. if (!pasv) {
  691. return 1;
  692. }
  693. n = sizeof(ftp->pasvaddr);
  694. memset(&ftp->pasvaddr, 0, n);
  695. sa = (struct sockaddr *) &ftp->pasvaddr;
  696. if (getpeername(ftp->fd, sa, &n) < 0) {
  697. return 0;
  698. }
  699. #if HAVE_IPV6
  700. if (sa->sa_family == AF_INET6) {
  701. struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa;
  702. char *endptr, delimiter;
  703. /* try EPSV first */
  704. if (!ftp_putcmd(ftp, "EPSV", sizeof("EPSV")-1, NULL, (size_t) 0)) {
  705. return 0;
  706. }
  707. if (!ftp_getresp(ftp)) {
  708. return 0;
  709. }
  710. if (ftp->resp == 229) {
  711. /* parse out the port */
  712. for (ptr = ftp->inbuf; *ptr && *ptr != '('; ptr++);
  713. if (!*ptr) {
  714. return 0;
  715. }
  716. delimiter = *++ptr;
  717. for (n = 0; *ptr && n < 3; ptr++) {
  718. if (*ptr == delimiter) {
  719. n++;
  720. }
  721. }
  722. sin6->sin6_port = htons((unsigned short) strtoul(ptr, &endptr, 10));
  723. if (ptr == endptr || *endptr != delimiter) {
  724. return 0;
  725. }
  726. ftp->pasv = 2;
  727. return 1;
  728. }
  729. }
  730. /* fall back to PASV */
  731. #endif
  732. if (!ftp_putcmd(ftp, "PASV", sizeof("PASV")-1, NULL, (size_t) 0)) {
  733. return 0;
  734. }
  735. if (!ftp_getresp(ftp) || ftp->resp != 227) {
  736. return 0;
  737. }
  738. /* parse out the IP and port */
  739. for (ptr = ftp->inbuf; *ptr && !isdigit(*ptr); ptr++);
  740. n = sscanf(ptr, "%lu,%lu,%lu,%lu,%lu,%lu", &b[0], &b[1], &b[2], &b[3], &b[4], &b[5]);
  741. if (n != 6) {
  742. return 0;
  743. }
  744. for (n = 0; n < 6; n++) {
  745. ipbox.c[n] = (unsigned char) b[n];
  746. }
  747. sin = (struct sockaddr_in *) sa;
  748. if (ftp->usepasvaddress) {
  749. sin->sin_addr = ipbox.ia[0];
  750. }
  751. sin->sin_port = ipbox.s[2];
  752. ftp->pasv = 2;
  753. return 1;
  754. }
  755. /* }}} */
  756. /* {{{ ftp_get
  757. */
  758. int
  759. ftp_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, const size_t path_len, ftptype_t type, zend_long resumepos)
  760. {
  761. databuf_t *data = NULL;
  762. size_t rcvd;
  763. char arg[11];
  764. if (ftp == NULL) {
  765. return 0;
  766. }
  767. if (!ftp_type(ftp, type)) {
  768. goto bail;
  769. }
  770. if ((data = ftp_getdata(ftp)) == NULL) {
  771. goto bail;
  772. }
  773. ftp->data = data;
  774. if (resumepos > 0) {
  775. int arg_len = snprintf(arg, sizeof(arg), ZEND_LONG_FMT, resumepos);
  776. if (arg_len < 0) {
  777. goto bail;
  778. }
  779. if (!ftp_putcmd(ftp, "REST", sizeof("REST")-1, arg, arg_len)) {
  780. goto bail;
  781. }
  782. if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
  783. goto bail;
  784. }
  785. }
  786. if (!ftp_putcmd(ftp, "RETR", sizeof("RETR")-1, path, path_len)) {
  787. goto bail;
  788. }
  789. if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
  790. goto bail;
  791. }
  792. if ((data = data_accept(data, ftp)) == NULL) {
  793. goto bail;
  794. }
  795. while ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
  796. if (rcvd == (size_t)-1) {
  797. goto bail;
  798. }
  799. if (type == FTPTYPE_ASCII) {
  800. #ifndef PHP_WIN32
  801. char *s;
  802. #endif
  803. char *ptr = data->buf;
  804. char *e = ptr + rcvd;
  805. /* logic depends on the OS EOL
  806. * Win32 -> \r\n
  807. * Everything Else \n
  808. */
  809. #ifdef PHP_WIN32
  810. php_stream_write(outstream, ptr, (e - ptr));
  811. ptr = e;
  812. #else
  813. while (e > ptr && (s = memchr(ptr, '\r', (e - ptr)))) {
  814. php_stream_write(outstream, ptr, (s - ptr));
  815. if (*(s + 1) == '\n') {
  816. s++;
  817. php_stream_putc(outstream, '\n');
  818. }
  819. ptr = s + 1;
  820. }
  821. #endif
  822. if (ptr < e) {
  823. php_stream_write(outstream, ptr, (e - ptr));
  824. }
  825. } else if (rcvd != php_stream_write(outstream, data->buf, rcvd)) {
  826. goto bail;
  827. }
  828. }
  829. ftp->data = data = data_close(ftp, data);
  830. if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
  831. goto bail;
  832. }
  833. return 1;
  834. bail:
  835. ftp->data = data_close(ftp, data);
  836. return 0;
  837. }
  838. /* }}} */
  839. /* {{{ ftp_put
  840. */
  841. int
  842. ftp_put(ftpbuf_t *ftp, const char *path, const size_t path_len, php_stream *instream, ftptype_t type, zend_long startpos)
  843. {
  844. databuf_t *data = NULL;
  845. zend_long size;
  846. char *ptr;
  847. int ch;
  848. char arg[11];
  849. if (ftp == NULL) {
  850. return 0;
  851. }
  852. if (!ftp_type(ftp, type)) {
  853. goto bail;
  854. }
  855. if ((data = ftp_getdata(ftp)) == NULL) {
  856. goto bail;
  857. }
  858. ftp->data = data;
  859. if (startpos > 0) {
  860. int arg_len = snprintf(arg, sizeof(arg), ZEND_LONG_FMT, startpos);
  861. if (arg_len < 0) {
  862. goto bail;
  863. }
  864. if (!ftp_putcmd(ftp, "REST", sizeof("REST")-1, arg, arg_len)) {
  865. goto bail;
  866. }
  867. if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
  868. goto bail;
  869. }
  870. }
  871. if (!ftp_putcmd(ftp, "STOR", sizeof("STOR")-1, path, path_len)) {
  872. goto bail;
  873. }
  874. if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
  875. goto bail;
  876. }
  877. if ((data = data_accept(data, ftp)) == NULL) {
  878. goto bail;
  879. }
  880. size = 0;
  881. ptr = data->buf;
  882. while (!php_stream_eof(instream) && (ch = php_stream_getc(instream))!=EOF) {
  883. /* flush if necessary */
  884. if (FTP_BUFSIZE - size < 2) {
  885. if (my_send(ftp, data->fd, data->buf, size) != size) {
  886. goto bail;
  887. }
  888. ptr = data->buf;
  889. size = 0;
  890. }
  891. if (ch == '\n' && type == FTPTYPE_ASCII) {
  892. *ptr++ = '\r';
  893. size++;
  894. }
  895. *ptr++ = ch;
  896. size++;
  897. }
  898. if (size && my_send(ftp, data->fd, data->buf, size) != size) {
  899. goto bail;
  900. }
  901. ftp->data = data = data_close(ftp, data);
  902. if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250 && ftp->resp != 200)) {
  903. goto bail;
  904. }
  905. return 1;
  906. bail:
  907. ftp->data = data_close(ftp, data);
  908. return 0;
  909. }
  910. /* }}} */
  911. /* {{{ ftp_append
  912. */
  913. int
  914. ftp_append(ftpbuf_t *ftp, const char *path, const size_t path_len, php_stream *instream, ftptype_t type)
  915. {
  916. databuf_t *data = NULL;
  917. zend_long size;
  918. char *ptr;
  919. int ch;
  920. if (ftp == NULL) {
  921. return 0;
  922. }
  923. if (!ftp_type(ftp, type)) {
  924. goto bail;
  925. }
  926. if ((data = ftp_getdata(ftp)) == NULL) {
  927. goto bail;
  928. }
  929. ftp->data = data;
  930. if (!ftp_putcmd(ftp, "APPE", sizeof("APPE")-1, path, path_len)) {
  931. goto bail;
  932. }
  933. if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
  934. goto bail;
  935. }
  936. if ((data = data_accept(data, ftp)) == NULL) {
  937. goto bail;
  938. }
  939. size = 0;
  940. ptr = data->buf;
  941. while (!php_stream_eof(instream) && (ch = php_stream_getc(instream))!=EOF) {
  942. /* flush if necessary */
  943. if (FTP_BUFSIZE - size < 2) {
  944. if (my_send(ftp, data->fd, data->buf, size) != size) {
  945. goto bail;
  946. }
  947. ptr = data->buf;
  948. size = 0;
  949. }
  950. if (ch == '\n' && type == FTPTYPE_ASCII) {
  951. *ptr++ = '\r';
  952. size++;
  953. }
  954. *ptr++ = ch;
  955. size++;
  956. }
  957. if (size && my_send(ftp, data->fd, data->buf, size) != size) {
  958. goto bail;
  959. }
  960. ftp->data = data = data_close(ftp, data);
  961. if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250 && ftp->resp != 200)) {
  962. goto bail;
  963. }
  964. return 1;
  965. bail:
  966. ftp->data = data_close(ftp, data);
  967. return 0;
  968. }
  969. /* }}} */
  970. /* {{{ ftp_size
  971. */
  972. zend_long
  973. ftp_size(ftpbuf_t *ftp, const char *path, const size_t path_len)
  974. {
  975. if (ftp == NULL) {
  976. return -1;
  977. }
  978. if (!ftp_type(ftp, FTPTYPE_IMAGE)) {
  979. return -1;
  980. }
  981. if (!ftp_putcmd(ftp, "SIZE", sizeof("SIZE")-1, path, path_len)) {
  982. return -1;
  983. }
  984. if (!ftp_getresp(ftp) || ftp->resp != 213) {
  985. return -1;
  986. }
  987. return atol(ftp->inbuf);
  988. }
  989. /* }}} */
  990. /* {{{ ftp_mdtm
  991. */
  992. time_t
  993. ftp_mdtm(ftpbuf_t *ftp, const char *path, const size_t path_len)
  994. {
  995. time_t stamp;
  996. struct tm *gmt, tmbuf;
  997. struct tm tm;
  998. char *ptr;
  999. int n;
  1000. if (ftp == NULL) {
  1001. return -1;
  1002. }
  1003. if (!ftp_putcmd(ftp, "MDTM", sizeof("MDTM")-1, path, path_len)) {
  1004. return -1;
  1005. }
  1006. if (!ftp_getresp(ftp) || ftp->resp != 213) {
  1007. return -1;
  1008. }
  1009. /* parse out the timestamp */
  1010. for (ptr = ftp->inbuf; *ptr && !isdigit(*ptr); ptr++);
  1011. n = sscanf(ptr, "%4u%2u%2u%2u%2u%2u", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
  1012. if (n != 6) {
  1013. return -1;
  1014. }
  1015. tm.tm_year -= 1900;
  1016. tm.tm_mon--;
  1017. tm.tm_isdst = -1;
  1018. /* figure out the GMT offset */
  1019. stamp = time(NULL);
  1020. gmt = php_gmtime_r(&stamp, &tmbuf);
  1021. if (!gmt) {
  1022. return -1;
  1023. }
  1024. gmt->tm_isdst = -1;
  1025. /* apply the GMT offset */
  1026. tm.tm_sec += stamp - mktime(gmt);
  1027. tm.tm_isdst = gmt->tm_isdst;
  1028. stamp = mktime(&tm);
  1029. return stamp;
  1030. }
  1031. /* }}} */
  1032. /* {{{ ftp_delete
  1033. */
  1034. int
  1035. ftp_delete(ftpbuf_t *ftp, const char *path, const size_t path_len)
  1036. {
  1037. if (ftp == NULL) {
  1038. return 0;
  1039. }
  1040. if (!ftp_putcmd(ftp, "DELE", sizeof("DELE")-1, path, path_len)) {
  1041. return 0;
  1042. }
  1043. if (!ftp_getresp(ftp) || ftp->resp != 250) {
  1044. return 0;
  1045. }
  1046. return 1;
  1047. }
  1048. /* }}} */
  1049. /* {{{ ftp_rename
  1050. */
  1051. int
  1052. ftp_rename(ftpbuf_t *ftp, const char *src, const size_t src_len, const char *dest, const size_t dest_len)
  1053. {
  1054. if (ftp == NULL) {
  1055. return 0;
  1056. }
  1057. if (!ftp_putcmd(ftp, "RNFR", sizeof("RNFR")-1, src, src_len)) {
  1058. return 0;
  1059. }
  1060. if (!ftp_getresp(ftp) || ftp->resp != 350) {
  1061. return 0;
  1062. }
  1063. if (!ftp_putcmd(ftp, "RNTO", sizeof("RNTO")-1, dest, dest_len)) {
  1064. return 0;
  1065. }
  1066. if (!ftp_getresp(ftp) || ftp->resp != 250) {
  1067. return 0;
  1068. }
  1069. return 1;
  1070. }
  1071. /* }}} */
  1072. /* {{{ ftp_site
  1073. */
  1074. int
  1075. ftp_site(ftpbuf_t *ftp, const char *cmd, const size_t cmd_len)
  1076. {
  1077. if (ftp == NULL) {
  1078. return 0;
  1079. }
  1080. if (!ftp_putcmd(ftp, "SITE", sizeof("SITE")-1, cmd, cmd_len)) {
  1081. return 0;
  1082. }
  1083. if (!ftp_getresp(ftp) || ftp->resp < 200 || ftp->resp >= 300) {
  1084. return 0;
  1085. }
  1086. return 1;
  1087. }
  1088. /* }}} */
  1089. /* static functions */
  1090. /* {{{ ftp_putcmd
  1091. */
  1092. int
  1093. ftp_putcmd(ftpbuf_t *ftp, const char *cmd, const size_t cmd_len, const char *args, const size_t args_len)
  1094. {
  1095. int size;
  1096. char *data;
  1097. if (strpbrk(cmd, "\r\n")) {
  1098. return 0;
  1099. }
  1100. /* build the output buffer */
  1101. if (args && args[0]) {
  1102. /* "cmd args\r\n\0" */
  1103. if (cmd_len + args_len + 4 > FTP_BUFSIZE) {
  1104. return 0;
  1105. }
  1106. if (strpbrk(args, "\r\n")) {
  1107. return 0;
  1108. }
  1109. size = slprintf(ftp->outbuf, sizeof(ftp->outbuf), "%s %s\r\n", cmd, args);
  1110. } else {
  1111. /* "cmd\r\n\0" */
  1112. if (cmd_len + 3 > FTP_BUFSIZE) {
  1113. return 0;
  1114. }
  1115. size = slprintf(ftp->outbuf, sizeof(ftp->outbuf), "%s\r\n", cmd);
  1116. }
  1117. data = ftp->outbuf;
  1118. /* Clear the extra-lines buffer */
  1119. ftp->extra = NULL;
  1120. if (my_send(ftp, ftp->fd, data, size) != size) {
  1121. return 0;
  1122. }
  1123. return 1;
  1124. }
  1125. /* }}} */
  1126. /* {{{ ftp_readline
  1127. */
  1128. int
  1129. ftp_readline(ftpbuf_t *ftp)
  1130. {
  1131. long size, rcvd;
  1132. char *data, *eol;
  1133. /* shift the extra to the front */
  1134. size = FTP_BUFSIZE;
  1135. rcvd = 0;
  1136. if (ftp->extra) {
  1137. memmove(ftp->inbuf, ftp->extra, ftp->extralen);
  1138. rcvd = ftp->extralen;
  1139. }
  1140. data = ftp->inbuf;
  1141. do {
  1142. size -= rcvd;
  1143. for (eol = data; rcvd; rcvd--, eol++) {
  1144. if (*eol == '\r') {
  1145. *eol = 0;
  1146. ftp->extra = eol + 1;
  1147. if (rcvd > 1 && *(eol + 1) == '\n') {
  1148. ftp->extra++;
  1149. rcvd--;
  1150. }
  1151. if ((ftp->extralen = --rcvd) == 0) {
  1152. ftp->extra = NULL;
  1153. }
  1154. return 1;
  1155. } else if (*eol == '\n') {
  1156. *eol = 0;
  1157. ftp->extra = eol + 1;
  1158. if ((ftp->extralen = --rcvd) == 0) {
  1159. ftp->extra = NULL;
  1160. }
  1161. return 1;
  1162. }
  1163. }
  1164. data = eol;
  1165. if ((rcvd = my_recv(ftp, ftp->fd, data, size)) < 1) {
  1166. return 0;
  1167. }
  1168. } while (size);
  1169. return 0;
  1170. }
  1171. /* }}} */
  1172. /* {{{ ftp_getresp
  1173. */
  1174. int
  1175. ftp_getresp(ftpbuf_t *ftp)
  1176. {
  1177. if (ftp == NULL) {
  1178. return 0;
  1179. }
  1180. ftp->resp = 0;
  1181. while (1) {
  1182. if (!ftp_readline(ftp)) {
  1183. return 0;
  1184. }
  1185. /* Break out when the end-tag is found */
  1186. if (isdigit(ftp->inbuf[0]) && isdigit(ftp->inbuf[1]) && isdigit(ftp->inbuf[2]) && ftp->inbuf[3] == ' ') {
  1187. break;
  1188. }
  1189. }
  1190. /* translate the tag */
  1191. if (!isdigit(ftp->inbuf[0]) || !isdigit(ftp->inbuf[1]) || !isdigit(ftp->inbuf[2])) {
  1192. return 0;
  1193. }
  1194. ftp->resp = 100 * (ftp->inbuf[0] - '0') + 10 * (ftp->inbuf[1] - '0') + (ftp->inbuf[2] - '0');
  1195. memmove(ftp->inbuf, ftp->inbuf + 4, FTP_BUFSIZE - 4);
  1196. if (ftp->extra) {
  1197. ftp->extra -= 4;
  1198. }
  1199. return 1;
  1200. }
  1201. /* }}} */
  1202. int single_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t size) {
  1203. #ifdef HAVE_FTP_SSL
  1204. int err;
  1205. zend_bool retry = 0;
  1206. SSL *handle = NULL;
  1207. php_socket_t fd;
  1208. size_t sent;
  1209. if (ftp->use_ssl && ftp->fd == s && ftp->ssl_active) {
  1210. handle = ftp->ssl_handle;
  1211. fd = ftp->fd;
  1212. } else if (ftp->use_ssl && ftp->fd != s && ftp->use_ssl_for_data && ftp->data->ssl_active) {
  1213. handle = ftp->data->ssl_handle;
  1214. fd = ftp->data->fd;
  1215. } else {
  1216. return send(s, buf, size, 0);
  1217. }
  1218. do {
  1219. sent = SSL_write(handle, buf, size);
  1220. err = SSL_get_error(handle, sent);
  1221. switch (err) {
  1222. case SSL_ERROR_NONE:
  1223. retry = 0;
  1224. break;
  1225. case SSL_ERROR_ZERO_RETURN:
  1226. retry = 0;
  1227. SSL_shutdown(handle);
  1228. break;
  1229. case SSL_ERROR_WANT_READ:
  1230. case SSL_ERROR_WANT_CONNECT: {
  1231. php_pollfd p;
  1232. int i;
  1233. p.fd = fd;
  1234. p.events = POLLOUT;
  1235. p.revents = 0;
  1236. i = php_poll2(&p, 1, 300);
  1237. retry = i > 0;
  1238. }
  1239. break;
  1240. default:
  1241. php_error_docref(NULL, E_WARNING, "SSL write failed");
  1242. return -1;
  1243. }
  1244. } while (retry);
  1245. return sent;
  1246. #else
  1247. return send(s, buf, size, 0);
  1248. #endif
  1249. }
  1250. /* {{{ my_send
  1251. */
  1252. int
  1253. my_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len)
  1254. {
  1255. zend_long size, sent;
  1256. int n;
  1257. size = len;
  1258. while (size) {
  1259. n = php_pollfd_for_ms(s, POLLOUT, ftp->timeout_sec * 1000);
  1260. if (n < 1) {
  1261. #ifdef PHP_WIN32
  1262. if (n == 0) {
  1263. _set_errno(ETIMEDOUT);
  1264. }
  1265. #else
  1266. if (n == 0) {
  1267. errno = ETIMEDOUT;
  1268. }
  1269. #endif
  1270. return -1;
  1271. }
  1272. sent = single_send(ftp, s, buf, size);
  1273. if (sent == -1) {
  1274. return -1;
  1275. }
  1276. buf = (char*) buf + sent;
  1277. size -= sent;
  1278. }
  1279. return len;
  1280. }
  1281. /* }}} */
  1282. /* {{{ my_recv
  1283. */
  1284. int
  1285. my_recv(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len)
  1286. {
  1287. int n, nr_bytes;
  1288. #ifdef HAVE_FTP_SSL
  1289. int err;
  1290. zend_bool retry = 0;
  1291. SSL *handle = NULL;
  1292. php_socket_t fd;
  1293. #endif
  1294. n = php_pollfd_for_ms(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000);
  1295. if (n < 1) {
  1296. #ifdef PHP_WIN32
  1297. if (n == 0) {
  1298. _set_errno(ETIMEDOUT);
  1299. }
  1300. #else
  1301. if (n == 0) {
  1302. errno = ETIMEDOUT;
  1303. }
  1304. #endif
  1305. return -1;
  1306. }
  1307. #ifdef HAVE_FTP_SSL
  1308. if (ftp->use_ssl && ftp->fd == s && ftp->ssl_active) {
  1309. handle = ftp->ssl_handle;
  1310. fd = ftp->fd;
  1311. } else if (ftp->use_ssl && ftp->fd != s && ftp->use_ssl_for_data && ftp->data->ssl_active) {
  1312. handle = ftp->data->ssl_handle;
  1313. fd = ftp->data->fd;
  1314. }
  1315. if (handle) {
  1316. do {
  1317. nr_bytes = SSL_read(handle, buf, len);
  1318. err = SSL_get_error(handle, nr_bytes);
  1319. switch (err) {
  1320. case SSL_ERROR_NONE:
  1321. retry = 0;
  1322. break;
  1323. case SSL_ERROR_ZERO_RETURN:
  1324. retry = 0;
  1325. SSL_shutdown(handle);
  1326. break;
  1327. case SSL_ERROR_WANT_READ:
  1328. case SSL_ERROR_WANT_CONNECT: {
  1329. php_pollfd p;
  1330. int i;
  1331. p.fd = fd;
  1332. p.events = POLLIN|POLLPRI;
  1333. p.revents = 0;
  1334. i = php_poll2(&p, 1, 300);
  1335. retry = i > 0;
  1336. }
  1337. break;
  1338. default:
  1339. php_error_docref(NULL, E_WARNING, "SSL read failed");
  1340. return -1;
  1341. }
  1342. } while (retry);
  1343. } else {
  1344. #endif
  1345. nr_bytes = recv(s, buf, len, 0);
  1346. #ifdef HAVE_FTP_SSL
  1347. }
  1348. #endif
  1349. return (nr_bytes);
  1350. }
  1351. /* }}} */
  1352. /* {{{ data_available
  1353. */
  1354. int
  1355. data_available(ftpbuf_t *ftp, php_socket_t s)
  1356. {
  1357. int n;
  1358. n = php_pollfd_for_ms(s, PHP_POLLREADABLE, 1000);
  1359. if (n < 1) {
  1360. #ifdef PHP_WIN32
  1361. if (n == 0) {
  1362. _set_errno(ETIMEDOUT);
  1363. }
  1364. #else
  1365. if (n == 0) {
  1366. errno = ETIMEDOUT;
  1367. }
  1368. #endif
  1369. return 0;
  1370. }
  1371. return 1;
  1372. }
  1373. /* }}} */
  1374. /* {{{ data_writeable
  1375. */
  1376. int
  1377. data_writeable(ftpbuf_t *ftp, php_socket_t s)
  1378. {
  1379. int n;
  1380. n = php_pollfd_for_ms(s, POLLOUT, 1000);
  1381. if (n < 1) {
  1382. #ifdef PHP_WIN32
  1383. if (n == 0) {
  1384. _set_errno(ETIMEDOUT);
  1385. }
  1386. #else
  1387. if (n == 0) {
  1388. errno = ETIMEDOUT;
  1389. }
  1390. #endif
  1391. return 0;
  1392. }
  1393. return 1;
  1394. }
  1395. /* }}} */
  1396. /* {{{ my_accept
  1397. */
  1398. int
  1399. my_accept(ftpbuf_t *ftp, php_socket_t s, struct sockaddr *addr, socklen_t *addrlen)
  1400. {
  1401. int n;
  1402. n = php_pollfd_for_ms(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000);
  1403. if (n < 1) {
  1404. #ifdef PHP_WIN32
  1405. if (n == 0) {
  1406. _set_errno(ETIMEDOUT);
  1407. }
  1408. #else
  1409. if (n == 0) {
  1410. errno = ETIMEDOUT;
  1411. }
  1412. #endif
  1413. return -1;
  1414. }
  1415. return accept(s, addr, addrlen);
  1416. }
  1417. /* }}} */
  1418. /* {{{ ftp_getdata
  1419. */
  1420. databuf_t*
  1421. ftp_getdata(ftpbuf_t *ftp)
  1422. {
  1423. int fd = -1;
  1424. databuf_t *data;
  1425. php_sockaddr_storage addr;
  1426. struct sockaddr *sa;
  1427. socklen_t size;
  1428. union ipbox ipbox;
  1429. char arg[sizeof("255, 255, 255, 255, 255, 255")];
  1430. struct timeval tv;
  1431. int arg_len;
  1432. /* ask for a passive connection if we need one */
  1433. if (ftp->pasv && !ftp_pasv(ftp, 1)) {
  1434. return NULL;
  1435. }
  1436. /* alloc the data structure */
  1437. data = ecalloc(1, sizeof(*data));
  1438. data->listener = -1;
  1439. data->fd = -1;
  1440. data->type = ftp->type;
  1441. sa = (struct sockaddr *) &ftp->localaddr;
  1442. /* bind/listen */
  1443. if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) == SOCK_ERR) {
  1444. php_error_docref(NULL, E_WARNING, "socket() failed: %s (%d)", strerror(errno), errno);
  1445. goto bail;
  1446. }
  1447. /* passive connection handler */
  1448. if (ftp->pasv) {
  1449. /* clear the ready status */
  1450. ftp->pasv = 1;
  1451. /* connect */
  1452. /* Win 95/98 seems not to like size > sizeof(sockaddr_in) */
  1453. size = php_sockaddr_size(&ftp->pasvaddr);
  1454. tv.tv_sec = ftp->timeout_sec;
  1455. tv.tv_usec = 0;
  1456. if (php_connect_nonb(fd, (struct sockaddr*) &ftp->pasvaddr, size, &tv) == -1) {
  1457. php_error_docref(NULL, E_WARNING, "php_connect_nonb() failed: %s (%d)", strerror(errno), errno);
  1458. goto bail;
  1459. }
  1460. data->fd = fd;
  1461. ftp->data = data;
  1462. return data;
  1463. }
  1464. /* active (normal) connection */
  1465. /* bind to a local address */
  1466. php_any_addr(sa->sa_family, &addr, 0);
  1467. size = php_sockaddr_size(&addr);
  1468. if (bind(fd, (struct sockaddr*) &addr, size) != 0) {
  1469. php_error_docref(NULL, E_WARNING, "bind() failed: %s (%d)", strerror(errno), errno);
  1470. goto bail;
  1471. }
  1472. if (getsockname(fd, (struct sockaddr*) &addr, &size) != 0) {
  1473. php_error_docref(NULL, E_WARNING, "getsockname() failed: %s (%d)", strerror(errno), errno);
  1474. goto bail;
  1475. }
  1476. if (listen(fd, 5) != 0) {
  1477. php_error_docref(NULL, E_WARNING, "listen() failed: %s (%d)", strerror(errno), errno);
  1478. goto bail;
  1479. }
  1480. data->listener = fd;
  1481. #if HAVE_IPV6 && HAVE_INET_NTOP
  1482. if (sa->sa_family == AF_INET6) {
  1483. /* need to use EPRT */
  1484. char eprtarg[INET6_ADDRSTRLEN + sizeof("|x||xxxxx|")];
  1485. char out[INET6_ADDRSTRLEN];
  1486. int eprtarg_len;
  1487. inet_ntop(AF_INET6, &((struct sockaddr_in6*) sa)->sin6_addr, out, sizeof(out));
  1488. eprtarg_len = snprintf(eprtarg, sizeof(eprtarg), "|2|%s|%hu|", out, ntohs(((struct sockaddr_in6 *) &addr)->sin6_port));
  1489. if (eprtarg_len < 0) {
  1490. goto bail;
  1491. }
  1492. if (!ftp_putcmd(ftp, "EPRT", sizeof("EPRT")-1, eprtarg, eprtarg_len)) {
  1493. goto bail;
  1494. }
  1495. if (!ftp_getresp(ftp) || ftp->resp != 200) {
  1496. goto bail;
  1497. }
  1498. ftp->data = data;
  1499. return data;
  1500. }
  1501. #endif
  1502. /* send the PORT */
  1503. ipbox.ia[0] = ((struct sockaddr_in*) sa)->sin_addr;
  1504. ipbox.s[2] = ((struct sockaddr_in*) &addr)->sin_port;
  1505. arg_len = snprintf(arg, sizeof(arg), "%u,%u,%u,%u,%u,%u", ipbox.c[0], ipbox.c[1], ipbox.c[2], ipbox.c[3], ipbox.c[4], ipbox.c[5]);
  1506. if (arg_len < 0) {
  1507. goto bail;
  1508. }
  1509. if (!ftp_putcmd(ftp, "PORT", sizeof("PORT")-1, arg, arg_len)) {
  1510. goto bail;
  1511. }
  1512. if (!ftp_getresp(ftp) || ftp->resp != 200) {
  1513. goto bail;
  1514. }
  1515. ftp->data = data;
  1516. return data;
  1517. bail:
  1518. if (fd != -1) {
  1519. closesocket(fd);
  1520. }
  1521. efree(data);
  1522. return NULL;
  1523. }
  1524. /* }}} */
  1525. /* {{{ data_accept
  1526. */
  1527. databuf_t*
  1528. data_accept(databuf_t *data, ftpbuf_t *ftp)
  1529. {
  1530. php_sockaddr_storage addr;
  1531. socklen_t size;
  1532. #ifdef HAVE_FTP_SSL
  1533. SSL_CTX *ctx;
  1534. SSL_SESSION *session;
  1535. int err, res;
  1536. zend_bool retry;
  1537. #endif
  1538. if (data->fd != -1) {
  1539. goto data_accepted;
  1540. }
  1541. size = sizeof(addr);
  1542. data->fd = my_accept(ftp, data->listener, (struct sockaddr*) &addr, &size);
  1543. closesocket(data->listener);
  1544. data->listener = -1;
  1545. if (data->fd == -1) {
  1546. efree(data);
  1547. return NULL;
  1548. }
  1549. data_accepted:
  1550. #ifdef HAVE_FTP_SSL
  1551. /* now enable ssl if we need to */
  1552. if (ftp->use_ssl && ftp->use_ssl_for_data) {
  1553. ctx = SSL_get_SSL_CTX(ftp->ssl_handle);
  1554. if (ctx == NULL) {
  1555. php_error_docref(NULL, E_WARNING, "data_accept: failed to retrieve the existing SSL context");
  1556. return 0;
  1557. }
  1558. data->ssl_handle = SSL_new(ctx);
  1559. if (data->ssl_handle == NULL) {
  1560. php_error_docref(NULL, E_WARNING, "data_accept: failed to create the SSL handle");
  1561. return 0;
  1562. }
  1563. SSL_set_fd(data->ssl_handle, data->fd);
  1564. if (ftp->old_ssl) {
  1565. SSL_copy_session_id(data->ssl_handle, ftp->ssl_handle);
  1566. }
  1567. /* get the session from the control connection so we can re-use it */
  1568. session = SSL_get_session(ftp->ssl_handle);
  1569. if (session == NULL) {
  1570. php_error_docref(NULL, E_WARNING, "data_accept: failed to retrieve the existing SSL session");
  1571. SSL_free(data->ssl_handle);
  1572. return 0;
  1573. }
  1574. /* and set it on the data connection */
  1575. res = SSL_set_session(data->ssl_handle, session);
  1576. if (res == 0) {
  1577. php_error_docref(NULL, E_WARNING, "data_accept: failed to set the existing SSL session");
  1578. SSL_free(data->ssl_handle);
  1579. return 0;
  1580. }
  1581. do {
  1582. res = SSL_connect(data->ssl_handle);
  1583. err = SSL_get_error(data->ssl_handle, res);
  1584. switch (err) {
  1585. case SSL_ERROR_NONE:
  1586. retry = 0;
  1587. break;
  1588. case SSL_ERROR_ZERO_RETURN:
  1589. retry = 0;
  1590. SSL_shutdown(data->ssl_handle);
  1591. break;
  1592. case SSL_ERROR_WANT_READ:
  1593. case SSL_ERROR_WANT_WRITE: {
  1594. php_pollfd p;
  1595. int i;
  1596. p.fd = data->fd;
  1597. p.events = (err == SSL_ERROR_WANT_READ) ? (POLLIN|POLLPRI) : POLLOUT;
  1598. p.revents = 0;
  1599. i = php_poll2(&p, 1, 300);
  1600. retry = i > 0;
  1601. }
  1602. break;
  1603. default:
  1604. php_error_docref(NULL, E_WARNING, "data_accept: SSL/TLS handshake failed");
  1605. SSL_shutdown(data->ssl_handle);
  1606. SSL_free(data->ssl_handle);
  1607. return 0;
  1608. }
  1609. } while (retry);
  1610. data->ssl_active = 1;
  1611. }
  1612. #endif
  1613. return data;
  1614. }
  1615. /* }}} */
  1616. /* {{{ ftp_ssl_shutdown
  1617. */
  1618. #ifdef HAVE_FTP_SSL
  1619. static void ftp_ssl_shutdown(ftpbuf_t *ftp, php_socket_t fd, SSL *ssl_handle) {
  1620. /* In TLS 1.3 it's common to receive session tickets after the handshake has completed. We need to train
  1621. the socket (read the tickets until EOF/close_notify alert) before closing the socket. Otherwise the
  1622. server might get an ECONNRESET which might lead to data truncation on server side.
  1623. */
  1624. char buf[256]; /* We will use this for the OpenSSL error buffer, so it has
  1625. to be at least 256 bytes long.*/
  1626. int done = 1, err, nread;
  1627. unsigned long sslerror;
  1628. err = SSL_shutdown(ssl_handle);
  1629. if (err < 0) {
  1630. php_error_docref(NULL, E_WARNING, "SSL_shutdown failed");
  1631. }
  1632. else if (err == 0) {
  1633. /* The shutdown is not yet finished. Call SSL_read() to do a bidirectional shutdown. */
  1634. done = 0;
  1635. }
  1636. while (!done && data_available(ftp, fd)) {
  1637. ERR_clear_error();
  1638. nread = SSL_read(ssl_handle, buf, sizeof(buf));
  1639. if (nread <= 0) {
  1640. err = SSL_get_error(ssl_handle, nread);
  1641. switch (err) {
  1642. case SSL_ERROR_NONE: /* this is not an error */
  1643. case SSL_ERROR_ZERO_RETURN: /* no more data */
  1644. /* This is the expected response. There was no data but only
  1645. the close notify alert */
  1646. done = 1;
  1647. break;
  1648. case SSL_ERROR_WANT_READ:
  1649. /* there's data pending, re-invoke SSL_read() */
  1650. break;
  1651. case SSL_ERROR_WANT_WRITE:
  1652. /* SSL wants a write. Really odd. Let's bail out. */
  1653. done = 1;
  1654. break;
  1655. default:
  1656. if ((sslerror = ERR_get_error())) {
  1657. ERR_error_string_n(sslerror, buf, sizeof(buf));
  1658. php_error_docref(NULL, E_WARNING, "SSL_read on shutdown: %s", buf);
  1659. } else if (errno) {
  1660. php_error_docref(NULL, E_WARNING, "SSL_read on shutdown: %s (%d)", strerror(errno), errno);
  1661. }
  1662. done = 1;
  1663. break;
  1664. }
  1665. }
  1666. }
  1667. (void)SSL_free(ssl_handle);
  1668. }
  1669. #endif
  1670. /* }}} */
  1671. /* {{{ data_close
  1672. */
  1673. databuf_t*
  1674. data_close(ftpbuf_t *ftp, databuf_t *data)
  1675. {
  1676. if (data == NULL) {
  1677. return NULL;
  1678. }
  1679. if (data->listener != -1) {
  1680. #ifdef HAVE_FTP_SSL
  1681. if (data->ssl_active) {
  1682. /* don't free the data context, it's the same as the control */
  1683. ftp_ssl_shutdown(ftp, data->listener, data->ssl_handle);
  1684. data->ssl_active = 0;
  1685. }
  1686. #endif
  1687. closesocket(data->listener);
  1688. }
  1689. if (data->fd != -1) {
  1690. #ifdef HAVE_FTP_SSL
  1691. if (data->ssl_active) {
  1692. /* don't free the data context, it's the same as the control */
  1693. ftp_ssl_shutdown(ftp, data->fd, data->ssl_handle);
  1694. data->ssl_active = 0;
  1695. }
  1696. #endif
  1697. closesocket(data->fd);
  1698. }
  1699. if (ftp) {
  1700. ftp->data = NULL;
  1701. }
  1702. efree(data);
  1703. return NULL;
  1704. }
  1705. /* }}} */
  1706. /* {{{ ftp_genlist
  1707. */
  1708. char**
  1709. ftp_genlist(ftpbuf_t *ftp, const char *cmd, const size_t cmd_len, const char *path, const size_t path_len)
  1710. {
  1711. php_stream *tmpstream = NULL;
  1712. databuf_t *data = NULL;
  1713. char *ptr;
  1714. int ch, lastch;
  1715. size_t size, rcvd;
  1716. size_t lines;
  1717. char **ret = NULL;
  1718. char **entry;
  1719. char *text;
  1720. if ((tmpstream = php_stream_fopen_tmpfile()) == NULL) {
  1721. php_error_docref(NULL, E_WARNING, "Unable to create temporary file. Check permissions in temporary files directory.");
  1722. return NULL;
  1723. }
  1724. if (!ftp_type(ftp, FTPTYPE_ASCII)) {
  1725. goto bail;
  1726. }
  1727. if ((data = ftp_getdata(ftp)) == NULL) {
  1728. goto bail;
  1729. }
  1730. ftp->data = data;
  1731. if (!ftp_putcmd(ftp, cmd, cmd_len, path, path_len)) {
  1732. goto bail;
  1733. }
  1734. if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125 && ftp->resp != 226)) {
  1735. goto bail;
  1736. }
  1737. /* some servers don't open a ftp-data connection if the directory is empty */
  1738. if (ftp->resp == 226) {
  1739. ftp->data = data_close(ftp, data);
  1740. php_stream_close(tmpstream);
  1741. return ecalloc(1, sizeof(char*));
  1742. }
  1743. /* pull data buffer into tmpfile */
  1744. if ((data = data_accept(data, ftp)) == NULL) {
  1745. goto bail;
  1746. }
  1747. size = 0;
  1748. lines = 0;
  1749. lastch = 0;
  1750. while ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
  1751. if (rcvd == (size_t)-1 || rcvd > ((size_t)(-1))-size) {
  1752. goto bail;
  1753. }
  1754. php_stream_write(tmpstream, data->buf, rcvd);
  1755. size += rcvd;
  1756. for (ptr = data->buf; rcvd; rcvd--, ptr++) {
  1757. if (*ptr == '\n' && lastch == '\r') {
  1758. lines++;
  1759. }
  1760. lastch = *ptr;
  1761. }
  1762. }
  1763. ftp->data = data_close(ftp, data);
  1764. php_stream_rewind(tmpstream);
  1765. ret = safe_emalloc((lines + 1), sizeof(char*), size);
  1766. entry = ret;
  1767. text = (char*) (ret + lines + 1);
  1768. *entry = text;
  1769. lastch = 0;
  1770. while ((ch = php_stream_getc(tmpstream)) != EOF) {
  1771. if (ch == '\n' && lastch == '\r') {
  1772. *(text - 1) = 0;
  1773. *++entry = text;
  1774. } else {
  1775. *text++ = ch;
  1776. }
  1777. lastch = ch;
  1778. }
  1779. *entry = NULL;
  1780. php_stream_close(tmpstream);
  1781. if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
  1782. efree(ret);
  1783. return NULL;
  1784. }
  1785. return ret;
  1786. bail:
  1787. ftp->data = data_close(ftp, data);
  1788. php_stream_close(tmpstream);
  1789. if (ret)
  1790. efree(ret);
  1791. return NULL;
  1792. }
  1793. /* }}} */
  1794. /* {{{ ftp_nb_get
  1795. */
  1796. int
  1797. ftp_nb_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, const size_t path_len, ftptype_t type, zend_long resumepos)
  1798. {
  1799. databuf_t *data = NULL;
  1800. char arg[11];
  1801. if (ftp == NULL) {
  1802. return PHP_FTP_FAILED;
  1803. }
  1804. if (!ftp_type(ftp, type)) {
  1805. goto bail;
  1806. }
  1807. if ((data = ftp_getdata(ftp)) == NULL) {
  1808. goto bail;
  1809. }
  1810. if (resumepos>0) {
  1811. int arg_len = snprintf(arg, sizeof(arg), ZEND_LONG_FMT, resumepos);
  1812. if (arg_len < 0) {
  1813. goto bail;
  1814. }
  1815. if (!ftp_putcmd(ftp, "REST", sizeof("REST")-1, arg, arg_len)) {
  1816. goto bail;
  1817. }
  1818. if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
  1819. goto bail;
  1820. }
  1821. }
  1822. if (!ftp_putcmd(ftp, "RETR", sizeof("RETR")-1, path, path_len)) {
  1823. goto bail;
  1824. }
  1825. if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
  1826. goto bail;
  1827. }
  1828. if ((data = data_accept(data, ftp)) == NULL) {
  1829. goto bail;
  1830. }
  1831. ftp->data = data;
  1832. ftp->stream = outstream;
  1833. ftp->lastch = 0;
  1834. ftp->nb = 1;
  1835. return (ftp_nb_continue_read(ftp));
  1836. bail:
  1837. ftp->data = data_close(ftp, data);
  1838. return PHP_FTP_FAILED;
  1839. }
  1840. /* }}} */
  1841. /* {{{ ftp_nb_continue_read
  1842. */
  1843. int
  1844. ftp_nb_continue_read(ftpbuf_t *ftp)
  1845. {
  1846. databuf_t *data = NULL;
  1847. char *ptr;
  1848. int lastch;
  1849. size_t rcvd;
  1850. ftptype_t type;
  1851. data = ftp->data;
  1852. /* check if there is already more data */
  1853. if (!data_available(ftp, data->fd)) {
  1854. return PHP_FTP_MOREDATA;
  1855. }
  1856. type = ftp->type;
  1857. lastch = ftp->lastch;
  1858. if ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
  1859. if (rcvd == (size_t)-1) {
  1860. goto bail;
  1861. }
  1862. if (type == FTPTYPE_ASCII) {
  1863. for (ptr = data->buf; rcvd; rcvd--, ptr++) {
  1864. if (lastch == '\r' && *ptr != '\n') {
  1865. php_stream_putc(ftp->stream, '\r');
  1866. }
  1867. if (*ptr != '\r') {
  1868. php_stream_putc(ftp->stream, *ptr);
  1869. }
  1870. lastch = *ptr;
  1871. }
  1872. } else if (rcvd != php_stream_write(ftp->stream, data->buf, rcvd)) {
  1873. goto bail;
  1874. }
  1875. ftp->lastch = lastch;
  1876. return PHP_FTP_MOREDATA;
  1877. }
  1878. if (type == FTPTYPE_ASCII && lastch == '\r') {
  1879. php_stream_putc(ftp->stream, '\r');
  1880. }
  1881. ftp->data = data = data_close(ftp, data);
  1882. if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
  1883. goto bail;
  1884. }
  1885. ftp->nb = 0;
  1886. return PHP_FTP_FINISHED;
  1887. bail:
  1888. ftp->nb = 0;
  1889. ftp->data = data_close(ftp, data);
  1890. return PHP_FTP_FAILED;
  1891. }
  1892. /* }}} */
  1893. /* {{{ ftp_nb_put
  1894. */
  1895. int
  1896. ftp_nb_put(ftpbuf_t *ftp, const char *path, const size_t path_len, php_stream *instream, ftptype_t type, zend_long startpos)
  1897. {
  1898. databuf_t *data = NULL;
  1899. char arg[11];
  1900. if (ftp == NULL) {
  1901. return 0;
  1902. }
  1903. if (!ftp_type(ftp, type)) {
  1904. goto bail;
  1905. }
  1906. if ((data = ftp_getdata(ftp)) == NULL) {
  1907. goto bail;
  1908. }
  1909. if (startpos > 0) {
  1910. int arg_len = snprintf(arg, sizeof(arg), ZEND_LONG_FMT, startpos);
  1911. if (arg_len < 0) {
  1912. goto bail;
  1913. }
  1914. if (!ftp_putcmd(ftp, "REST", sizeof("REST")-1, arg, arg_len)) {
  1915. goto bail;
  1916. }
  1917. if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
  1918. goto bail;
  1919. }
  1920. }
  1921. if (!ftp_putcmd(ftp, "STOR", sizeof("STOR")-1, path, path_len)) {
  1922. goto bail;
  1923. }
  1924. if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
  1925. goto bail;
  1926. }
  1927. if ((data = data_accept(data, ftp)) == NULL) {
  1928. goto bail;
  1929. }
  1930. ftp->data = data;
  1931. ftp->stream = instream;
  1932. ftp->lastch = 0;
  1933. ftp->nb = 1;
  1934. return (ftp_nb_continue_write(ftp));
  1935. bail:
  1936. ftp->data = data_close(ftp, data);
  1937. return PHP_FTP_FAILED;
  1938. }
  1939. /* }}} */
  1940. /* {{{ ftp_nb_continue_write
  1941. */
  1942. int
  1943. ftp_nb_continue_write(ftpbuf_t *ftp)
  1944. {
  1945. long size;
  1946. char *ptr;
  1947. int ch;
  1948. /* check if we can write more data */
  1949. if (!data_writeable(ftp, ftp->data->fd)) {
  1950. return PHP_FTP_MOREDATA;
  1951. }
  1952. size = 0;
  1953. ptr = ftp->data->buf;
  1954. while (!php_stream_eof(ftp->stream) && (ch = php_stream_getc(ftp->stream)) != EOF) {
  1955. if (ch == '\n' && ftp->type == FTPTYPE_ASCII) {
  1956. *ptr++ = '\r';
  1957. size++;
  1958. }
  1959. *ptr++ = ch;
  1960. size++;
  1961. /* flush if necessary */
  1962. if (FTP_BUFSIZE - size < 2) {
  1963. if (my_send(ftp, ftp->data->fd, ftp->data->buf, size) != size) {
  1964. goto bail;
  1965. }
  1966. return PHP_FTP_MOREDATA;
  1967. }
  1968. }
  1969. if (size && my_send(ftp, ftp->data->fd, ftp->data->buf, size) != size) {
  1970. goto bail;
  1971. }
  1972. ftp->data = data_close(ftp, ftp->data);
  1973. if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
  1974. goto bail;
  1975. }
  1976. ftp->nb = 0;
  1977. return PHP_FTP_FINISHED;
  1978. bail:
  1979. ftp->data = data_close(ftp, ftp->data);
  1980. ftp->nb = 0;
  1981. return PHP_FTP_FAILED;
  1982. }
  1983. /* }}} */
  1984. #endif /* HAVE_FTP */