]> granicus.if.org Git - php/commitdiff
Abtract some ftp connection/negotiation steps out and add support for stream_opendir...
authorSara Golemon <pollita@php.net>
Thu, 24 Apr 2003 20:32:14 +0000 (20:32 +0000)
committerSara Golemon <pollita@php.net>
Thu, 24 Apr 2003 20:32:14 +0000 (20:32 +0000)
ext/standard/ftp_fopen_wrapper.c

index 3ec09c1fa7795cf3f05bff364f3ba7720ba966d4..a60be294a1ce15ead2c578ceca8735cb400c7dd7 100644 (file)
@@ -113,49 +113,18 @@ static int php_stream_ftp_stream_close(php_stream_wrapper *wrapper,
        return 0;
 }
 
-
-static php_stream_wrapper_ops ftp_stream_wops = {
-       php_stream_url_wrap_ftp,
-       php_stream_ftp_stream_close, /* stream_close */
-       php_stream_ftp_stream_stat,
-       NULL, /* stat_url */
-       NULL, /* opendir */
-       "FTP"
-};
-
-PHPAPI php_stream_wrapper php_stream_ftp_wrapper =     {
-       &ftp_stream_wops,
-       NULL,
-       1 /* is_url */
-};
-
-
-/* {{{ php_fopen_url_wrap_ftp
+/* {{{ php_ftp_fopen_connect
  */
-php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC)
+static php_stream *php_ftp_fopen_connect(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context,
+                                                                        php_stream **preuseid, php_url **presource, int *puse_ssl, int *puse_ssl_on_data
+                                                                        STREAMS_DC TSRMLS_DC)
 {
-       php_stream *stream=NULL, *datastream=NULL;
-       php_url *resource=NULL;
-       char tmp_line[512];
-       char ip[sizeof("123.123.123.123")];
-       unsigned short portno;
+       php_stream *stream = NULL, *reuseid = NULL;
+       php_url *resource = NULL;
+       int result, use_ssl, use_ssl_on_data = 0;
        char *scratch;
-       int result;
-       int i, use_ssl;
-       int use_ssl_on_data=0;
-       php_stream *reuseid=NULL;
-       char *tpath, *ttpath, *hoststart=NULL;
-       size_t file_size = 0;
-       zval **tmpzval;
-       int allow_overwrite = 0;
-
-       tmp_line[0] = '\0';
+       char tmp_line[512];
 
-       if (strpbrk(mode, "a+")) {
-               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "FTP does not support simultaneous read/write connections.");
-               return NULL;
-       }
-       
        resource = php_url_parse((char *) path);
        if (resource == NULL || resource->path == NULL)
                return NULL;
@@ -169,7 +138,7 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, char *path, ch
        stream = php_stream_sock_open_host(resource->host, resource->port, SOCK_STREAM, NULL, 0);
        if (stream == NULL) {
                result = 0; /* silence */
-               goto errexit;
+               goto connect_errexit;
        }
 
        php_stream_context_set(stream, context);
@@ -179,7 +148,7 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, char *path, ch
        result = GET_FTP_RESULT(stream);
        if (result > 299 || result < 200) {
                php_stream_notify_error(context, PHP_STREAM_NOTIFY_FAILURE, tmp_line, result);
-               goto errexit;
+               goto connect_errexit;
        }
 
        if (use_ssl)    {
@@ -217,7 +186,7 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, char *path, ch
                        php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "Unable to activate SSL mode");
                        php_stream_close(stream);
                        stream = NULL;
-                       goto errexit;
+                       goto connect_errexit;
                }
        
                /* set PBSZ to 0 */
@@ -282,9 +251,147 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, char *path, ch
                        php_stream_notify_info(context, PHP_STREAM_NOTIFY_AUTH_RESULT, tmp_line, result);
                }
        }
