Back to index

php5  5.3.10
ftp.c
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | PHP Version 5                                                        |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1997-2012 The PHP Group                                |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 3.01 of the PHP license,      |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.php.net/license/3_01.txt                                  |
00011    | If you did not receive a copy of the PHP license and are unable to   |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@php.net so we can mail you a copy immediately.               |
00014    +----------------------------------------------------------------------+
00015    | Authors: Andrew Skalski <askalski@chek.com>                          |
00016    |          Stefan Esser <sesser@php.net> (resume functions)            |
00017    +----------------------------------------------------------------------+
00018  */
00019 
00020 /* $Id: ftp.c 321634 2012-01-01 13:15:04Z felipe $ */
00021 
00022 #ifdef HAVE_CONFIG_H
00023 #include "config.h"
00024 #endif
00025 
00026 #include "php.h"
00027 
00028 #if HAVE_FTP
00029 
00030 #include <stdio.h>
00031 #include <ctype.h>
00032 #include <stdlib.h>
00033 #ifdef HAVE_UNISTD_H
00034 #include <unistd.h>
00035 #endif
00036 #include <fcntl.h>
00037 #include <string.h>
00038 #include <time.h>
00039 #ifdef PHP_WIN32
00040 #include <winsock2.h>
00041 #elif defined(NETWARE)
00042 #ifdef USE_WINSOCK    /* Modified to use Winsock (NOVSOCK2.H), atleast for now */
00043 #include <novsock2.h>
00044 #else
00045 #include <sys/socket.h>
00046 #include <netinet/in.h>
00047 #include <netdb.h>
00048 #endif
00049 #else
00050 #ifdef HAVE_SYS_TYPES_H
00051 #include <sys/types.h>
00052 #endif
00053 #include <sys/socket.h>
00054 #include <netinet/in.h>
00055 #include <arpa/inet.h>
00056 #include <netdb.h>
00057 #endif
00058 #include <errno.h>
00059 
00060 #if HAVE_SYS_TIME_H
00061 #include <sys/time.h>
00062 #endif
00063 
00064 #ifdef HAVE_SYS_SELECT_H
00065 #include <sys/select.h>
00066 #endif
00067 
00068 #if HAVE_OPENSSL_EXT
00069 #include <openssl/ssl.h>
00070 #endif
00071 
00072 #include "ftp.h"
00073 #include "ext/standard/fsock.h"
00074 
00075 /* Additional headers for NetWare */
00076 #if defined(NETWARE) && !defined(USE_WINSOCK)
00077 #include <sys/select.h>
00078 #endif
00079 
00080 /* sends an ftp command, returns true on success, false on error.
00081  * it sends the string "cmd args\r\n" if args is non-null, or
00082  * "cmd\r\n" if args is null
00083  */
00084 static int           ftp_putcmd(   ftpbuf_t *ftp,
00085                                    const char *cmd,
00086                                    const char *args);
00087 
00088 /* wrapper around send/recv to handle timeouts */
00089 static int           my_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len);
00090 static int           my_recv(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len);
00091 static int           my_accept(ftpbuf_t *ftp, php_socket_t s, struct sockaddr *addr, socklen_t *addrlen);
00092 
00093 /* reads a line the socket , returns true on success, false on error */
00094 static int           ftp_readline(ftpbuf_t *ftp);
00095 
00096 /* reads an ftp response, returns true on success, false on error */
00097 static int           ftp_getresp(ftpbuf_t *ftp);
00098 
00099 /* sets the ftp transfer type */
00100 static int           ftp_type(ftpbuf_t *ftp, ftptype_t type);
00101 
00102 /* opens up a data stream */
00103 static databuf_t*    ftp_getdata(ftpbuf_t *ftp TSRMLS_DC);
00104 
00105 /* accepts the data connection, returns updated data buffer */
00106 static databuf_t*    data_accept(databuf_t *data, ftpbuf_t *ftp TSRMLS_DC);
00107 
00108 /* closes the data connection, returns NULL */
00109 static databuf_t*    data_close(ftpbuf_t *ftp, databuf_t *data);
00110 
00111 /* generic file lister */
00112 static char**        ftp_genlist(ftpbuf_t *ftp, const char *cmd, const char *path TSRMLS_DC);
00113 
00114 /* IP and port conversion box */
00115 union ipbox {
00116        struct in_addr       ia[2];
00117        unsigned short       s[4];
00118        unsigned char c[8];
00119 };
00120 
00121 /* {{{ ftp_open
00122  */
00123 ftpbuf_t*
00124 ftp_open(const char *host, short port, long timeout_sec TSRMLS_DC)
00125 {
00126        ftpbuf_t             *ftp;
00127        socklen_t             size;
00128        struct timeval tv;
00129 
00130 
00131        /* alloc the ftp structure */
00132        ftp = ecalloc(1, sizeof(*ftp));
00133 
00134        tv.tv_sec = timeout_sec;
00135        tv.tv_usec = 0;
00136 
00137        ftp->fd = php_network_connect_socket_to_host(host,
00138                      (unsigned short) (port ? port : 21), SOCK_STREAM,
00139                      0, &tv, NULL, NULL, NULL, 0 TSRMLS_CC);
00140        if (ftp->fd == -1) {
00141               goto bail;
00142        }
00143 
00144        /* Default Settings */
00145        ftp->timeout_sec = timeout_sec;
00146        ftp->nb = 0;
00147 
00148        size = sizeof(ftp->localaddr);
00149        memset(&ftp->localaddr, 0, size);
00150        if (getsockname(ftp->fd, (struct sockaddr*) &ftp->localaddr, &size) != 0) {
00151               php_error_docref(NULL TSRMLS_CC, E_WARNING, "getsockname failed: %s (%d)", strerror(errno), errno);
00152               goto bail;
00153        }
00154 
00155        if (!ftp_getresp(ftp) || ftp->resp != 220) {
00156               goto bail;
00157        }
00158 
00159        return ftp;
00160 
00161 bail:
00162        if (ftp->fd != -1) {
00163               closesocket(ftp->fd);
00164        }
00165        efree(ftp);
00166        return NULL;
00167 }
00168 /* }}} */
00169 
00170 /* {{{ ftp_close
00171  */
00172 ftpbuf_t*
00173 ftp_close(ftpbuf_t *ftp)
00174 {
00175        if (ftp == NULL) {
00176               return NULL;
00177        }
00178        if (ftp->data) {
00179               data_close(ftp, ftp->data);
00180        }
00181        if (ftp->fd != -1) {
00182 #if HAVE_OPENSSL_EXT
00183               if (ftp->ssl_active) {
00184                      SSL_shutdown(ftp->ssl_handle);
00185               }
00186 #endif        
00187               closesocket(ftp->fd);
00188        }      
00189        ftp_gc(ftp);
00190        efree(ftp);
00191        return NULL;
00192 }
00193 /* }}} */
00194 
00195 /* {{{ ftp_gc
00196  */
00197 void
00198 ftp_gc(ftpbuf_t *ftp)
00199 {
00200        if (ftp == NULL) {
00201               return;
00202        }
00203        if (ftp->pwd) {
00204               efree(ftp->pwd);
00205               ftp->pwd = NULL;
00206        }
00207        if (ftp->syst) {
00208               efree(ftp->syst);
00209               ftp->syst = NULL;
00210        }
00211 }
00212 /* }}} */
00213 
00214 /* {{{ ftp_quit
00215  */
00216 int
00217 ftp_quit(ftpbuf_t *ftp)
00218 {
00219        if (ftp == NULL) {
00220               return 0;
00221        }
00222 
00223        if (!ftp_putcmd(ftp, "QUIT", NULL)) {
00224               return 0;
00225        }
00226        if (!ftp_getresp(ftp) || ftp->resp != 221) {
00227               return 0;
00228        }
00229 
00230        if (ftp->pwd) {
00231               efree(ftp->pwd);
00232               ftp->pwd = NULL;
00233        }
00234 
00235        return 1;
00236 }
00237 /* }}} */
00238 
00239 /* {{{ ftp_login
00240  */
00241 int
00242 ftp_login(ftpbuf_t *ftp, const char *user, const char *pass TSRMLS_DC)
00243 {
00244 #if HAVE_OPENSSL_EXT
00245        SSL_CTX       *ctx = NULL;
00246 #endif
00247        if (ftp == NULL) {
00248               return 0;
00249        }
00250 
00251 #if HAVE_OPENSSL_EXT
00252        if (ftp->use_ssl && !ftp->ssl_active) {
00253               if (!ftp_putcmd(ftp, "AUTH", "TLS")) {
00254                      return 0;
00255               }
00256               if (!ftp_getresp(ftp)) {
00257                      return 0;
00258               }
00259                      
00260               if (ftp->resp != 234) {
00261                      if (!ftp_putcmd(ftp, "AUTH", "SSL")) {
00262                             return 0;
00263                      }
00264                      if (!ftp_getresp(ftp)) {
00265                             return 0;
00266                      }
00267                             
00268                      if (ftp->resp != 334) {
00269                             return 0;
00270                      } else {
00271                             ftp->old_ssl = 1;
00272                             ftp->use_ssl_for_data = 1;
00273                      }
00274               }
00275               
00276               ctx = SSL_CTX_new(SSLv23_client_method());
00277               if (ctx == NULL) {
00278                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create the SSL context");
00279                      return 0;
00280               }
00281 
00282               SSL_CTX_set_options(ctx, SSL_OP_ALL);
00283 
00284               ftp->ssl_handle = SSL_new(ctx);
00285               if (ftp->ssl_handle == NULL) {
00286                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create the SSL handle");
00287                      SSL_CTX_free(ctx);
00288                      return 0;
00289               }
00290 
00291               SSL_set_fd(ftp->ssl_handle, ftp->fd);
00292 
00293               if (SSL_connect(ftp->ssl_handle) <= 0) {
00294                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL/TLS handshake failed");
00295                      SSL_shutdown(ftp->ssl_handle);
00296                      return 0;
00297               }
00298 
00299               ftp->ssl_active = 1;
00300 
00301               if (!ftp->old_ssl) {
00302 
00303                      /* set protection buffersize to zero */
00304                      if (!ftp_putcmd(ftp, "PBSZ", "0")) {
00305                             return 0;
00306                      }
00307                      if (!ftp_getresp(ftp)) {
00308                             return 0;
00309                      }
00310 
00311                      /* enable data conn encryption */
00312                      if (!ftp_putcmd(ftp, "PROT", "P")) {
00313                             return 0;
00314                      }
00315                      if (!ftp_getresp(ftp)) {
00316                             return 0;
00317                      }
00318                      
00319                      ftp->use_ssl_for_data = (ftp->resp >= 200 && ftp->resp <=299);        
00320               }
00321        }
00322 #endif
00323 
00324        if (!ftp_putcmd(ftp, "USER", user)) {
00325               return 0;
00326        }
00327        if (!ftp_getresp(ftp)) {
00328               return 0;
00329        }
00330        if (ftp->resp == 230) {
00331               return 1;
00332        }
00333        if (ftp->resp != 331) {
00334               return 0;
00335        }
00336        if (!ftp_putcmd(ftp, "PASS", pass)) {
00337               return 0;
00338        }
00339        if (!ftp_getresp(ftp)) {
00340               return 0;
00341        }
00342        return (ftp->resp == 230);
00343 }
00344 /* }}} */
00345 
00346 /* {{{ ftp_reinit
00347  */
00348 int
00349 ftp_reinit(ftpbuf_t *ftp)
00350 {
00351        if (ftp == NULL) {
00352               return 0;
00353        }      
00354 
00355        ftp_gc(ftp);
00356 
00357        ftp->nb = 0;
00358 
00359        if (!ftp_putcmd(ftp, "REIN", NULL)) {
00360               return 0;
00361        }
00362        if (!ftp_getresp(ftp) || ftp->resp != 220) {
00363               return 0;
00364        }
00365 
00366        return 1;
00367 }
00368 /* }}} */
00369 
00370 /* {{{ ftp_syst
00371  */
00372 const char*
00373 ftp_syst(ftpbuf_t *ftp)
00374 {
00375        char *syst, *end;
00376 
00377        if (ftp == NULL) {
00378               return NULL;
00379        }
00380 
00381        /* default to cached value */
00382        if (ftp->syst) {
00383               return ftp->syst;
00384        }
00385        if (!ftp_putcmd(ftp, "SYST", NULL)) {
00386               return NULL;
00387        }
00388        if (!ftp_getresp(ftp) || ftp->resp != 215) { 
00389               return NULL;
00390        }
00391        syst = ftp->inbuf;
00392        while (*syst == ' ') {
00393               syst++;
00394        }
00395        if ((end = strchr(syst, ' '))) {
00396               *end = 0;
00397        }
00398        ftp->syst = estrdup(syst);
00399        if (end) {
00400               *end = ' ';
00401        }
00402        return ftp->syst;
00403 }
00404 /* }}} */
00405 
00406 /* {{{ ftp_pwd
00407  */
00408 const char*
00409 ftp_pwd(ftpbuf_t *ftp)
00410 {
00411        char *pwd, *end;
00412 
00413        if (ftp == NULL) {
00414               return NULL;
00415        }
00416 
00417        /* default to cached value */
00418        if (ftp->pwd) {
00419               return ftp->pwd;
00420        }
00421        if (!ftp_putcmd(ftp, "PWD", NULL)) {
00422               return NULL;
00423        }
00424        if (!ftp_getresp(ftp) || ftp->resp != 257) { 
00425               return NULL;
00426        }
00427        /* copy out the pwd from response */
00428        if ((pwd = strchr(ftp->inbuf, '"')) == NULL) { 
00429               return NULL;
00430        }
00431        if ((end = strrchr(++pwd, '"')) == NULL) { 
00432               return NULL;
00433        }
00434        ftp->pwd = estrndup(pwd, end - pwd);
00435 
00436        return ftp->pwd;
00437 }
00438 /* }}} */
00439 
00440 /* {{{ ftp_exec
00441  */
00442 int
00443 ftp_exec(ftpbuf_t *ftp, const char *cmd)
00444 {
00445        if (ftp == NULL) {
00446               return 0;
00447        }
00448        if (!ftp_putcmd(ftp, "SITE EXEC", cmd)) {
00449               return 0;
00450        }
00451        if (!ftp_getresp(ftp) || ftp->resp != 200) {
00452               return 0;
00453        }
00454 
00455        return 1;
00456 }
00457 /* }}} */
00458 
00459 /* {{{ ftp_raw
00460  */
00461 void
00462 ftp_raw(ftpbuf_t *ftp, const char *cmd, zval *return_value)
00463 {
00464        if (ftp == NULL || cmd == NULL) {
00465               RETURN_NULL();
00466        }
00467        if (!ftp_putcmd(ftp, cmd, NULL)) {
00468               RETURN_NULL();
00469        }
00470        array_init(return_value);
00471        while (ftp_readline(ftp)) {
00472               add_next_index_string(return_value, ftp->inbuf, 1);
00473               if (isdigit(ftp->inbuf[0]) && isdigit(ftp->inbuf[1]) && isdigit(ftp->inbuf[2]) && ftp->inbuf[3] == ' ') {
00474                      return;
00475               }
00476        }
00477 }
00478 /* }}} */
00479 
00480 /* {{{ ftp_chdir
00481  */
00482 int
00483 ftp_chdir(ftpbuf_t *ftp, const char *dir)
00484 {
00485        if (ftp == NULL) {
00486               return 0;
00487        }
00488 
00489        if (ftp->pwd) {
00490               efree(ftp->pwd);
00491               ftp->pwd = NULL;
00492        }
00493 
00494        if (!ftp_putcmd(ftp, "CWD", dir)) {
00495               return 0;
00496        }
00497        if (!ftp_getresp(ftp) || ftp->resp != 250) {
00498               return 0;
00499        }
00500        return 1;
00501 }
00502 /* }}} */
00503 
00504 /* {{{ ftp_cdup
00505  */
00506 int
00507 ftp_cdup(ftpbuf_t *ftp)
00508 {
00509        if (ftp == NULL) {
00510               return 0;
00511        }
00512 
00513        if (ftp->pwd) {
00514               efree(ftp->pwd);
00515               ftp->pwd = NULL;
00516        }
00517 
00518        if (!ftp_putcmd(ftp, "CDUP", NULL)) {
00519               return 0;
00520        }
00521        if (!ftp_getresp(ftp) || ftp->resp != 250) {
00522               return 0;
00523        }
00524        return 1;
00525 }
00526 /* }}} */
00527 
00528 /* {{{ ftp_mkdir
00529  */
00530 char*
00531 ftp_mkdir(ftpbuf_t *ftp, const char *dir)
00532 {
00533        char *mkd, *end;
00534 
00535        if (ftp == NULL) {
00536               return NULL;
00537        }
00538        if (!ftp_putcmd(ftp, "MKD", dir)) {
00539               return NULL;
00540        }
00541        if (!ftp_getresp(ftp) || ftp->resp != 257) {
00542               return NULL;
00543        }
00544        /* copy out the dir from response */
00545        if ((mkd = strchr(ftp->inbuf, '"')) == NULL) {
00546               mkd = estrdup(dir);
00547               return mkd;
00548        }
00549        if ((end = strrchr(++mkd, '"')) == NULL) {
00550               return NULL;
00551        }
00552        *end = 0;
00553        mkd = estrdup(mkd);
00554        *end = '"';
00555 
00556        return mkd;
00557 }
00558 /* }}} */
00559 
00560 /* {{{ ftp_rmdir
00561  */
00562 int
00563 ftp_rmdir(ftpbuf_t *ftp, const char *dir)
00564 {
00565        if (ftp == NULL) {
00566               return 0;
00567        }
00568        if (!ftp_putcmd(ftp, "RMD", dir)) {
00569               return 0;
00570        }
00571        if (!ftp_getresp(ftp) || ftp->resp != 250) {
00572               return 0;
00573        }
00574        return 1;
00575 }
00576 /* }}} */
00577 
00578 /* {{{ ftp_chmod
00579  */
00580 int
00581 ftp_chmod(ftpbuf_t *ftp, const int mode, const char *filename, const int filename_len)
00582 {
00583        char *buffer;
00584 
00585        if (ftp == NULL || filename_len <= 0) {
00586               return 0;
00587        }
00588 
00589        spprintf(&buffer, 0, "CHMOD %o %s", mode, filename);
00590 
00591        if (!ftp_putcmd(ftp, "SITE", buffer)) {
00592               efree(buffer);
00593               return 0;
00594        }
00595 
00596        efree(buffer);
00597 
00598        if (!ftp_getresp(ftp) || ftp->resp != 200) {
00599               return 0;
00600        }
00601        
00602        return 1;
00603 }
00604 /* }}} */
00605 
00606 /* {{{ ftp_alloc
00607  */
00608 int
00609 ftp_alloc(ftpbuf_t *ftp, const int size, char **response)
00610 {
00611        char buffer[64];
00612 
00613        if (ftp == NULL || size <= 0) {
00614               return 0;
00615        }
00616 
00617        snprintf(buffer, sizeof(buffer) - 1, "%d", size);
00618 
00619        if (!ftp_putcmd(ftp, "ALLO", buffer)) {
00620               return 0;
00621        }
00622 
00623        if (!ftp_getresp(ftp)) {
00624               return 0;
00625        }
00626 
00627        if (response && ftp->inbuf) {
00628               *response = estrdup(ftp->inbuf);
00629        }
00630 
00631        if (ftp->resp < 200 || ftp->resp >= 300) {
00632               return 0;
00633        }
00634 
00635        return 1;     
00636 }
00637 /* }}} */
00638 
00639 /* {{{ ftp_nlist
00640  */
00641 char**
00642 ftp_nlist(ftpbuf_t *ftp, const char *path TSRMLS_DC)
00643 {
00644        return ftp_genlist(ftp, "NLST", path TSRMLS_CC);
00645 }
00646 /* }}} */
00647 
00648 /* {{{ ftp_list
00649  */
00650 char**
00651 ftp_list(ftpbuf_t *ftp, const char *path, int recursive TSRMLS_DC)
00652 {
00653        return ftp_genlist(ftp, ((recursive) ? "LIST -R" : "LIST"), path TSRMLS_CC);
00654 }
00655 /* }}} */
00656 
00657 /* {{{ ftp_type
00658  */
00659 int
00660 ftp_type(ftpbuf_t *ftp, ftptype_t type)
00661 {
00662        char typechar[2] = "?";
00663 
00664        if (ftp == NULL) {
00665               return 0;
00666        }
00667        if (type == ftp->type) { 
00668               return 1;
00669        }
00670        if (type == FTPTYPE_ASCII) {
00671               typechar[0] = 'A';
00672        } else if (type == FTPTYPE_IMAGE) {
00673               typechar[0] = 'I';
00674        } else {
00675               return 0;
00676        }
00677        if (!ftp_putcmd(ftp, "TYPE", typechar)) {
00678               return 0;
00679        }
00680        if (!ftp_getresp(ftp) || ftp->resp != 200) {
00681               return 0;
00682        }
00683        ftp->type = type;
00684 
00685        return 1;
00686 }
00687 /* }}} */
00688 
00689 /* {{{ ftp_pasv
00690  */
00691 int
00692 ftp_pasv(ftpbuf_t *ftp, int pasv)
00693 {
00694        char                 *ptr;
00695        union ipbox          ipbox;
00696        unsigned long        b[6];
00697        socklen_t                   n;
00698        struct sockaddr *sa;
00699        struct sockaddr_in *sin;
00700 
00701        if (ftp == NULL) {
00702               return 0;
00703        }
00704        if (pasv && ftp->pasv == 2) {
00705               return 1;
00706        }
00707        ftp->pasv = 0;
00708        if (!pasv) {
00709               return 1;
00710        }
00711        n = sizeof(ftp->pasvaddr);
00712        memset(&ftp->pasvaddr, 0, n);
00713        sa = (struct sockaddr *) &ftp->pasvaddr;
00714 
00715 #if HAVE_IPV6
00716        if (getpeername(ftp->fd, sa, &n) < 0) {
00717               return 0;
00718        }
00719        if (sa->sa_family == AF_INET6) {
00720               struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa;
00721               char *endptr, delimiter;
00722 
00723               /* try EPSV first */
00724               if (!ftp_putcmd(ftp, "EPSV", NULL)) {
00725                      return 0;
00726               }
00727               if (!ftp_getresp(ftp)) {
00728                      return 0;
00729               }
00730               if (ftp->resp == 229) {
00731                      /* parse out the port */
00732                      for (ptr = ftp->inbuf; *ptr && *ptr != '('; ptr++);
00733                      if (!*ptr) {
00734                             return 0;
00735                      }
00736                      delimiter = *++ptr;
00737                      for (n = 0; *ptr && n < 3; ptr++) {
00738                             if (*ptr == delimiter) {
00739                                    n++;
00740                             }
00741                      }
00742 
00743                      sin6->sin6_port = htons((unsigned short) strtoul(ptr, &endptr, 10));
00744                      if (ptr == endptr || *endptr != delimiter) {
00745                             return 0;
00746                      }
00747                      ftp->pasv = 2;
00748                      return 1;
00749               }
00750        }
00751 
00752        /* fall back to PASV */
00753 #endif
00754 
00755        if (!ftp_putcmd(ftp, "PASV", NULL)) {
00756               return 0;
00757        }
00758        if (!ftp_getresp(ftp) || ftp->resp != 227) { 
00759               return 0;
00760        }
00761        /* parse out the IP and port */
00762        for (ptr = ftp->inbuf; *ptr && !isdigit(*ptr); ptr++);
00763        n = sscanf(ptr, "%lu,%lu,%lu,%lu,%lu,%lu", &b[0], &b[1], &b[2], &b[3], &b[4], &b[5]);
00764        if (n != 6) {
00765               return 0;
00766        }
00767        for (n = 0; n < 6; n++) {
00768               ipbox.c[n] = (unsigned char) b[n];
00769        }
00770        sin = (struct sockaddr_in *) sa;
00771        sin->sin_family = AF_INET;
00772        sin->sin_addr = ipbox.ia[0];
00773        sin->sin_port = ipbox.s[2];
00774 
00775        ftp->pasv = 2;
00776 
00777        return 1;
00778 }
00779 /* }}} */
00780 
00781 /* {{{ ftp_get
00782  */
00783 int
00784 ftp_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, ftptype_t type, int resumepos TSRMLS_DC)
00785 {
00786        databuf_t            *data = NULL;
00787        int                  lastch;
00788        size_t               rcvd;
00789        char                 arg[11];
00790 
00791        if (ftp == NULL) {
00792               return 0;
00793        }
00794        if (!ftp_type(ftp, type)) {
00795               goto bail;
00796        }
00797 
00798        if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
00799               goto bail;
00800        }
00801        
00802        ftp->data = data;
00803 
00804        if (resumepos > 0) {
00805               if (resumepos > 2147483647) {
00806                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "PHP cannot handle files greater than 2147483647 bytes.");
00807                      goto bail;
00808               }
00809               snprintf(arg, sizeof(arg), "%u", resumepos);
00810               if (!ftp_putcmd(ftp, "REST", arg)) {
00811                      goto bail;
00812               }
00813               if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
00814                      goto bail;
00815               }
00816        }
00817 
00818        if (!ftp_putcmd(ftp, "RETR", path)) {
00819               goto bail;
00820        }
00821        if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
00822               goto bail;
00823        }
00824 
00825        if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) {
00826               goto bail;
00827        }
00828 
00829        lastch = 0;
00830        while ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
00831               if (rcvd == -1) {
00832                      goto bail;
00833               }
00834 
00835               if (type == FTPTYPE_ASCII) {
00836 #ifndef PHP_WIN32
00837                      char *s;
00838 #endif
00839                      char *ptr = data->buf;
00840                      char *e = ptr + rcvd;
00841                      /* logic depends on the OS EOL
00842                       * Win32 -> \r\n
00843                       * Everything Else \n
00844                       */
00845 #ifdef PHP_WIN32
00846                      php_stream_write(outstream, ptr, (e - ptr));
00847                      ptr = e;
00848 #else
00849                      while (e > ptr && (s = memchr(ptr, '\r', (e - ptr)))) {
00850                             php_stream_write(outstream, ptr, (s - ptr));
00851                             if (*(s + 1) == '\n') {
00852                                    s++;
00853                                    php_stream_putc(outstream, '\n');
00854                             }
00855                             ptr = s + 1;
00856                      }
00857 #endif
00858                      if (ptr < e) {
00859                             php_stream_write(outstream, ptr, (e - ptr));
00860                      }
00861               } else if (rcvd != php_stream_write(outstream, data->buf, rcvd)) {
00862                      goto bail;
00863               }
00864        }
00865 
00866        ftp->data = data = data_close(ftp, data);
00867 
00868        if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
00869               goto bail;
00870        }
00871 
00872        return 1;
00873 bail:
00874        ftp->data = data_close(ftp, data);
00875        return 0;
00876 }
00877 /* }}} */
00878 
00879 /* {{{ ftp_put
00880  */
00881 int
00882 ftp_put(ftpbuf_t *ftp, const char *path, php_stream *instream, ftptype_t type, int startpos TSRMLS_DC)
00883 {
00884        databuf_t            *data = NULL;
00885        int                  size;
00886        char                 *ptr;
00887        int                  ch;
00888        char                 arg[11];
00889 
00890        if (ftp == NULL) {
00891               return 0;
00892        }
00893        if (!ftp_type(ftp, type)) {
00894               goto bail;
00895        }
00896        if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
00897               goto bail;
00898        }
00899        ftp->data = data;    
00900 
00901        if (startpos > 0) {
00902               if (startpos > 2147483647) {
00903                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "PHP cannot handle files with a size greater than 2147483647 bytes.");
00904                      goto bail;
00905               }
00906               snprintf(arg, sizeof(arg), "%u", startpos);
00907               if (!ftp_putcmd(ftp, "REST", arg)) {
00908                      goto bail;
00909               }
00910               if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
00911                      goto bail;
00912               }
00913        }
00914 
00915        if (!ftp_putcmd(ftp, "STOR", path)) {
00916               goto bail;
00917        }
00918        if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
00919               goto bail;
00920        }
00921        if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) {
00922               goto bail;
00923        }
00924 
00925        size = 0;
00926        ptr = data->buf;
00927        while (!php_stream_eof(instream) && (ch = php_stream_getc(instream))!=EOF) {
00928               /* flush if necessary */
00929               if (FTP_BUFSIZE - size < 2) {
00930                      if (my_send(ftp, data->fd, data->buf, size) != size) {
00931                             goto bail;
00932                      }
00933                      ptr = data->buf;
00934                      size = 0;
00935               }
00936 
00937               if (ch == '\n' && type == FTPTYPE_ASCII) {
00938                      *ptr++ = '\r';
00939                      size++;
00940               }
00941 
00942               *ptr++ = ch;
00943               size++;
00944        }
00945 
00946        if (size && my_send(ftp, data->fd, data->buf, size) != size) {
00947               goto bail;
00948        }
00949        ftp->data = data = data_close(ftp, data);
00950 
00951        if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250 && ftp->resp != 200)) {
00952               goto bail;
00953        }
00954        return 1;
00955 bail:
00956        ftp->data = data_close(ftp, data);
00957        return 0;
00958 }
00959 /* }}} */
00960 
00961 /* {{{ ftp_size
00962  */
00963 int
00964 ftp_size(ftpbuf_t *ftp, const char *path)
00965 {
00966        if (ftp == NULL) {
00967               return -1;
00968        }
00969        if (!ftp_type(ftp, FTPTYPE_IMAGE)) {
00970               return -1;
00971        }
00972        if (!ftp_putcmd(ftp, "SIZE", path)) {
00973               return -1;
00974        }
00975        if (!ftp_getresp(ftp) || ftp->resp != 213) {
00976               return -1;
00977        }
00978        return atoi(ftp->inbuf);
00979 }
00980 /* }}} */
00981 
00982 /* {{{ ftp_mdtm
00983  */
00984 time_t
00985 ftp_mdtm(ftpbuf_t *ftp, const char *path)
00986 {
00987        time_t        stamp;
00988        struct tm     *gmt, tmbuf;
00989        struct tm     tm;
00990        char          *ptr;
00991        int           n;
00992 
00993        if (ftp == NULL) {
00994               return -1;
00995        }
00996        if (!ftp_putcmd(ftp, "MDTM", path)) {
00997               return -1;
00998        }
00999        if (!ftp_getresp(ftp) || ftp->resp != 213) {
01000               return -1;
01001        }
01002        /* parse out the timestamp */
01003        for (ptr = ftp->inbuf; *ptr && !isdigit(*ptr); ptr++);
01004        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);
01005        if (n != 6) {
01006               return -1;
01007        }
01008        tm.tm_year -= 1900;
01009        tm.tm_mon--;
01010        tm.tm_isdst = -1;
01011 
01012        /* figure out the GMT offset */
01013        stamp = time(NULL);
01014        gmt = php_gmtime_r(&stamp, &tmbuf);
01015        if (!gmt) {
01016               return -1;
01017        }
01018        gmt->tm_isdst = -1;
01019 
01020        /* apply the GMT offset */
01021        tm.tm_sec += stamp - mktime(gmt);
01022        tm.tm_isdst = gmt->tm_isdst;
01023 
01024        stamp = mktime(&tm);
01025 
01026        return stamp;
01027 }
01028 /* }}} */
01029 
01030 /* {{{ ftp_delete
01031  */
01032 int
01033 ftp_delete(ftpbuf_t *ftp, const char *path)
01034 {
01035        if (ftp == NULL) {
01036               return 0;
01037        }
01038        if (!ftp_putcmd(ftp, "DELE", path)) {
01039               return 0;
01040        }
01041        if (!ftp_getresp(ftp) || ftp->resp != 250) {
01042               return 0;
01043        }
01044 
01045        return 1;
01046 }
01047 /* }}} */
01048 
01049 /* {{{ ftp_rename
01050  */
01051 int
01052 ftp_rename(ftpbuf_t *ftp, const char *src, const char *dest)
01053 {
01054        if (ftp == NULL) {
01055               return 0;
01056        }
01057        if (!ftp_putcmd(ftp, "RNFR", src)) {
01058               return 0;
01059        }
01060        if (!ftp_getresp(ftp) || ftp->resp != 350) {
01061               return 0;
01062        }
01063        if (!ftp_putcmd(ftp, "RNTO", dest)) {
01064               return 0;
01065        }
01066        if (!ftp_getresp(ftp) || ftp->resp != 250) {
01067               return 0;
01068        }
01069        return 1;
01070 }
01071 /* }}} */
01072 
01073 /* {{{ ftp_site
01074  */
01075 int
01076 ftp_site(ftpbuf_t *ftp, const char *cmd)
01077 {
01078        if (ftp == NULL) {
01079               return 0;
01080        }
01081        if (!ftp_putcmd(ftp, "SITE", cmd)) {
01082               return 0;
01083        }
01084        if (!ftp_getresp(ftp) || ftp->resp < 200 || ftp->resp >= 300) {
01085               return 0;
01086        }
01087 
01088        return 1;
01089 }
01090 /* }}} */
01091 
01092 /* static functions */
01093 
01094 /* {{{ ftp_putcmd
01095  */
01096 int
01097 ftp_putcmd(ftpbuf_t *ftp, const char *cmd, const char *args)
01098 {
01099        int           size;
01100        char          *data;
01101 
01102        if (strpbrk(cmd, "\r\n")) {
01103               return 0;
01104        } 
01105        /* build the output buffer */
01106        if (args && args[0]) {
01107               /* "cmd args\r\n\0" */
01108               if (strlen(cmd) + strlen(args) + 4 > FTP_BUFSIZE) {
01109                      return 0;
01110               }
01111               if (strpbrk(args, "\r\n")) {
01112                      return 0;
01113               }
01114               size = slprintf(ftp->outbuf, sizeof(ftp->outbuf), "%s %s\r\n", cmd, args);
01115        } else {
01116               /* "cmd\r\n\0" */
01117               if (strlen(cmd) + 3 > FTP_BUFSIZE) {
01118                      return 0;
01119               }
01120               size = slprintf(ftp->outbuf, sizeof(ftp->outbuf), "%s\r\n", cmd);
01121        }
01122 
01123        data = ftp->outbuf;
01124 
01125        /* Clear the extra-lines buffer */
01126        ftp->extra = NULL;
01127 
01128        if (my_send(ftp, ftp->fd, data, size) != size) {
01129               return 0;
01130        }
01131        return 1;
01132 }
01133 /* }}} */
01134 
01135 /* {{{ ftp_readline
01136  */
01137 int
01138 ftp_readline(ftpbuf_t *ftp)
01139 {
01140        int           size, rcvd;
01141        char          *data, *eol;
01142 
01143        /* shift the extra to the front */
01144        size = FTP_BUFSIZE;
01145        rcvd = 0;
01146        if (ftp->extra) {
01147               memmove(ftp->inbuf, ftp->extra, ftp->extralen);
01148               rcvd = ftp->extralen;
01149        }
01150 
01151        data = ftp->inbuf;
01152 
01153        do {
01154               size -= rcvd;
01155               for (eol = data; rcvd; rcvd--, eol++) {
01156                      if (*eol == '\r') {
01157                             *eol = 0;
01158                             ftp->extra = eol + 1;
01159                             if (rcvd > 1 && *(eol + 1) == '\n') {
01160                                    ftp->extra++;
01161                                    rcvd--;
01162                             }
01163                             if ((ftp->extralen = --rcvd) == 0) {
01164                                    ftp->extra = NULL;
01165                             }
01166                             return 1;
01167                      } else if (*eol == '\n') {
01168                             *eol = 0;
01169                             ftp->extra = eol + 1;
01170                             if ((ftp->extralen = --rcvd) == 0) {
01171                                    ftp->extra = NULL;
01172                             }
01173                             return 1;
01174                      }
01175               }
01176 
01177               data = eol;
01178               if ((rcvd = my_recv(ftp, ftp->fd, data, size)) < 1) {
01179                      return 0;
01180               }
01181        } while (size);
01182 
01183        return 0;
01184 }
01185 /* }}} */
01186 
01187 /* {{{ ftp_getresp
01188  */
01189 int
01190 ftp_getresp(ftpbuf_t *ftp)
01191 {
01192        char *buf;
01193 
01194        if (ftp == NULL) {
01195               return 0;
01196        }
01197        buf = ftp->inbuf;
01198        ftp->resp = 0;
01199 
01200        while (1) {
01201 
01202               if (!ftp_readline(ftp)) {
01203                      return 0;
01204               }
01205 
01206               /* Break out when the end-tag is found */
01207               if (isdigit(ftp->inbuf[0]) && isdigit(ftp->inbuf[1]) && isdigit(ftp->inbuf[2]) && ftp->inbuf[3] == ' ') {
01208                      break;
01209               }
01210        }
01211 
01212        /* translate the tag */
01213        if (!isdigit(ftp->inbuf[0]) || !isdigit(ftp->inbuf[1]) || !isdigit(ftp->inbuf[2])) {
01214               return 0;
01215        }
01216 
01217        ftp->resp = 100 * (ftp->inbuf[0] - '0') + 10 * (ftp->inbuf[1] - '0') + (ftp->inbuf[2] - '0');
01218 
01219        memmove(ftp->inbuf, ftp->inbuf + 4, FTP_BUFSIZE - 4);
01220 
01221        if (ftp->extra) {
01222               ftp->extra -= 4;
01223        }
01224        return 1;
01225 }
01226 /* }}} */
01227 
01228 /* {{{ my_send
01229  */
01230 int
01231 my_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len)
01232 {
01233        int           n, size, sent;
01234 
01235        size = len;
01236        while (size) {
01237               n = php_pollfd_for_ms(s, POLLOUT, ftp->timeout_sec * 1000);
01238 
01239               if (n < 1) {
01240 
01241 #if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK))
01242                      if (n == 0) {
01243                             errno = ETIMEDOUT;
01244                      }
01245 #endif
01246                      return -1;
01247               }
01248 
01249 #if HAVE_OPENSSL_EXT
01250               if (ftp->use_ssl && ftp->fd == s && ftp->ssl_active) {
01251                      sent = SSL_write(ftp->ssl_handle, buf, size);
01252               } else if (ftp->use_ssl && ftp->fd != s && ftp->use_ssl_for_data && ftp->data->ssl_active) {      
01253                      sent = SSL_write(ftp->data->ssl_handle, buf, size);
01254               } else {
01255 #endif
01256                      sent = send(s, buf, size, 0);
01257 #if HAVE_OPENSSL_EXT
01258               }
01259 #endif
01260               if (sent == -1) {
01261                      return -1;
01262               }
01263 
01264               buf = (char*) buf + sent;
01265               size -= sent;
01266        }
01267 
01268        return len;
01269 }
01270 /* }}} */
01271 
01272 /* {{{ my_recv
01273  */
01274 int
01275 my_recv(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len)
01276 {
01277        int           n, nr_bytes;
01278 
01279        n = php_pollfd_for_ms(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000);
01280        if (n < 1) {
01281 #if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK))
01282               if (n == 0) {
01283                      errno = ETIMEDOUT;
01284               }
01285 #endif
01286               return -1;
01287        }
01288 
01289 #if HAVE_OPENSSL_EXT
01290        if (ftp->use_ssl && ftp->fd == s && ftp->ssl_active) {
01291               nr_bytes = SSL_read(ftp->ssl_handle, buf, len);
01292        } else if (ftp->use_ssl && ftp->fd != s && ftp->use_ssl_for_data && ftp->data->ssl_active) {      
01293               nr_bytes = SSL_read(ftp->data->ssl_handle, buf, len);
01294        } else {
01295 #endif
01296               nr_bytes = recv(s, buf, len, 0);
01297 #if HAVE_OPENSSL_EXT
01298        }
01299 #endif 
01300        return (nr_bytes);
01301 }
01302 /* }}} */
01303 
01304 /* {{{ data_available
01305  */
01306 int
01307 data_available(ftpbuf_t *ftp, php_socket_t s)
01308 {
01309        int           n;
01310 
01311        n = php_pollfd_for_ms(s, PHP_POLLREADABLE, 1000);
01312        if (n < 1) {
01313 #if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK))
01314               if (n == 0) {
01315                      errno = ETIMEDOUT;
01316               }
01317 #endif
01318               return 0;
01319        }
01320 
01321        return 1;
01322 }
01323 /* }}} */
01324 /* {{{ data_writeable
01325  */
01326 int
01327 data_writeable(ftpbuf_t *ftp, php_socket_t s)
01328 {
01329        int           n;
01330 
01331        n = php_pollfd_for_ms(s, POLLOUT, 1000);
01332        if (n < 1) {
01333 #ifndef PHP_WIN32
01334               if (n == 0) {
01335                      errno = ETIMEDOUT;
01336               }
01337 #endif
01338               return 0;
01339        }
01340 
01341        return 1;
01342 }
01343 /* }}} */
01344 
01345 /* {{{ my_accept
01346  */
01347 int
01348 my_accept(ftpbuf_t *ftp, php_socket_t s, struct sockaddr *addr, socklen_t *addrlen)
01349 {
01350        int           n;
01351 
01352        n = php_pollfd_for_ms(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000);
01353        if (n < 1) {
01354 #if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK))
01355               if (n == 0) {
01356                      errno = ETIMEDOUT;
01357               }
01358 #endif
01359               return -1;
01360        }
01361 
01362        return accept(s, addr, addrlen);
01363 }
01364 /* }}} */
01365 
01366 /* {{{ ftp_getdata
01367  */
01368 databuf_t*
01369 ftp_getdata(ftpbuf_t *ftp TSRMLS_DC)
01370 {
01371        int                  fd = -1;
01372        databuf_t            *data;
01373        php_sockaddr_storage addr;
01374        struct sockaddr *sa;
01375        socklen_t            size;
01376        union ipbox          ipbox;
01377        char                 arg[sizeof("255, 255, 255, 255, 255, 255")];
01378        struct timeval       tv;
01379 
01380 
01381        /* ask for a passive connection if we need one */
01382        if (ftp->pasv && !ftp_pasv(ftp, 1)) {
01383               return NULL;
01384        }
01385        /* alloc the data structure */
01386        data = ecalloc(1, sizeof(*data));
01387        data->listener = -1;
01388        data->fd = -1;
01389        data->type = ftp->type;
01390 
01391        sa = (struct sockaddr *) &ftp->localaddr;
01392        /* bind/listen */
01393        if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) == SOCK_ERR) {
01394               php_error_docref(NULL TSRMLS_CC, E_WARNING, "socket() failed: %s (%d)", strerror(errno), errno);
01395               goto bail;
01396        }
01397 
01398        /* passive connection handler */
01399        if (ftp->pasv) {
01400               /* clear the ready status */
01401               ftp->pasv = 1;
01402 
01403               /* connect */
01404               /* Win 95/98 seems not to like size > sizeof(sockaddr_in) */
01405               size = php_sockaddr_size(&ftp->pasvaddr);
01406               tv.tv_sec = ftp->timeout_sec;
01407               tv.tv_usec = 0;
01408               if (php_connect_nonb(fd, (struct sockaddr*) &ftp->pasvaddr, size, &tv) == -1) {
01409                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_connect_nonb() failed: %s (%d)", strerror(errno), errno);
01410                      goto bail;
01411               }
01412 
01413               data->fd = fd;
01414 
01415               ftp->data = data;
01416               return data;
01417        }
01418 
01419 
01420        /* active (normal) connection */
01421 
01422        /* bind to a local address */
01423        php_any_addr(sa->sa_family, &addr, 0);
01424        size = php_sockaddr_size(&addr);
01425 
01426        if (bind(fd, (struct sockaddr*) &addr, size) != 0) {
01427               php_error_docref(NULL TSRMLS_CC, E_WARNING, "bind() failed: %s (%d)", strerror(errno), errno);
01428               goto bail;
01429        }
01430 
01431        if (getsockname(fd, (struct sockaddr*) &addr, &size) != 0) {
01432               php_error_docref(NULL TSRMLS_CC, E_WARNING, "getsockname() failed: %s (%d)", strerror(errno), errno);
01433               goto bail;
01434        }
01435 
01436        if (listen(fd, 5) != 0) {
01437               php_error_docref(NULL TSRMLS_CC, E_WARNING, "listen() failed: %s (%d)", strerror(errno), errno);
01438               goto bail;
01439        }
01440 
01441        data->listener = fd;
01442 
01443 #if HAVE_IPV6 && HAVE_INET_NTOP
01444        if (sa->sa_family == AF_INET6) {
01445               /* need to use EPRT */
01446               char eprtarg[INET6_ADDRSTRLEN + sizeof("|x||xxxxx|")];
01447               char out[INET6_ADDRSTRLEN];
01448               inet_ntop(AF_INET6, &((struct sockaddr_in6*) sa)->sin6_addr, out, sizeof(out));
01449               snprintf(eprtarg, sizeof(eprtarg), "|2|%s|%hu|", out, ntohs(((struct sockaddr_in6 *) &addr)->sin6_port));
01450 
01451               if (!ftp_putcmd(ftp, "EPRT", eprtarg)) {
01452                      goto bail;
01453               }
01454 
01455               if (!ftp_getresp(ftp) || ftp->resp != 200) {
01456                      goto bail;
01457               }
01458 
01459               ftp->data = data;
01460               return data;
01461        }
01462 #endif
01463 
01464        /* send the PORT */
01465        ipbox.ia[0] = ((struct sockaddr_in*) sa)->sin_addr;
01466        ipbox.s[2] = ((struct sockaddr_in*) &addr)->sin_port;
01467        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]);
01468 
01469        if (!ftp_putcmd(ftp, "PORT", arg)) {
01470               goto bail;
01471        }
01472        if (!ftp_getresp(ftp) || ftp->resp != 200) {
01473               goto bail;
01474        }
01475 
01476        ftp->data = data;
01477        return data;
01478 
01479 bail:
01480        if (fd != -1) {
01481               closesocket(fd);
01482        }
01483        efree(data);
01484        return NULL;
01485 }
01486 /* }}} */
01487 
01488 /* {{{ data_accept
01489  */
01490 databuf_t*
01491 data_accept(databuf_t *data, ftpbuf_t *ftp TSRMLS_DC)
01492 {
01493        php_sockaddr_storage addr;
01494        socklen_t                   size;
01495 
01496 #if HAVE_OPENSSL_EXT
01497        SSL_CTX              *ctx;
01498 #endif
01499 
01500        if (data->fd != -1) {
01501               goto data_accepted;
01502        }
01503        size = sizeof(addr);
01504        data->fd = my_accept(ftp, data->listener, (struct sockaddr*) &addr, &size);
01505        closesocket(data->listener);
01506        data->listener = -1;
01507 
01508        if (data->fd == -1) {
01509               efree(data);
01510               return NULL;
01511        }
01512 
01513 data_accepted:
01514 #if HAVE_OPENSSL_EXT
01515        
01516        /* now enable ssl if we need to */
01517        if (ftp->use_ssl && ftp->use_ssl_for_data) {
01518               ctx = SSL_CTX_new(SSLv23_client_method());
01519               if (ctx == NULL) {
01520                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: failed to create the SSL context");
01521                      return 0;
01522               }
01523 
01524               SSL_CTX_set_options(ctx, SSL_OP_ALL);
01525 
01526               data->ssl_handle = SSL_new(ctx);
01527               if (data->ssl_handle == NULL) {
01528                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: failed to create the SSL handle");
01529                      SSL_CTX_free(ctx);
01530                      return 0;
01531               }
01532                      
01533               
01534               SSL_set_fd(data->ssl_handle, data->fd);
01535 
01536               if (ftp->old_ssl) {
01537                      SSL_copy_session_id(data->ssl_handle, ftp->ssl_handle);
01538               }
01539                      
01540               if (SSL_connect(data->ssl_handle) <= 0) {
01541                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: SSL/TLS handshake failed");
01542                      SSL_shutdown(data->ssl_handle);
01543                      return 0;
01544               }
01545                      
01546               data->ssl_active = 1;
01547        }      
01548 
01549 #endif
01550 
01551        return data;
01552 }
01553 /* }}} */
01554 
01555 /* {{{ data_close
01556  */
01557 databuf_t*
01558 data_close(ftpbuf_t *ftp, databuf_t *data)
01559 {
01560        if (data == NULL) {
01561               return NULL;
01562        }
01563        if (data->listener != -1) {
01564 #if HAVE_OPENSSL_EXT
01565               if (data->ssl_active) {
01566                      SSL_shutdown(data->ssl_handle);
01567                      data->ssl_active = 0;
01568               }
01569 #endif                      
01570               closesocket(data->listener);
01571        }      
01572        if (data->fd != -1) {
01573 #if HAVE_OPENSSL_EXT
01574               if (data->ssl_active) {
01575                      SSL_shutdown(data->ssl_handle);
01576                      data->ssl_active = 0;
01577               }
01578 #endif                      
01579               closesocket(data->fd);
01580        }      
01581        if (ftp) {
01582               ftp->data = NULL;
01583        }
01584        efree(data);
01585        return NULL;
01586 }
01587 /* }}} */
01588 
01589 /* {{{ ftp_genlist
01590  */
01591 char**
01592 ftp_genlist(ftpbuf_t *ftp, const char *cmd, const char *path TSRMLS_DC)
01593 {
01594        php_stream    *tmpstream = NULL;
01595        databuf_t     *data = NULL;
01596        char          *ptr;
01597        int           ch, lastch;
01598        int           size, rcvd;
01599        int           lines;
01600        char          **ret = NULL;
01601        char          **entry;
01602        char          *text;
01603 
01604 
01605        if ((tmpstream = php_stream_fopen_tmpfile()) == NULL) {
01606               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to create temporary file.  Check permissions in temporary files directory.");
01607               return NULL;
01608        }
01609 
01610        if (!ftp_type(ftp, FTPTYPE_ASCII)) {
01611               goto bail;
01612        }
01613 
01614        if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
01615               goto bail;
01616        }
01617        ftp->data = data;    
01618 
01619        if (!ftp_putcmd(ftp, cmd, path)) {
01620               goto bail;
01621        }
01622        if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125 && ftp->resp != 226)) {
01623               goto bail;
01624        }
01625 
01626        /* some servers don't open a ftp-data connection if the directory is empty */
01627        if (ftp->resp == 226) {
01628               ftp->data = data_close(ftp, data);
01629               php_stream_close(tmpstream);
01630               return ecalloc(1, sizeof(char**));
01631        }
01632 
01633        /* pull data buffer into tmpfile */
01634        if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) {
01635               goto bail;
01636        }
01637        size = 0;
01638        lines = 0;
01639        lastch = 0;
01640        while ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
01641               if (rcvd == -1) {
01642                      goto bail;
01643               }
01644 
01645               php_stream_write(tmpstream, data->buf, rcvd);
01646 
01647               size += rcvd;
01648               for (ptr = data->buf; rcvd; rcvd--, ptr++) {
01649                      if (*ptr == '\n' && lastch == '\r') {
01650                             lines++;
01651                      } else {
01652                             size++;
01653                      }
01654                      lastch = *ptr;
01655               }
01656        }
01657 
01658        ftp->data = data = data_close(ftp, data);
01659 
01660        php_stream_rewind(tmpstream);
01661 
01662        ret = safe_emalloc((lines + 1), sizeof(char**), size * sizeof(char*));
01663 
01664        entry = ret;
01665        text = (char*) (ret + lines + 1);
01666        *entry = text;
01667        lastch = 0;
01668        while ((ch = php_stream_getc(tmpstream)) != EOF) {
01669               if (ch == '\n' && lastch == '\r') {
01670                      *(text - 1) = 0;
01671                      *++entry = text;
01672               } else {
01673                      *text++ = ch;
01674               }
01675               lastch = ch;
01676        }
01677        *entry = NULL;
01678 
01679        php_stream_close(tmpstream);
01680 
01681        if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
01682               efree(ret);
01683               return NULL;
01684        }
01685 
01686        return ret;
01687 bail:
01688        ftp->data = data_close(ftp, data);
01689        php_stream_close(tmpstream);
01690        if (ret)
01691               efree(ret);
01692        return NULL;
01693 }
01694 /* }}} */
01695 
01696 /* {{{ ftp_nb_get
01697  */
01698 int
01699 ftp_nb_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, ftptype_t type, int resumepos TSRMLS_DC)
01700 {
01701        databuf_t            *data = NULL;
01702        char                 arg[11];
01703 
01704        if (ftp == NULL) {
01705               return PHP_FTP_FAILED;
01706        }
01707 
01708        if (!ftp_type(ftp, type)) {
01709               goto bail;
01710        }
01711 
01712        if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
01713               goto bail;
01714        }
01715 
01716        if (resumepos>0) {
01717               /* We are working on an architecture that supports 64-bit integers
01718                * since php is 32 bit by design, we bail out with warning
01719                */
01720               if (resumepos > 2147483647) {
01721                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "PHP cannot handle files greater than 2147483648 bytes.");
01722                      goto bail;
01723               }
01724               snprintf(arg, sizeof(arg), "%u", resumepos);
01725               if (!ftp_putcmd(ftp, "REST", arg)) {
01726                      goto bail;
01727               }
01728               if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
01729                      goto bail;
01730               }
01731        }
01732 
01733        if (!ftp_putcmd(ftp, "RETR", path)) {
01734               goto bail;
01735        }
01736        if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
01737               goto bail;
01738        }
01739 
01740        if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) {
01741               goto bail;
01742        }
01743 
01744        ftp->data = data;
01745        ftp->stream = outstream;
01746        ftp->lastch = 0;
01747        ftp->nb = 1;
01748 
01749        return (ftp_nb_continue_read(ftp TSRMLS_CC));
01750 
01751 bail:
01752        ftp->data = data_close(ftp, data);
01753        return PHP_FTP_FAILED;
01754 }
01755 /* }}} */
01756 
01757 /* {{{ ftp_nb_continue_read
01758  */
01759 int
01760 ftp_nb_continue_read(ftpbuf_t *ftp TSRMLS_DC)
01761 {
01762        databuf_t     *data = NULL;
01763        char          *ptr;
01764        int           lastch;
01765        size_t        rcvd;
01766        ftptype_t     type;
01767 
01768        data = ftp->data;
01769 
01770        /* check if there is already more data */
01771        if (!data_available(ftp, data->fd)) {
01772               return PHP_FTP_MOREDATA;
01773        }
01774 
01775        type = ftp->type;
01776 
01777        lastch = ftp->lastch;
01778        if ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
01779               if (rcvd == -1) {
01780                      goto bail;
01781               }
01782 
01783               if (type == FTPTYPE_ASCII) {
01784                      for (ptr = data->buf; rcvd; rcvd--, ptr++) {
01785                             if (lastch == '\r' && *ptr != '\n') {
01786                                    php_stream_putc(ftp->stream, '\r');
01787                             }
01788                             if (*ptr != '\r') {
01789                                    php_stream_putc(ftp->stream, *ptr);
01790                             }
01791                             lastch = *ptr;
01792                      }
01793               } else if (rcvd != php_stream_write(ftp->stream, data->buf, rcvd)) {
01794                      goto bail;
01795               }
01796 
01797               ftp->lastch = lastch;
01798               return PHP_FTP_MOREDATA;
01799        }
01800 
01801        if (type == FTPTYPE_ASCII && lastch == '\r') {
01802               php_stream_putc(ftp->stream, '\r');
01803        }
01804 
01805        ftp->data = data = data_close(ftp, data);
01806 
01807        if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
01808               goto bail;
01809        }
01810 
01811        ftp->nb = 0;
01812        return PHP_FTP_FINISHED;
01813 bail:
01814        ftp->nb = 0;
01815        ftp->data = data_close(ftp, data);
01816        return PHP_FTP_FAILED;
01817 }
01818 /* }}} */
01819 
01820 /* {{{ ftp_nb_put
01821  */
01822 int
01823 ftp_nb_put(ftpbuf_t *ftp, const char *path, php_stream *instream, ftptype_t type, int startpos TSRMLS_DC)
01824 {
01825        databuf_t            *data = NULL;
01826        char                 arg[11];
01827 
01828        if (ftp == NULL) {
01829               return 0;
01830        }
01831        if (!ftp_type(ftp, type)) {
01832               goto bail;
01833        }
01834        if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
01835               goto bail;
01836        }
01837        if (startpos > 0) {
01838               if (startpos > 2147483647) {
01839                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "PHP cannot handle files with a size greater than 2147483647 bytes.");
01840                      goto bail;
01841               }
01842               snprintf(arg, sizeof(arg), "%u", startpos);
01843               if (!ftp_putcmd(ftp, "REST", arg)) {
01844                      goto bail;
01845               }
01846               if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
01847                      goto bail;
01848               }
01849        }
01850 
01851        if (!ftp_putcmd(ftp, "STOR", path)) {
01852               goto bail;
01853        }
01854        if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
01855               goto bail;
01856        }
01857        if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) { 
01858               goto bail;
01859        }
01860        ftp->data = data;
01861        ftp->stream = instream;
01862        ftp->lastch = 0;
01863        ftp->nb = 1;
01864 
01865        return (ftp_nb_continue_write(ftp TSRMLS_CC));
01866 
01867 bail:
01868        ftp->data = data_close(ftp, data);
01869        return PHP_FTP_FAILED;
01870 }
01871 /* }}} */
01872 
01873 
01874 /* {{{ ftp_nb_continue_write
01875  */
01876 int
01877 ftp_nb_continue_write(ftpbuf_t *ftp TSRMLS_DC)
01878 {
01879        int                  size;
01880        char                 *ptr;
01881        int                  ch;
01882 
01883        /* check if we can write more data */
01884        if (!data_writeable(ftp, ftp->data->fd)) {
01885               return PHP_FTP_MOREDATA;
01886        }
01887 
01888        size = 0;
01889        ptr = ftp->data->buf;
01890        while (!php_stream_eof(ftp->stream) && (ch = php_stream_getc(ftp->stream)) != EOF) {
01891 
01892               if (ch == '\n' && ftp->type == FTPTYPE_ASCII) {
01893                      *ptr++ = '\r';
01894                      size++;
01895               }
01896 
01897               *ptr++ = ch;
01898               size++;
01899 
01900               /* flush if necessary */
01901               if (FTP_BUFSIZE - size < 2) {
01902                      if (my_send(ftp, ftp->data->fd, ftp->data->buf, size) != size) {
01903                             goto bail;
01904                      }
01905                      return PHP_FTP_MOREDATA;
01906               }
01907        }
01908 
01909        if (size && my_send(ftp, ftp->data->fd, ftp->data->buf, size) != size) {
01910               goto bail;
01911        }
01912        ftp->data = data_close(ftp, ftp->data);
01913  
01914        if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
01915               goto bail;
01916        }
01917        ftp->nb = 0;
01918        return PHP_FTP_FINISHED;
01919 bail:
01920        ftp->data = data_close(ftp, ftp->data);
01921        ftp->nb = 0;
01922        return PHP_FTP_FAILED;
01923 }
01924 /* }}} */
01925 
01926 #endif /* HAVE_FTP */
01927 
01928 /*
01929  * Local variables:
01930  * tab-width: 4
01931  * c-basic-offset: 4
01932  * End:
01933  * vim600: sw=4 ts=4 fdm=marker
01934  * vim<600: sw=4 ts=4
01935  */