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

/ext/ftp/ftp.c

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