root/ext/ftp/ftp.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ftp_open
  2. ftp_close
  3. ftp_gc
  4. ftp_quit
  5. ftp_login
  6. ftp_reinit
  7. ftp_syst
  8. ftp_pwd
  9. ftp_exec
  10. ftp_raw
  11. ftp_chdir
  12. ftp_cdup
  13. ftp_mkdir
  14. ftp_rmdir
  15. ftp_chmod
  16. ftp_alloc
  17. ftp_nlist
  18. ftp_list
  19. ftp_type
  20. ftp_pasv
  21. ftp_get
  22. ftp_put
  23. ftp_size
  24. ftp_mdtm
  25. ftp_delete
  26. ftp_rename
  27. ftp_site
  28. ftp_putcmd
  29. ftp_readline
  30. ftp_getresp
  31. my_send
  32. my_recv
  33. data_available
  34. data_writeable
  35. my_accept
  36. ftp_getdata
  37. data_accept
  38. data_close
  39. ftp_genlist
  40. ftp_nb_get
  41. ftp_nb_continue_read
  42. ftp_nb_put
  43. ftp_nb_continue_write

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

/* [<][>][^][v][top][bottom][index][help] */