PageRenderTime 73ms CodeModel.GetById 33ms RepoModel.GetById 1ms app.codeStats 0ms

/ext/ftp/ftp.c

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