-       if (result > 299 || result < 200)
-               goto errexit;
+       if (result > 299 || result < 200) {
+               goto connect_errexit;
+       }
+
+       if (puse_ssl) {
+               *puse_ssl = use_ssl;
+       }
+       if (puse_ssl_on_data) {
+               *puse_ssl_on_data = use_ssl_on_data;
+       }
+       if (preuseid) {
+               *preuseid = reuseid;
+       }
+       if (presource) {
+               *presource = resource;
+       }
+
+       return stream;
+
+ connect_errexit:
+       if (stream) {
+               php_stream_close(stream);
+       }
+
+       return NULL;
+}
+/* }}} */
+
+/* {{{ php_fopen_do_pasv
+ */
+static unsigned short php_fopen_do_pasv(php_stream *stream, char *ip, int ip_size, char **phoststart STREAMS_DC TSRMLS_DC)
+{
+       char tmp_line[512];
+       int result, i;
+       unsigned short portno;
+       char *tpath, *ttpath, *hoststart=NULL;
+
+       /* We try EPSV first, needed for IPv6 and works on some IPv4 servers */
+       php_stream_write_string(stream, "EPSV\r\n");
+       result = GET_FTP_RESULT(stream);
+
+       /* check if we got a 229 response */
+       if (result != 229) {
+               /* EPSV failed, let's try PASV */
+               php_stream_write_string(stream, "PASV\r\n");
+               result = GET_FTP_RESULT(stream);
+               
+               /* make sure we got a 227 response */
+               if (result != 227) {
+                       return 0;
+               }
+
+               /* parse pasv command (129, 80, 95, 25, 13, 221) */
+               tpath = tmp_line;
+               /* skip over the "227 Some message " part */
+               for (tpath += 4; *tpath && !isdigit((int) *tpath); tpath++);
+               if (!*tpath) {
+                       return 0;
+               }
+               /* skip over the host ip, to get the port */
+               hoststart = tpath;
+               for (i = 0; i < 4; i++) {
+                       for (; isdigit((int) *tpath); tpath++);
+                       if (*tpath != ',') {
+                               return 0;
+                       }
+                       *tpath='.';     
+                       tpath++;
+               }
+               tpath[-1] = '\0';
+               memcpy(ip, hoststart, ip_size);
+               ip[ip_size-1] = '\0';
+               hoststart = ip;
+               
+               /* pull out the MSB of the port */
+               portno = (unsigned short) strtoul(tpath, &ttpath, 10) * 256;
+               if (ttpath == NULL) {
+                       /* didn't get correct response from PASV */
+                       return 0;
+               }
+               tpath = ttpath;
+               if (*tpath != ',') {
+                       return 0;
+               }
+               tpath++;
+               /* pull out the LSB of the port */
+               portno += (unsigned short) strtoul(tpath, &ttpath, 10);
+       } else {
+               /* parse epsv command (|||6446|) */
+               for (i = 0, tpath = tmp_line + 4; *tpath; tpath++) {
+                       if (*tpath == '|') {
+                               i++;
+                               if (i == 3)
+                                       break;
+                       }
+               }
+               if (i < 3) {
+                       return 0;
+               }
+               /* pull out the port */
+               portno = (unsigned short) strtoul(tpath + 1, &ttpath, 10);
+       }
        
+       if (ttpath == NULL) {
+               /* didn't get correct response from EPSV/PASV */
+               return 0;
+       }
+
+       if (phoststart) {
+               *phoststart = hoststart;
+       }       
+
+       return portno;
+}
+/* }}} */
+
+/* {{{ php_fopen_url_wrap_ftp
+ */
+php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC)
+{
+       php_stream *stream = NULL, *datastream = NULL;
+       php_url *resource = NULL;
+       char tmp_line[512];
+       char ip[sizeof("123.123.123.123")];
+       unsigned short portno;
+       char *scratch, *hoststart = NULL;
+       int result, use_ssl, use_ssl_on_data=0;
+       php_stream *reuseid=NULL;
+       size_t file_size = 0;
+       zval **tmpzval;
+       int allow_overwrite = 0;
+
+       tmp_line[0] = '\0';
+
+       if (strpbrk(mode, "a+")) {
+               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "FTP does not support simultaneous read/write connections.");
+               return NULL;
+       }
+
+       stream = php_ftp_fopen_connect(wrapper, path, mode, options, opened_path, context, &reuseid, &resource, &use_ssl, &use_ssl_on_data STREAMS_CC TSRMLS_CC);
+
        /* set the connection to be binary */
        php_stream_write_string(stream, "TYPE I\r\n");
        result = GET_FTP_RESULT(stream);
@@ -337,73 +444,13 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, char *path, ch
        }
 
        /* set up the passive connection */
+       portno = php_fopen_do_pasv(stream, ip, sizeof(ip), &hoststart STREAMS_CC TSRMLS_CC);
 
