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

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