-       /* We try EPSV first, needed for IPv6 and works on some IPv4 servers */
-       php_stream_write_string(stream, "EPSV\r\n");
-       result = GET_FTP_RESULT(stream);
-
-       /* check if we got a 229 response */
-       if (result != 229) {
-               /* EPSV failed, let's try PASV */
-               php_stream_write_string(stream, "PASV\r\n");
-               result = GET_FTP_RESULT(stream);
-               
-               /* make sure we got a 227 response */
-               if (result != 227)
-                       goto errexit;
-
-               /* parse pasv command (129, 80, 95, 25, 13, 221) */
-               tpath = tmp_line;
-               /* skip over the "227 Some message " part */
-               for (tpath += 4; *tpath && !isdigit((int) *tpath); tpath++);
-               if (!*tpath)
-                       goto errexit;
-               /* skip over the host ip, to get the port */
-               hoststart = tpath;
-               for (i = 0; i < 4; i++) {
-                       for (; isdigit((int) *tpath); tpath++);
-                       if (*tpath != ',')
-                               goto errexit;
-                       *tpath='.';     
-                       tpath++;
-               }
-               tpath[-1] = '\0';
-               memcpy(ip, hoststart, sizeof(ip));
-               ip[sizeof(ip)-1] = '\0';
-               hoststart = ip;
-               
-               /* pull out the MSB of the port */
-               portno = (unsigned short) strtoul(tpath, &ttpath, 10) * 256;
-               if (ttpath == NULL) {
-                       /* didn't get correct response from PASV */
-                       goto errexit;
-               }
-               tpath = ttpath;
-               if (*tpath != ',')
-                       goto errexit;
-               tpath++;
-               /* pull out the LSB of the port */
-               portno += (unsigned short) strtoul(tpath, &ttpath, 10);
-       } else {
-               /* parse epsv command (|||6446|) */
-               for (i = 0, tpath = tmp_line + 4; *tpath; tpath++) {
-                       if (*tpath == '|') {
-                               i++;
-                               if (i == 3)
-                                       break;
-                       }
-               }
-               if (i < 3)
-                       goto errexit;
-               /* pull out the port */
-               portno = (unsigned short) strtoul(tpath + 1, &ttpath, 10);
-       }
-       
-       if (ttpath == NULL) {
-               /* didn't get correct response from EPSV/PASV */
+       if (!portno) {
                goto errexit;
        }
-       
+
+       /* Send RETR/STOR command */
        if (mode[0] == 'r') {
                /* retrieve file */
                php_stream_write_string(stream, "RETR ");
@@ -475,6 +522,178 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, char *path, ch
 }
 /* }}} */
 
+/* {{{ php_ftp_dirsteam_read
+ */
+static size_t php_ftp_dirstream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
+{
+       php_stream_dirent *ent = (php_stream_dirent *)buf;
+       php_stream *innerstream = (php_stream *)stream->abstract;
+       size_t tmp_len;
+       char *basename;
+
+       if (count != sizeof(php_stream_dirent)) {
+               return 0;
+       }
+
+       if (php_stream_eof(innerstream)) {
+               return 0;
+       }
+
+       if (!php_stream_get_line(innerstream, ent->d_name, sizeof(ent->d_name), &tmp_len)) {
+               return 0;
+       }
+       if (!(basename = php_basename(ent->d_name, tmp_len, NULL, 0))) {
+               return 0;
+       }
+
+       if (strlen(basename) == 0) {
+               efree(basename);
+               return 0;
+       }
+       strcpy(ent->d_name, basename);
+       efree(basename);
+
+       return sizeof(php_stream_dirent);
+}
+/* }}} */
+
+/* {{{ php_ftp_dirstream_close
+ */
+static int php_ftp_dirstream_close(php_stream *stream, int close_handle TSRMLS_DC)
+{
+       php_stream *innerstream = (php_stream *)stream->abstract;
+
+       if (innerstream->wrapperdata) {
+               php_stream_close((php_stream *)innerstream->wrapperdata);
+               innerstream->wrapperdata = NULL;
+       }
+       php_stream_close((php_stream *)stream->abstract);
+       stream->abstract = NULL;
+
+       return 0;
+}
+/* }}} */
+
+/* ftp dirstreams only need to support read and close operations,
+   They can't be rewound because the underlying ftp stream can't be rewound. */
+static php_stream_ops php_ftp_dirstream_ops = {
+       NULL, /* write */
+       php_ftp_dirstream_read, /* read */
+       php_ftp_dirstream_close, /* close */
+       NULL, /* flush */
+       "ftpdir",
+       NULL, /* rewind */
+       NULL, /* cast */
+       NULL, /* stat */
+       NULL  /* set option */
+};
+
+/* {{{ php_stream_ftp_opendir
+ */
+php_stream * php_stream_ftp_opendir(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC)
+{
+       php_stream *stream, *reuseid, *datastream = NULL;
+       php_url *resource;
+       int result, use_ssl, use_ssl_on_data = 0;
+       char *hoststart = NULL, tmp_line[512];
+       char ip[sizeof("123.123.123.123")];
+       unsigned short portno;
+
+       stream = php_ftp_fopen_connect(wrapper, path, mode, options, opened_path, context, &reuseid, &resource, &use_ssl, &use_ssl_on_data STREAMS_CC TSRMLS_CC);
+
+       /* set the connection to be ascii */
+       php_stream_write_string(stream, "TYPE A\r\n");
+       result = GET_FTP_RESULT(stream);
+       if (result > 299 || result < 200)
+               goto opendir_errexit;
+
+       /* set up the passive connection */
+       portno = php_fopen_do_pasv(stream, ip, sizeof(ip), &hoststart STREAMS_CC TSRMLS_CC);
+
+       if (!portno) {
+               goto opendir_errexit;
+       }
+
+       php_stream_write_string(stream, "NLST ");
+       if (resource->path != NULL) {
+               php_stream_write_string(stream, resource->path);
+       } else {
+               php_stream_write_string(stream, "/");
+       }
+       php_stream_write_string(stream, "\r\n");
+       
+       /* open the data channel */
+       if (hoststart == NULL) {
+               hoststart = resource->host;
+       }
+       datastream = php_stream_sock_open_host(hoststart, portno, SOCK_STREAM, 0, 0);
+       if (datastream == NULL) {
+               goto opendir_errexit;
+       }
+
+       result = GET_FTP_RESULT(stream);
+       if (result != 150 && result != 125) {
+               /* Could not retrieve or send the file 
+                * this data will only be sent to us after connection on the data port was initiated.
+                */
+               php_stream_close(datastream);
+               datastream = NULL;
+               goto opendir_errexit;   
+       }
+       
+       /* close control connection if not in ssl mode */
+       if (!use_ssl) {
+               php_stream_write_string(stream, "QUIT\r\n");
+               php_stream_close(stream);
+               stream = NULL;
+       }
+               
+       php_stream_context_set(datastream, context);
+
+       if (use_ssl_on_data && (php_stream_xport_crypto_setup(stream,
+                       STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL TSRMLS_CC) < 0 ||
+                       php_stream_xport_crypto_enable(stream, 1 TSRMLS_CC) < 0)) {
+
+               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "Unable to activate SSL mode");
+               php_stream_close(datastream);
+               datastream = NULL;
+               goto opendir_errexit;
+       }
+
+       /* remember control stream */   
+       datastream->wrapperdata = (zval *)stream;
+
+       php_url_free(resource);
+       return php_stream_alloc(&php_ftp_dirstream_ops, datastream, 0, mode);
+
+ opendir_errexit:
+       php_url_free(resource);
+       if (stream) {
+               php_stream_notify_error(context, PHP_STREAM_NOTIFY_FAILURE, tmp_line, result);
+               php_stream_close(stream);
+       }
+       if (tmp_line[0] != '\0')
+               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "FTP server reports %s", tmp_line);
+       return NULL;
+}
+/* }}} */
+
+static php_stream_wrapper_ops ftp_stream_wops = {
+       php_stream_url_wrap_ftp,
+       php_stream_ftp_stream_close, /* stream_close */
+       php_stream_ftp_stream_stat,
+       NULL, /* stat_url */
+       php_stream_ftp_opendir, /* opendir */
+       "FTP"
+};
+
+PHPAPI php_stream_wrapper php_stream_ftp_wrapper =     {
+       &ftp_stream_wops,
+       NULL,
+       1 /* is_url */
+};
+
+
 /*
  * Local variables:
  * tab-width: 4