diff -Nru php-ssh2-0.11.2/debian/changelog php-ssh2-0.11.3/debian/changelog --- php-ssh2-0.11.2/debian/changelog 2010-12-12 02:40:53.000000000 +0000 +++ php-ssh2-0.11.3/debian/changelog 2012-03-20 17:29:21.000000000 +0000 @@ -1,3 +1,15 @@ +php-ssh2 (0.11.3-0~ppa1~precise) precise; urgency=low + + * New upstream release + + -- Nathan Rennie-Waldock Tue, 20 Mar 2012 17:29:21 +0000 + +php-ssh2 (0.11.3-0~ppa1~oneiric) oneiric; urgency=low + + * New upstream release + + -- Nathan Rennie-Waldock Tue, 20 Mar 2012 17:29:19 +0000 + php-ssh2 (0.11.2-1) unstable; urgency=low * New upstream release diff -Nru php-ssh2-0.11.2/package.xml php-ssh2-0.11.3/package.xml --- php-ssh2-0.11.2/package.xml 2010-11-03 22:24:33.000000000 +0000 +++ php-ssh2-0.11.3/package.xml 2011-09-27 20:05:05.000000000 +0000 @@ -1,5 +1,5 @@ - + ssh2 pecl.php.net Bindings for the libssh2 library @@ -23,10 +23,10 @@ pollita@php.net no - 2010-11-03 - + 2011-09-27 + - 0.11.2 + 0.11.3 0.11.0 @@ -35,16 +35,19 @@ PHP License -- Fixed the extension version info - . no other changes since 0.11.1 +- Fixed bug#24349 (ssh2_sftp_mkdir fails with trailing slash) +- Fixed bug#22729 (using ssh2.sftp stream on 64bit vs. 32bit) +- Fixed bug#22671 (trailing spaces trimmed from filenames) +- Fixed bug#17142 (Missing EOF detection in ssh2.sftp:// streams) +- Fixed bug#16944 (Segmentation fault SIGSEGV with protected variable with ssh2) - - - - - + + + + + @@ -75,6 +78,22 @@ PHP License +- Fixed the extension version info +. no other changes since 0.11.1 + + + + 2009-11-28 + + 0.11.1 + 0.11.0 + + + beta + beta + + PHP License + - Fixed bug #9500, SSH2 sockets not being closed properly because of close vs closesocket difference - Fixed Windows support - Support for latest libssh2 release diff -Nru php-ssh2-0.11.2/ssh2-0.11.2/config.m4 php-ssh2-0.11.3/ssh2-0.11.2/config.m4 --- php-ssh2-0.11.2/ssh2-0.11.2/config.m4 2010-11-03 22:24:33.000000000 +0000 +++ php-ssh2-0.11.3/ssh2-0.11.2/config.m4 1970-01-01 00:00:00.000000000 +0000 @@ -1,81 +0,0 @@ -dnl $Id: config.m4 305068 2010-11-03 22:24:19Z pajoye $ -dnl config.m4 for extension ssh2 - -PHP_ARG_WITH(ssh2, for ssh2 support, -[ --with-ssh2=[DIR] Include ssh2 support]) - -if test "$PHP_SSH2" != "no"; then - SEARCH_PATH="/usr/local /usr" - SEARCH_FOR="/include/libssh2.h" - if test -r $PHP_SSH2/$SEARCH_FOR; then # path given as parameter - SSH2_DIR=$PHP_SSH2 - else - AC_MSG_CHECKING([for ssh2 files in default path]) - for i in $SEARCH_PATH ; do - if test -r $i/$SEARCH_FOR; then - SSH2_DIR=$i - AC_MSG_RESULT(found in $i) - fi - done - fi - - if test -z "$SSH2_DIR"; then - AC_MSG_RESULT([not found]) - AC_MSG_ERROR([The required libssh2 library was not found. You can obtain that package from http://sourceforge.net/projects/libssh2/]) - fi - - PHP_ADD_INCLUDE($SSH2_DIR/include) - - LIBNAME=ssh2 - LIBSYMBOL=libssh2_banner_set - - PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, - [ - PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $SSH2_DIR/lib, SSH2_SHARED_LIBADD) - AC_DEFINE(HAVE_SSH2LIB,1,[Have libssh2]) - ],[ - AC_MSG_ERROR([libssh2 version >= 0.4 not found]) - ],[ - -L$SSH2_DIR/lib -lm - ]) - - PHP_CHECK_LIBRARY($LIBNAME,libssh2_channel_forward_listen_ex, - [ - AC_DEFINE(PHP_SSH2_REMOTE_FORWARDING, 1, [Have libssh2 with remote forwarding]) - ],[ - AC_MSG_WARN([libssh2 <= 0.4, remote forwarding not enabled]) - ],[ - -L$SSH2_DIR/lib -lm - ]) - - PHP_CHECK_LIBRARY($LIBNAME,libssh2_userauth_hostbased_fromfile_ex, - [ - AC_DEFINE(PHP_SSH2_HOSTBASED_AUTH, 1, [Have libssh2 with hostbased authentication]) - ],[ - AC_MSG_WARN([libssh2 <= 0.6, hostbased authentication not enabled]) - ],[ - -L$SSH2_DIR/lib -lm - ]) - - PHP_CHECK_LIBRARY($LIBNAME,libssh2_poll, - [ - AC_DEFINE(PHP_SSH2_POLL, 1, [Have libssh2 with poll() support]) - ],[ - AC_MSG_WARN([libssh2 <= 0.7, poll support not enabled]) - ],[ - -L$SSH2_DIR/lib -lm - ]) - - PHP_CHECK_LIBRARY($LIBNAME,libssh2_publickey_init, - [ - AC_DEFINE(PHP_SSH2_PUBLICKEY_SUBSYSTEM, 1, [Have libssh2 with publickey subsystem support]) - ],[ - AC_MSG_WARN([libssh2 <= 0.11, publickey subsystem support not enabled]) - ],[ - -L$SSH2_DIR/lib -lm - ]) - - PHP_SUBST(SSH2_SHARED_LIBADD) - - PHP_NEW_EXTENSION(ssh2, ssh2.c ssh2_fopen_wrappers.c ssh2_sftp.c, $ext_shared) -fi diff -Nru php-ssh2-0.11.2/ssh2-0.11.2/php_ssh2.h php-ssh2-0.11.3/ssh2-0.11.2/php_ssh2.h --- php-ssh2-0.11.2/ssh2-0.11.2/php_ssh2.h 2010-11-03 22:24:33.000000000 +0000 +++ php-ssh2-0.11.3/ssh2-0.11.2/php_ssh2.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,213 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | PHP Version 4 | - +----------------------------------------------------------------------+ - | Copyright (c) 1997-2006 The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 2.02 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available at through the world-wide-web at | - | http://www.php.net/license/2_02.txt. | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Author: Sara Golemon | - +----------------------------------------------------------------------+ - - $Id: php_ssh2.h 305068 2010-11-03 22:24:19Z pajoye $ -*/ - -#ifndef PHP_SSH2_H -#define PHP_SSH2_H - -#include -#include -#include "ext/standard/url.h" - -#define PHP_SSH2_VERSION "0.11.2" -#define PHP_SSH2_DEFAULT_PORT 22 - -/* Exported Constants */ -#define PHP_SSH2_FINGERPRINT_MD5 0x0000 -#define PHP_SSH2_FINGERPRINT_SHA1 0x0001 -#define PHP_SSH2_FINGERPRINT_HEX 0x0000 -#define PHP_SSH2_FINGERPRINT_RAW 0x0002 - -#define PHP_SSH2_TERM_UNIT_CHARS 0x0000 -#define PHP_SSH2_TERM_UNIT_PIXELS 0x0001 - -#define PHP_SSH2_DEFAULT_TERMINAL "vanilla" -#define PHP_SSH2_DEFAULT_TERM_WIDTH 80 -#define PHP_SSH2_DEFAULT_TERM_HEIGHT 25 -#define PHP_SSH2_DEFAULT_TERM_UNIT PHP_SSH2_TERM_UNIT_CHARS - -#define PHP_SSH2_SESSION_RES_NAME "SSH2 Session" -#define PHP_SSH2_CHANNEL_STREAM_NAME "SSH2 Channel" -#define PHP_SSH2_LISTENER_RES_NAME "SSH2 Listener" -#define PHP_SSH2_SFTP_RES_NAME "SSH2 SFTP" -#define PHP_SSH2_PKEY_SUBSYS_RES_NAME "SSH2 Publickey Subsystem" - -#define PHP_SSH2_SFTP_STREAM_NAME "SSH2 SFTP File" -#define PHP_SSH2_SFTP_DIRSTREAM_NAME "SSH2 SFTP Directory" -#define PHP_SSH2_SFTP_WRAPPER_NAME "SSH2 SFTP" - -#define PHP_SSH2_LISTEN_MAX_QUEUED 16 - -#define PHP_SSH2_DEFAULT_POLL_TIMEOUT 30 - -extern zend_module_entry ssh2_module_entry; -#define phpext_ssh2_ptr &ssh2_module_entry - -typedef struct _php_ssh2_session_data { - /* Userspace callback functions */ - zval *ignore_cb; - zval *debug_cb; - zval *macerror_cb; - zval *disconnect_cb; - - int socket; - -#ifdef ZTS - /* Avoid unnecessary TSRMLS_FETCH() calls */ - TSRMLS_D; -#endif -} php_ssh2_session_data; - -typedef struct _php_ssh2_sftp_data { - LIBSSH2_SESSION *session; - LIBSSH2_SFTP *sftp; - - int session_rsrcid; -} php_ssh2_sftp_data; - -#ifdef PHP_SSH2_REMOTE_FORWARDING -typedef struct _php_ssh2_listener_data { - LIBSSH2_SESSION *session; - LIBSSH2_LISTENER *listener; - - int session_rsrcid; -} php_ssh2_listener_data; -#endif - -#ifdef PHP_SSH2_PUBLICKEY_SUBSYSTEM -#include "libssh2_publickey.h" - -typedef struct _php_ssh2_pkey_subsys_data { - LIBSSH2_SESSION *session; - LIBSSH2_PUBLICKEY *pkey; - - int session_rsrcid; -} php_ssh2_pkey_subsys_data; -#endif - -#ifndef PHP_WIN32 -#define closesocket(s) close(s) -#endif - -#ifdef ZTS -#define SSH2_TSRMLS_SET(datap) ((php_ssh2_session_data*)(datap))->tsrm_ls = TSRMLS_C -#define SSH2_TSRMLS_FETCH(datap) TSRMLS_D = ((php_ssh2_session_data*)(datap))->tsrm_ls -#else -#define SSH2_TSRMLS_SET(datap) -#define SSH2_TSRMLS_FETCH(datap) -#endif - -#if (PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION >= 3) -#define ZEND_IS_CALLABLE_TSRMLS_CC TSRMLS_CC -#else -#define ZEND_IS_CALLABLE_TSRMLS_CC -#endif - -/* < 5.3 compatibility */ -#ifndef Z_REFCOUNT_P -#define Z_REFCOUNT_P(pz) (pz)->refcount -#define Z_REFCOUNT_PP(ppz) Z_REFCOUNT_P(*(ppz)) -#endif - -#ifndef Z_SET_REFCOUNT_P -#define Z_SET_REFCOUNT_P(pz, rc) (pz)->refcount = rc -#define Z_SET_REFCOUNT_PP(ppz, rc) Z_SET_REFCOUNT_P(*(ppz), rc) -#endif - -#ifndef Z_ISREF_P -#define Z_ISREF_P(pz) (pz)->is_ref -#define Z_ISREF_PP(ppz) Z_ISREF_P(*(ppz)) -#endif - -#ifndef Z_SET_ISREF_P -#define Z_SET_ISREF_P(pz) (pz)->is_ref = 1 -#define Z_SET_ISREF_PP(ppz) Z_SET_ISREF_P(*(ppz)) -#endif - -#ifndef Z_UNSET_ISREF_P -#define Z_UNSET_ISREF_P(pz) (pz)->is_ref = 0 -#define Z_UNSET_ISREF_PP(ppz) Z_UNSET_ISREF_P(*(ppz)) -#endif - - -typedef struct _php_ssh2_channel_data { - LIBSSH2_CHANNEL *channel; - - /* Distinguish which stream we should read/write from/to */ - unsigned int streamid; - char is_blocking; - - /* Resource ID, zend_list_addref() when opening, zend_list_delete() when closing */ - long session_rsrc; - - /* Allow one stream to be closed while the other is kept open */ - unsigned char *refcount; - -} php_ssh2_channel_data; - -/* In ssh2_fopen_wrappers.c */ -PHP_FUNCTION(ssh2_shell); -PHP_FUNCTION(ssh2_exec); -PHP_FUNCTION(ssh2_tunnel); -PHP_FUNCTION(ssh2_scp_recv); -PHP_FUNCTION(ssh2_scp_send); -PHP_FUNCTION(ssh2_fetch_stream); - -/* In ssh2_sftp.c */ -PHP_FUNCTION(ssh2_sftp); - -PHP_FUNCTION(ssh2_sftp_rename); -PHP_FUNCTION(ssh2_sftp_unlink); -PHP_FUNCTION(ssh2_sftp_mkdir); -PHP_FUNCTION(ssh2_sftp_rmdir); -PHP_FUNCTION(ssh2_sftp_stat); -PHP_FUNCTION(ssh2_sftp_lstat); -PHP_FUNCTION(ssh2_sftp_symlink); -PHP_FUNCTION(ssh2_sftp_readlink); -PHP_FUNCTION(ssh2_sftp_realpath); - -LIBSSH2_SESSION *php_ssh2_session_connect(char *host, int port, zval *methods, zval *callbacks TSRMLS_DC); -void php_ssh2_sftp_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC); -php_url *php_ssh2_fopen_wraper_parse_path( char *path, char *type, php_stream_context *context, - LIBSSH2_SESSION **psession, int *presource_id, - LIBSSH2_SFTP **psftp, int *psftp_rsrcid - TSRMLS_DC); - -extern php_stream_ops php_ssh2_channel_stream_ops; - -extern php_stream_wrapper php_ssh2_stream_wrapper_shell; -extern php_stream_wrapper php_ssh2_stream_wrapper_exec; -extern php_stream_wrapper php_ssh2_stream_wrapper_tunnel; -extern php_stream_wrapper php_ssh2_stream_wrapper_scp; -extern php_stream_wrapper php_ssh2_sftp_wrapper; - -/* Resource list entries */ -extern int le_ssh2_session; -extern int le_ssh2_sftp; - -#endif /* PHP_SSH2_H */ - - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * indent-tabs-mode: t - * End: - */ diff -Nru php-ssh2-0.11.2/ssh2-0.11.2/ssh2.c php-ssh2-0.11.3/ssh2-0.11.2/ssh2.c --- php-ssh2-0.11.2/ssh2-0.11.2/ssh2.c 2010-11-03 22:24:33.000000000 +0000 +++ php-ssh2-0.11.3/ssh2-0.11.2/ssh2.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1357 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | PHP Version 4 | - +----------------------------------------------------------------------+ - | Copyright (c) 1997-2006 The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 2.02 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available at through the world-wide-web at | - | http://www.php.net/license/2_02.txt. | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Author: Sara Golemon | - +----------------------------------------------------------------------+ - - $Id: ssh2.c 305068 2010-11-03 22:24:19Z pajoye $ -*/ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "php.h" -#include "ext/standard/info.h" -#include "ext/standard/file.h" -#include "php_ssh2.h" -#include "main/php_network.h" - -/* Internal Constants */ -#ifndef SHA_DIGEST_LENGTH -#define SHA_DIGEST_LENGTH 20 -#endif - -#ifndef MD5_DIGEST_LENGTH -#define MD5_DIGEST_LENGTH 16 -#endif - -/* True global resources - no need for thread safety here */ -int le_ssh2_session; -#ifdef PHP_SSH2_REMOTE_FORWARDING -int le_ssh2_listener; -#endif -int le_ssh2_sftp; -#ifdef PHP_SSH2_PUBLICKEY_SUBSYSTEM -int le_ssh2_pkey_subsys; -#endif - -ZEND_BEGIN_ARG_INFO(php_ssh2_first_arg_force_ref, 0) - ZEND_ARG_PASS_INFO(1) -ZEND_END_ARG_INFO() - -/* ************* - * Callbacks * - ************* */ - -#ifdef ZTS -#define PHP_SSH2_TSRMLS_FETCH() TSRMLS_D = *(void****)abstract; -#else -#define PHP_SSH2_TSRMLS_FETCH() -#endif - -/* {{{ php_ssh2_alloc_cb - * Wrap emalloc() - */ -static LIBSSH2_ALLOC_FUNC(php_ssh2_alloc_cb) -{ - return emalloc(count); -} -/* }}} */ - -/* {{{ php_ssh2_free_cb - * Wrap efree() - */ -static LIBSSH2_FREE_FUNC(php_ssh2_free_cb) -{ - efree(ptr); -} -/* }}} */ - -/* {{{ php_ssh2_realloc_cb - * Wrap erealloc() - */ -static LIBSSH2_REALLOC_FUNC(php_ssh2_realloc_cb) -{ - return erealloc(ptr, count); -} -/* }}} */ - -/* {{{ php_ssh2_debug_cb - * Debug packets - */ -LIBSSH2_DEBUG_FUNC(php_ssh2_debug_cb) -{ - php_ssh2_session_data *data; - zval *zdisplay, *zmessage, *zlanguage; - zval **args[3]; - SSH2_TSRMLS_FETCH(*abstract); - - if (!abstract || !*abstract) { - return; - } - data = (php_ssh2_session_data*)*abstract; - if (!data->debug_cb) { - return; - } - - MAKE_STD_ZVAL(zmessage); - ZVAL_STRINGL(zmessage, (char*)message, message_len, 1); - args[0] = &zmessage; - - MAKE_STD_ZVAL(zlanguage); - ZVAL_STRINGL(zlanguage, (char*)language, language_len, 1); - args[1] = &zlanguage; - - MAKE_STD_ZVAL(zdisplay); - ZVAL_LONG(zdisplay, always_display); - args[2] = &zdisplay; - - if (FAILURE == call_user_function_ex(NULL, NULL, data->disconnect_cb, NULL, 3, args, 0, NULL TSRMLS_CC)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failure calling disconnect callback"); - } - zval_ptr_dtor(&zdisplay); - zval_ptr_dtor(&zmessage); - zval_ptr_dtor(&zlanguage); -} -/* }}} */ - -/* {{{ php_ssh2_ignore_cb - * Ignore packets - */ -LIBSSH2_IGNORE_FUNC(php_ssh2_ignore_cb) -{ - php_ssh2_session_data *data; - zval *zretval = NULL, *zmessage; - zval **args[1]; - SSH2_TSRMLS_FETCH(*abstract); - - if (!abstract || !*abstract) { - return; - } - data = (php_ssh2_session_data*)*abstract; - if (!data->ignore_cb) { - return; - } - - MAKE_STD_ZVAL(zmessage); - ZVAL_STRINGL(zmessage, (char*)message, message_len, 1); - args[0] = &zmessage; - - if (FAILURE == call_user_function_ex(NULL, NULL, data->ignore_cb, &zretval, 1, args, 0, NULL TSRMLS_CC)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failure calling ignore callback"); - } - zval_ptr_dtor(&zmessage); - if (zretval) { - zval_ptr_dtor(&zretval); - } -} -/* }}} */ - -/* {{{ php_ssh2_macerror_cb - * Called when a MAC error occurs, offers the chance to ignore - * WHY ARE YOU IGNORING MAC ERRORS?????? - */ -LIBSSH2_MACERROR_FUNC(php_ssh2_macerror_cb) -{ - php_ssh2_session_data *data; - zval *zretval = NULL, *zpacket; - zval **args[1]; - int retval = -1; - SSH2_TSRMLS_FETCH(*abstract); - - if (!abstract || !*abstract) { - return -1; - } - data = (php_ssh2_session_data*)*abstract; - if (!data->macerror_cb) { - return -1; - } - - MAKE_STD_ZVAL(zpacket); - ZVAL_STRINGL(zpacket, (char*)packet, packet_len, 1); - args[0] = &zpacket; - - if (FAILURE == call_user_function_ex(NULL, NULL, data->macerror_cb, &zretval, 1, args, 0, NULL TSRMLS_CC)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failure calling macerror callback"); - } else { - retval = zval_is_true(zretval) ? 0 : -1; - } - zval_ptr_dtor(&zpacket); - if (zretval) { - zval_ptr_dtor(&zretval); - } - - return retval; -} -/* }}} */ - -/* {{{ php_ssh2_disconnect_cb - * Connection closed by foreign host - */ -LIBSSH2_DISCONNECT_FUNC(php_ssh2_disconnect_cb) -{ - php_ssh2_session_data *data; - zval *zreason, *zmessage, *zlanguage; - zval **args[3]; - SSH2_TSRMLS_FETCH(*abstract); - - if (!abstract || !*abstract) { - return; - } - data = (php_ssh2_session_data*)*abstract; - if (!data->disconnect_cb) { - return; - } - - MAKE_STD_ZVAL(zreason); - ZVAL_LONG(zreason, reason); - args[0] = &zreason; - - MAKE_STD_ZVAL(zmessage); - ZVAL_STRINGL(zmessage, (char*)message, message_len, 1); - args[1] = &zmessage; - - MAKE_STD_ZVAL(zlanguage); - ZVAL_STRINGL(zlanguage, (char*)language, language_len, 1); - args[2] = &zlanguage; - - if (FAILURE == call_user_function_ex(NULL, NULL, data->disconnect_cb, NULL, 3, args, 0, NULL TSRMLS_CC)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failure calling disconnect callback"); - } - zval_ptr_dtor(&zreason); - zval_ptr_dtor(&zmessage); - zval_ptr_dtor(&zlanguage); -} -/* }}} */ - -/* ***************** - * Userspace API * - ***************** */ - -/* {{{ php_ssh2_set_callback - * Try to set a method if it's passed in with the hash table - */ -static int php_ssh2_set_callback(LIBSSH2_SESSION *session, HashTable *ht, char *callback, int callback_len, int callback_type, php_ssh2_session_data *data TSRMLS_DC) -{ - zval **handler, *copyval; - void *internal_handler; - - if (zend_hash_find(ht, callback, callback_len + 1, (void**)&handler) == FAILURE) { - return 0; - } - - if (!handler || !*handler || !zend_is_callable(*handler, 0, NULL ZEND_IS_CALLABLE_TSRMLS_CC)) { - return -1; - } - - ALLOC_INIT_ZVAL(copyval); - *copyval = **handler; - zval_copy_ctor(copyval); - - switch (callback_type) { - case LIBSSH2_CALLBACK_IGNORE: - internal_handler = php_ssh2_ignore_cb; - if (data->ignore_cb) { - zval_ptr_dtor(&data->ignore_cb); - } - data->ignore_cb = copyval; - break; - case LIBSSH2_CALLBACK_DEBUG: - internal_handler = php_ssh2_debug_cb; - if (data->debug_cb) { - zval_ptr_dtor(&data->debug_cb); - } - data->debug_cb = copyval; - break; - case LIBSSH2_CALLBACK_MACERROR: - internal_handler = php_ssh2_macerror_cb; - if (data->macerror_cb) { - zval_ptr_dtor(&data->macerror_cb); - } - data->macerror_cb = copyval; - break; - case LIBSSH2_CALLBACK_DISCONNECT: - internal_handler = php_ssh2_disconnect_cb; - if (data->disconnect_cb) { - zval_ptr_dtor(&data->disconnect_cb); - } - data->disconnect_cb = copyval; - break; - default: - zval_ptr_dtor(©val); - return -1; - } - - libssh2_session_callback_set(session, callback_type, internal_handler); - - return 0; -} -/* }}} */ - -/* {{{ php_ssh2_set_method - * Try to set a method if it's passed in with the hash table - */ -static int php_ssh2_set_method(LIBSSH2_SESSION *session, HashTable *ht, char *method, int method_len, int method_type) -{ - zval **value; - - if (zend_hash_find(ht, method, method_len + 1, (void**)&value) == FAILURE) { - return 0; - } - - if (!value || !*value || (Z_TYPE_PP(value) != IS_STRING)) { - return -1; - } - - return libssh2_session_method_pref(session, method_type, Z_STRVAL_PP(value)); -} -/* }}} */ - -/* {{{ php_ssh2_session_connect - * Connect to an SSH server with requested methods - */ -LIBSSH2_SESSION *php_ssh2_session_connect(char *host, int port, zval *methods, zval *callbacks TSRMLS_DC) -{ - LIBSSH2_SESSION *session; - int socket; - php_ssh2_session_data *data; - struct timeval tv; - - tv.tv_sec = FG(default_socket_timeout); - tv.tv_usec = 0; - -#if PHP_MAJOR_VERSION > 5 || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 0) - socket = php_network_connect_socket_to_host(host, port, SOCK_STREAM, 0, &tv, NULL, NULL, NULL, 0 TSRMLS_CC); -#elif PHP_MAJOR_VERSION == 5 - socket = php_network_connect_socket_to_host(host, port, SOCK_STREAM, 0, &tv, NULL, NULL TSRMLS_CC); -#else - socket = php_hostconnect(host, port, SOCK_STREAM, &tv TSRMLS_CC); -#endif - - if (socket <= 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to connect to %s on port %d", host, port); - return NULL; - } - - data = ecalloc(1, sizeof(php_ssh2_session_data)); - SSH2_TSRMLS_SET(data); - data->socket = socket; - - session = libssh2_session_init_ex(php_ssh2_alloc_cb, php_ssh2_free_cb, php_ssh2_realloc_cb, data); - if (!session) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to initialize SSH2 session"); - efree(data); - closesocket(socket); - return NULL; - } - libssh2_banner_set(session, LIBSSH2_SSH_DEFAULT_BANNER " PHP"); - - /* Override method preferences */ - if (methods) { - zval **container; - - if (php_ssh2_set_method(session, HASH_OF(methods), "kex", sizeof("kex") - 1, LIBSSH2_METHOD_KEX)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed overriding KEX method"); - } - if (php_ssh2_set_method(session, HASH_OF(methods), "hostkey", sizeof("hostkey") - 1, LIBSSH2_METHOD_HOSTKEY)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed overriding HOSTKEY method"); - } - - if (zend_hash_find(HASH_OF(methods), "client_to_server", sizeof("client_to_server"), (void**)&container) == SUCCESS && - container && *container && Z_TYPE_PP(container) == IS_ARRAY) { - if (php_ssh2_set_method(session, HASH_OF(*container), "crypt", sizeof("crypt") - 1, LIBSSH2_METHOD_CRYPT_CS)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed overriding client to server CRYPT method"); - } - if (php_ssh2_set_method(session, HASH_OF(*container), "mac", sizeof("mac") - 1, LIBSSH2_METHOD_MAC_CS)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed overriding client to server MAC method"); - } - if (php_ssh2_set_method(session, HASH_OF(*container), "comp", sizeof("comp") - 1, LIBSSH2_METHOD_COMP_CS)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed overriding client to server COMP method"); - } - if (php_ssh2_set_method(session, HASH_OF(*container), "lang", sizeof("lang") - 1, LIBSSH2_METHOD_LANG_CS)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed overriding client to server LANG method"); - } - } - - if (zend_hash_find(HASH_OF(methods), "server_to_client", sizeof("server_to_client"), (void**)&container) == SUCCESS && - container && *container && Z_TYPE_PP(container) == IS_ARRAY) { - if (php_ssh2_set_method(session, HASH_OF(*container), "crypt", sizeof("crypt") - 1, LIBSSH2_METHOD_CRYPT_SC)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed overriding server to client CRYPT method"); - } - if (php_ssh2_set_method(session, HASH_OF(*container), "mac", sizeof("mac") - 1, LIBSSH2_METHOD_MAC_SC)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed overriding server to client MAC method"); - } - if (php_ssh2_set_method(session, HASH_OF(*container), "comp", sizeof("comp") - 1, LIBSSH2_METHOD_COMP_SC)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed overriding server to client COMP method"); - } - if (php_ssh2_set_method(session, HASH_OF(*container), "lang", sizeof("lang") - 1, LIBSSH2_METHOD_LANG_SC)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed overriding server to client LANG method"); - } - } - } - - /* Register Callbacks */ - if (callbacks) { - /* ignore debug disconnect macerror */ - - if (php_ssh2_set_callback(session, HASH_OF(callbacks), "ignore", sizeof("ignore") - 1, LIBSSH2_CALLBACK_IGNORE, data TSRMLS_CC)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed setting IGNORE callback"); - } - - if (php_ssh2_set_callback(session, HASH_OF(callbacks), "debug", sizeof("debug") - 1, LIBSSH2_CALLBACK_DEBUG, data TSRMLS_CC)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed setting DEBUG callback"); - } - - if (php_ssh2_set_callback(session, HASH_OF(callbacks), "macerror", sizeof("macerror") - 1, LIBSSH2_CALLBACK_MACERROR, data TSRMLS_CC)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed setting MACERROR callback"); - } - - if (php_ssh2_set_callback(session, HASH_OF(callbacks), "disconnect", sizeof("disconnect") - 1, LIBSSH2_CALLBACK_DISCONNECT, data TSRMLS_CC)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed setting DISCONNECT callback"); - } - } - - if (libssh2_session_startup(session, socket)) { - int last_error = 0; - char *error_msg = NULL; - - last_error = libssh2_session_last_error(session, &error_msg, NULL, 0); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error starting up SSH connection(%d): %s", last_error, error_msg); - closesocket(socket); - libssh2_session_free(session); - efree(data); - return NULL; - } - - return session; -} -/* }}} */ - -/* {{{ proto resource ssh2_connect(string host[, int port[, array methods[, array callbacks]]]) - * Establish a connection to a remote SSH server and return a resource on success, false on error - */ -PHP_FUNCTION(ssh2_connect) -{ - LIBSSH2_SESSION *session; - zval *methods = NULL, *callbacks = NULL; - char *host; - long port = PHP_SSH2_DEFAULT_PORT; - int host_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|la!a!", &host, &host_len, &port, &methods, &callbacks) == FAILURE) { - RETURN_FALSE; - } - - session = php_ssh2_session_connect(host, port, methods, callbacks TSRMLS_CC); - if (!session) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to connect to %s", host); - RETURN_FALSE; - } - - ZEND_REGISTER_RESOURCE(return_value, session, le_ssh2_session); -} -/* }}} */ - -/* {{{ proto array ssh2_methods_negotiated(resource session) - * Return list of negotiaed methods - */ -PHP_FUNCTION(ssh2_methods_negotiated) -{ - LIBSSH2_SESSION *session; - zval *zsession, *endpoint; - const char *kex, *hostkey, *crypt_cs, *crypt_sc, *mac_cs, *mac_sc, *comp_cs, *comp_sc, *lang_cs, *lang_sc; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zsession) == FAILURE) { - RETURN_FALSE; - } - - ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); - -#if defined(LIBSSH2_APINO) && LIBSSH2_APINO < 200412301450 - libssh2_session_methods(session, &kex, &hostkey, &crypt_cs, &crypt_sc, &mac_cs, &mac_sc, &comp_cs, &comp_sc, &lang_cs, &lang_sc); -#else - kex = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_KEX); - hostkey = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_HOSTKEY); - crypt_cs = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_CRYPT_CS); - crypt_sc = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_CRYPT_SC); - mac_cs = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_MAC_CS); - mac_sc = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_MAC_SC); - comp_cs = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_COMP_CS); - comp_sc = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_COMP_SC); - lang_cs = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_LANG_CS); - lang_sc = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_LANG_SC); -#endif - - array_init(return_value); - add_assoc_string(return_value, "kex", kex, 1); - add_assoc_string(return_value, "hostkey", hostkey, 1); - - ALLOC_INIT_ZVAL(endpoint); - array_init(endpoint); - add_assoc_string(endpoint, "crypt", crypt_cs, 1); - add_assoc_string(endpoint, "mac", mac_cs, 1); - add_assoc_string(endpoint, "comp", comp_cs, 1); - add_assoc_string(endpoint, "lang", lang_cs, 1); - add_assoc_zval(return_value, "client_to_server", endpoint); - - ALLOC_INIT_ZVAL(endpoint); - array_init(endpoint); - add_assoc_string(endpoint, "crypt", crypt_sc, 1); - add_assoc_string(endpoint, "mac", mac_sc, 1); - add_assoc_string(endpoint, "comp", comp_sc, 1); - add_assoc_string(endpoint, "lang", lang_sc, 1); - add_assoc_zval(return_value, "server_to_client", endpoint); -} -/* }}} */ - -/* {{{ proto string ssh2_fingerprint(resource session[, int flags]) - * Returns a server hostkey hash from an active session - * Defaults to MD5 fingerprint encoded as ASCII hex values - */ -PHP_FUNCTION(ssh2_fingerprint) -{ - LIBSSH2_SESSION *session; - zval *zsession; - const char *fingerprint; - long flags = 0; - int i, fingerprint_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|l", &zsession, &flags) == FAILURE) { - RETURN_FALSE; - } - fingerprint_len = (flags & PHP_SSH2_FINGERPRINT_SHA1) ? SHA_DIGEST_LENGTH : MD5_DIGEST_LENGTH; - - ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); - - fingerprint = (char*)libssh2_hostkey_hash(session, (flags & PHP_SSH2_FINGERPRINT_SHA1) ? LIBSSH2_HOSTKEY_HASH_SHA1 : LIBSSH2_HOSTKEY_HASH_MD5); - if (!fingerprint) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to retreive fingerprint from specified session"); - RETURN_FALSE; - } - - for(i = 0; i < fingerprint_len; i++) { - if (fingerprint[i] != '\0') { - goto fingerprint_good; - } - } - php_error_docref(NULL TSRMLS_CC, E_WARNING, "No fingerprint available using specified hash"); - RETURN_NULL(); - fingerprint_good: - if (flags & PHP_SSH2_FINGERPRINT_RAW) { - RETURN_STRINGL(fingerprint, fingerprint_len, 1); - } else { - char *hexchars; - - hexchars = emalloc((fingerprint_len * 2) + 1); - for(i = 0; i < fingerprint_len; i++) { - snprintf(hexchars + (2 * i), 3, "%02X", (unsigned char)fingerprint[i]); - } - RETURN_STRINGL(hexchars, 2 * fingerprint_len, 0); - } -} -/* }}} */ - -/* {{{ proto array ssh2_auth_none(resource session, string username) - * Attempt "none" authentication, returns a list of allowed methods on failed authentication, - * false on utter failure, or true on success - */ -PHP_FUNCTION(ssh2_auth_none) -{ - LIBSSH2_SESSION *session; - zval *zsession; - char *username, *methods, *s, *p; - int username_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &zsession, &username, &username_len) == FAILURE) { - RETURN_FALSE; - } - - ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); - - s = methods = libssh2_userauth_list(session, username, username_len); - if (!methods) { - /* Either bad failure, or unexpected success */ - RETURN_BOOL(libssh2_userauth_authenticated(session)); - } - - array_init(return_value); - while ((p = strchr(s, ','))) { - if ((p - s) > 0) { - add_next_index_stringl(return_value, s, p - s, 1); - } - s = p + 1; - } - if (strlen(s)) { - add_next_index_string(return_value, s, 1); - } - efree(methods); -} -/* }}} */ - -/* {{{ proto bool ssh2_auth_password(resource session, string username, string password) - * Authenticate over SSH using a plain password - */ -PHP_FUNCTION(ssh2_auth_password) -{ - LIBSSH2_SESSION *session; - zval *zsession; - char *username, *password; - int username_len, password_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rss", &zsession, &username, &username_len, &password, &password_len) == FAILURE) { - RETURN_FALSE; - } - - ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); - - /* TODO: Support password change callback */ - if (libssh2_userauth_password_ex(session, username, username_len, password, password_len, NULL)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Authentication failed for %s using password", username); - RETURN_FALSE; - } - - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto bool ssh2_auth_pubkey_file(resource session, string username, string pubkeyfile, string privkeyfile[, string passphrase]) - * Authenticate using a public key - */ -PHP_FUNCTION(ssh2_auth_pubkey_file) -{ - LIBSSH2_SESSION *session; - zval *zsession; - char *username, *pubkey, *privkey, *passphrase = NULL; - int username_len, pubkey_len, privkey_len, passphrase_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsss|s", &zsession, &username, &username_len, - &pubkey, &pubkey_len, - &privkey, &privkey_len, - &passphrase, &passphrase_len) == FAILURE) { - RETURN_FALSE; - } - - if (PG(safe_mode) && !php_checkuid(pubkey, NULL, CHECKUID_CHECK_FILE_AND_DIR)) { - RETURN_FALSE; - } - if (PG(safe_mode) && !php_checkuid(privkey, NULL, CHECKUID_CHECK_FILE_AND_DIR)) { - RETURN_FALSE; - } - - if (php_check_open_basedir(pubkey TSRMLS_CC)) { - RETURN_FALSE; - } - if (php_check_open_basedir(privkey TSRMLS_CC)) { - RETURN_FALSE; - } - - ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); - - /* TODO: Support passphrase callback */ - if (libssh2_userauth_publickey_fromfile_ex(session, username, username_len, pubkey, privkey, passphrase)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Authentication failed for %s using public key", username); - RETURN_FALSE; - } - - RETURN_TRUE; -} -/* }}} */ - -#ifdef PHP_SSH2_HOSTBASED_AUTH -/* {{{ proto bool ssh2_auth_hostbased_file(resource session, string username, string local_hostname, string pubkeyfile, string privkeyfile[, string passphrase[, string local_username]]) - * Authenticate using a hostkey - */ -PHP_FUNCTION(ssh2_auth_hostbased_file) -{ - LIBSSH2_SESSION *session; - zval *zsession; - char *username, *hostname, *pubkey, *privkey, *passphrase = NULL, *local_username = NULL; - int username_len, hostname_len, pubkey_len, privkey_len, passphrase_len, local_username_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rssss|s!s!", &zsession, &username, &username_len, - &hostname, &hostname_len, - &pubkey, &pubkey_len, - &privkey, &privkey_len, - &passphrase, &passphrase_len, - &local_username, &local_username_len) == FAILURE) { - RETURN_FALSE; - } - - if (PG(safe_mode) && !php_checkuid(pubkey, NULL, CHECKUID_CHECK_FILE_AND_DIR)) { - RETURN_FALSE; - } - if (PG(safe_mode) && !php_checkuid(privkey, NULL, CHECKUID_CHECK_FILE_AND_DIR)) { - RETURN_FALSE; - } - - if (php_check_open_basedir(pubkey TSRMLS_CC)) { - RETURN_FALSE; - } - if (php_check_open_basedir(privkey TSRMLS_CC)) { - RETURN_FALSE; - } - - ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); - - if (!local_username) { - local_username = username; - local_username_len = username_len; - } - - /* TODO: Support passphrase callback */ - if (libssh2_userauth_hostbased_fromfile_ex(session, username, username_len, pubkey, privkey, passphrase, hostname, hostname_len, local_username, local_username_len)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Authentication failed for %s using hostbased public key", username); - RETURN_FALSE; - } - - RETURN_TRUE; -} -/* }}} */ -#endif /* PHP_SSH2_HOSTBASED_AUTH */ - -#ifdef PHP_SSH2_REMOTE_FORWARDING -/* {{{ proto resource ssh2_forward_listen(resource session, int port[, string host[, long max_connections]]) - * Bind a port on the remote server and listen for connections - */ -PHP_FUNCTION(ssh2_forward_listen) -{ - zval *zsession; - LIBSSH2_SESSION *session; - LIBSSH2_LISTENER *listener; - php_ssh2_listener_data *data; - long port; - char *host = NULL; - int host_len; - long max_connections = PHP_SSH2_LISTEN_MAX_QUEUED; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl|sl", &zsession, &port, &host, &host_len, &max_connections) == FAILURE) { - RETURN_FALSE; - } - - ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); - - listener = libssh2_channel_forward_listen_ex(session, host, port, NULL, max_connections); - - if (!listener) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failure listening on remote port"); - RETURN_FALSE; - } - - data = emalloc(sizeof(php_ssh2_listener_data)); - data->session = session; - data->session_rsrcid = Z_LVAL_P(zsession); - zend_list_addref(data->session_rsrcid); - data->listener = listener; - - ZEND_REGISTER_RESOURCE(return_value, data, le_ssh2_listener); -} -/* }}} */ - -/* {{{ proto stream ssh2_forward_accept(resource listener[, string &shost[, long &sport]]) - * Accept a connection created by a listener - */ -PHP_FUNCTION(ssh2_forward_accept) -{ - zval *zlistener; - php_ssh2_listener_data *data; - LIBSSH2_CHANNEL *channel; - php_ssh2_channel_data *channel_data; - php_stream *stream; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zlistener) == FAILURE) { - RETURN_FALSE; - } - - ZEND_FETCH_RESOURCE(data, php_ssh2_listener_data*, &zlistener, -1, PHP_SSH2_LISTENER_RES_NAME, le_ssh2_listener); - - channel = libssh2_channel_forward_accept(data->listener); - - if (!channel) { - RETURN_FALSE; - } - - channel_data = emalloc(sizeof(php_ssh2_channel_data)); - channel_data->channel = channel; - channel_data->streamid = 0; - channel_data->is_blocking = 0; - channel_data->session_rsrc = data->session_rsrcid; - channel_data->refcount = NULL; - - stream = php_stream_alloc(&php_ssh2_channel_stream_ops, channel_data, 0, "r+"); - if (!stream) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failure allocating stream"); - efree(channel_data); - libssh2_channel_free(channel); - RETURN_FALSE; - } - zend_list_addref(channel_data->session_rsrc); - - php_stream_to_zval(stream, return_value); -} -/* }}} */ -#endif /* PHP_SSH2_REMOTE_FORWARDING */ - -#ifdef PHP_SSH2_POLL -/* {{{ proto int ssh2_poll(array &polldes[, int timeout]) - * Poll the channels/listeners/streams for events - * Returns number of descriptors which returned non-zero revents - * Input array should be of the form: - * array( - * 0 => array( - * [resource] => $channel,$listener, or $stream - * [events] => SSH2_POLL* flags bitwise ORed together - * ), - * 1 => ... - * ) - * Each subarray will be populated with an revents element on return - */ -PHP_FUNCTION(ssh2_poll) -{ - zval *zdesc, **subarray; - long timeout = PHP_SSH2_DEFAULT_POLL_TIMEOUT; - LIBSSH2_POLLFD *pollfds; - int numfds, i = 0, fds_ready; - int le_stream = php_file_le_stream(); - int le_pstream = php_file_le_pstream(); - zval ***pollmap; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &zdesc, &timeout) == FAILURE) { - RETURN_NULL(); - } - - numfds = zend_hash_num_elements(Z_ARRVAL_P(zdesc)); - pollfds = safe_emalloc(sizeof(LIBSSH2_POLLFD), numfds, 0); - pollmap = safe_emalloc(sizeof(zval**), numfds, 0); - - for(zend_hash_internal_pointer_reset(Z_ARRVAL_P(zdesc)); - zend_hash_get_current_data(Z_ARRVAL_P(zdesc), (void**)&subarray) == SUCCESS; - zend_hash_move_forward(Z_ARRVAL_P(zdesc))) { - zval **tmpzval; - int res_type = 0; - void *res; - - if (Z_TYPE_PP(subarray) != IS_ARRAY) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid element in poll array, not a sub array"); - numfds--; - continue; - } - if (zend_hash_find(Z_ARRVAL_PP(subarray), "events", sizeof("events"), (void**)&tmpzval) == FAILURE || - Z_TYPE_PP(tmpzval) != IS_LONG) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid data in subarray, no events element, or not a bitmask"); - numfds--; - continue; - } - pollfds[i].events = Z_LVAL_PP(tmpzval); - if (zend_hash_find(Z_ARRVAL_PP(subarray), "resource", sizeof("resource"), (void**)&tmpzval) == FAILURE || - Z_TYPE_PP(tmpzval) != IS_RESOURCE) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid data in subarray, no resource element, or not of type resource"); - numfds--; - continue; - } - zend_list_find(Z_LVAL_PP(tmpzval), &res_type); - res = zend_fetch_resource(tmpzval TSRMLS_CC, -1, "Poll Resource", NULL, 1, res_type); - if (res_type == le_ssh2_listener) { - pollfds[i].type = LIBSSH2_POLLFD_LISTENER; - pollfds[i].fd.listener = ((php_ssh2_listener_data*)res)->listener; - } else if ((res_type == le_stream || res_type == le_pstream) && - ((php_stream*)res)->ops == &php_ssh2_channel_stream_ops) { - pollfds[i].type = LIBSSH2_POLLFD_CHANNEL; - pollfds[i].fd.channel = ((php_ssh2_channel_data*)(((php_stream*)res)->abstract))->channel; - /* TODO: Add the ability to select against other stream types */ - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid resource type in subarray: %s", zend_rsrc_list_get_rsrc_type(Z_LVAL_PP(tmpzval) TSRMLS_CC)); - numfds--; - continue; - } - pollmap[i] = subarray; - i++; - } - - fds_ready = libssh2_poll(pollfds, numfds, timeout * 1000); - - for(i = 0; i < numfds; i++) { - zval *subarray = *pollmap[i]; - - if (!Z_ISREF_P(subarray) && Z_REFCOUNT_P(subarray) > 1) { - /* Make a new copy of the subarray zval* */ - MAKE_STD_ZVAL(subarray); - *subarray = **pollmap[i]; - - /* Point the pData to the new zval* and duplicate its resources */ - *pollmap[i] = subarray; - zval_copy_ctor(subarray); - - /* Fixup its refcount */ - Z_UNSET_ISREF_P(subarray); - Z_SET_REFCOUNT_P(subarray, 1); - } - zend_hash_del(Z_ARRVAL_P(subarray), "revents", sizeof("revents")); - add_assoc_long(subarray, "revents", pollfds[i].revents); - - } - efree(pollmap); - efree(pollfds); - - RETURN_LONG(fds_ready); -} -/* }}} */ -#endif /* PHP_SSH2_POLL */ - -#ifdef PHP_SSH2_PUBLICKEY_SUBSYSTEM -/* *********************** - * Publickey Subsystem * - *********************** */ - -/* {{{ proto resource ssh2_publickey_init(resource connection) -Initialize the publickey subsystem */ -PHP_FUNCTION(ssh2_publickey_init) -{ - zval *zsession; - LIBSSH2_SESSION *session; - LIBSSH2_PUBLICKEY *pkey; - php_ssh2_pkey_subsys_data *data; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zsession) == FAILURE) { - RETURN_FALSE; - } - - ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); - - pkey = libssh2_publickey_init(session); - - if (!pkey) { - int last_error = 0; - char *error_msg = NULL; - - last_error = libssh2_session_last_error(session, &error_msg, NULL, 0); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to initialize publickey subsystem(%d) %s", last_error, error_msg); - RETURN_FALSE; - } - - data = emalloc(sizeof(php_ssh2_pkey_subsys_data)); - data->session = session; - data->session_rsrcid = Z_LVAL_P(zsession); - zend_list_addref(data->session_rsrcid); - data->pkey = pkey; - - ZEND_REGISTER_RESOURCE(return_value, data, le_ssh2_pkey_subsys); -} -/* }}} */ - -/* {{{ proto bool ssh2_publickey_add(resource pkey, string algoname, string blob[, bool overwrite=FALSE [,array attributes=NULL]]) -Add an additional publickey */ -PHP_FUNCTION(ssh2_publickey_add) -{ - zval *zpkey_data, *zattrs = NULL; - php_ssh2_pkey_subsys_data *data; - char *algo, *blob; - int algo_len, blob_len; - unsigned long num_attrs = 0; - libssh2_publickey_attribute *attrs = NULL; - zend_bool overwrite = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rss|ba", &zpkey_data, &algo, &algo_len, &blob, &blob_len, &overwrite, &zattrs) == FAILURE) { - RETURN_FALSE; - } - - ZEND_FETCH_RESOURCE(data, php_ssh2_pkey_subsys_data*, &zpkey_data, -1, PHP_SSH2_PKEY_SUBSYS_RES_NAME, le_ssh2_pkey_subsys); - - if (zattrs) { - HashPosition pos; - zval **attr_val; - unsigned long current_attr = 0; - - num_attrs = zend_hash_num_elements(Z_ARRVAL_P(zattrs)); - attrs = safe_emalloc(num_attrs, sizeof(libssh2_publickey_attribute), 0); - - for(zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(zattrs), &pos); - zend_hash_get_current_data_ex(Z_ARRVAL_P(zattrs), (void**)&attr_val, &pos) == SUCCESS; - zend_hash_move_forward_ex(Z_ARRVAL_P(zattrs), &pos)) { - char *key; - int key_len, type; - long idx; - zval copyval = **attr_val; - - type = zend_hash_get_current_key_ex(Z_ARRVAL_P(zattrs), &key, &key_len, &idx, 0, &pos); - if (type == HASH_KEY_NON_EXISTANT) { - /* All but impossible */ - break; - } - if (type == HASH_KEY_IS_LONG) { - /* Malformed, ignore */ - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Malformed attirbute array, contains numeric index"); - num_attrs--; - continue; - } - - if (key_len == 0 || (key_len == 1 && *key == '*')) { - /* Empty key, ignore */ - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty attribute key"); - num_attrs--; - continue; - } - - zval_copy_ctor(©val); - Z_UNSET_ISREF_P(©val); - Z_SET_REFCOUNT_P(©val, 1); - convert_to_string(©val); - - if (*key == '*') { - attrs[current_attr].mandatory = 1; - attrs[current_attr].name = key + 1; - attrs[current_attr].name_len = key_len - 2; - } else { - attrs[current_attr].mandatory = 0; - attrs[current_attr].name = key; - attrs[current_attr].name_len = key_len - 1; - } - attrs[current_attr].value_len = Z_STRLEN(copyval); - attrs[current_attr].value = Z_STRVAL(copyval); - - /* copyval deliberately not dtor'd, we're stealing the string */ - current_attr++; - } - } - - if (libssh2_publickey_add_ex(data->pkey, algo, algo_len, blob, blob_len, overwrite, num_attrs, attrs)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to add %s key", algo); - RETVAL_FALSE; - } else { - RETVAL_TRUE; - } - - if (attrs) { - unsigned long i; - - for(i = 0; i < num_attrs; i++) { - /* name doesn't need freeing */ - efree(attrs[i].value); - } - efree(attrs); - } -} -/* }}} */ - -/* {{{ proto bool ssh2_publickey_remove(resource pkey, string algoname, string blob) -Remove a publickey entry */ -PHP_FUNCTION(ssh2_publickey_remove) -{ - zval *zpkey_data; - php_ssh2_pkey_subsys_data *data; - char *algo, *blob; - int algo_len, blob_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rss", &zpkey_data, &algo, &algo_len, &blob, &blob_len) == FAILURE) { - RETURN_FALSE; - } - - ZEND_FETCH_RESOURCE(data, php_ssh2_pkey_subsys_data*, &zpkey_data, -1, PHP_SSH2_PKEY_SUBSYS_RES_NAME, le_ssh2_pkey_subsys); - - if (libssh2_publickey_remove_ex(data->pkey, algo, algo_len, blob, blob_len)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to remove %s key", algo); - RETURN_FALSE; - } - - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto array ssh2_publickey_list(resource pkey) -List currently installed publickey entries */ -PHP_FUNCTION(ssh2_publickey_list) -{ - zval *zpkey_data; - php_ssh2_pkey_subsys_data *data; - unsigned long num_keys, i; - libssh2_publickey_list *keys; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zpkey_data) == FAILURE) { - RETURN_FALSE; - } - - ZEND_FETCH_RESOURCE(data, php_ssh2_pkey_subsys_data*, &zpkey_data, -1, PHP_SSH2_PKEY_SUBSYS_RES_NAME, le_ssh2_pkey_subsys); - - if (libssh2_publickey_list_fetch(data->pkey, &num_keys, &keys)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to list keys on remote server"); - RETURN_FALSE; - } - - array_init(return_value); - for(i = 0; i < num_keys; i++) { - zval *key, *attrs; - unsigned long j; - - MAKE_STD_ZVAL(key); - array_init(key); - - add_assoc_stringl(key, "name", keys[i].name, keys[i].name_len, 1); - add_assoc_stringl(key, "blob", keys[i].blob, keys[i].blob_len, 1); - - MAKE_STD_ZVAL(attrs); - array_init(attrs); - for(j = 0; j < keys[i].num_attrs; j++) { - zval *attr; - - MAKE_STD_ZVAL(attr); - ZVAL_STRINGL(attr, keys[i].attrs[j].value, keys[i].attrs[j].value_len, 1); - zend_hash_add(Z_ARRVAL_P(attrs), keys[i].attrs[j].name, keys[i].attrs[j].name_len + 1, (void**)&attr, sizeof(zval*), NULL); - } - add_assoc_zval(key, "attrs", attrs); - - add_next_index_zval(return_value, key); - } - - libssh2_publickey_list_free(data->pkey, keys); -} -/* }}} */ -#endif /* PHP_SSH2_PUBLICKEY_SUBSYSTEM */ - -/* *********************** - * Module Housekeeping * - *********************** */ - -static void php_ssh2_session_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) -{ - LIBSSH2_SESSION *session = (LIBSSH2_SESSION*)rsrc->ptr; - php_ssh2_session_data **data = (php_ssh2_session_data**)libssh2_session_abstract(session); - - libssh2_session_disconnect(session, "PECL/ssh2 (http://pecl.php.net/packages/ssh2)"); - - if (*data) { - if ((*data)->ignore_cb) { - zval_ptr_dtor(&(*data)->ignore_cb); - } - if ((*data)->debug_cb) { - zval_ptr_dtor(&(*data)->debug_cb); - } - if ((*data)->macerror_cb) { - zval_ptr_dtor(&(*data)->macerror_cb); - } - if ((*data)->disconnect_cb) { - zval_ptr_dtor(&(*data)->disconnect_cb); - } - - closesocket((*data)->socket); - - efree(*data); - *data = NULL; - } - - libssh2_session_free(session); -} - -#ifdef PHP_SSH2_REMOTE_FORWARDING -static void php_ssh2_listener_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) -{ - php_ssh2_listener_data *data = (php_ssh2_listener_data*)rsrc->ptr; - LIBSSH2_LISTENER *listener = data->listener; - - libssh2_channel_forward_cancel(listener); - zend_list_delete(data->session_rsrcid); - efree(data); -} -#endif /* PHP_SSH2_REMOTE_FORWARDING */ - -#ifdef PHP_SSH2_PUBLICKEY_SUBSYSTEM -static void php_ssh2_pkey_subsys_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) -{ - php_ssh2_pkey_subsys_data *data = (php_ssh2_pkey_subsys_data*)rsrc->ptr; - LIBSSH2_PUBLICKEY *pkey = data->pkey; - - libssh2_publickey_shutdown(pkey); - zend_list_delete(data->session_rsrcid); - efree(data); -} -#endif /* PHP_SSH2_PUBLICKEY_SUBSYSTEM */ - -/* {{{ PHP_MINIT_FUNCTION - */ -PHP_MINIT_FUNCTION(ssh2) -{ - le_ssh2_session = zend_register_list_destructors_ex(php_ssh2_session_dtor, NULL, PHP_SSH2_SESSION_RES_NAME, module_number); -#ifdef PHP_SSH2_REMOTE_FORWARDING - le_ssh2_listener = zend_register_list_destructors_ex(php_ssh2_listener_dtor, NULL, PHP_SSH2_LISTENER_RES_NAME, module_number); -#endif /* PHP_SSH2_REMOTE_FORWARDING */ - le_ssh2_sftp = zend_register_list_destructors_ex(php_ssh2_sftp_dtor, NULL, PHP_SSH2_SFTP_RES_NAME, module_number); -#ifdef PHP_SSH2_PUBLICKEY_SUBSYSTEM - le_ssh2_pkey_subsys = zend_register_list_destructors_ex(php_ssh2_pkey_subsys_dtor, NULL, PHP_SSH2_PKEY_SUBSYS_RES_NAME, module_number); -#endif /* PHP_SSH2_PUBLICKEY_SUBSYSTEM */ - - REGISTER_LONG_CONSTANT("SSH2_FINGERPRINT_MD5", PHP_SSH2_FINGERPRINT_MD5, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SSH2_FINGERPRINT_SHA1", PHP_SSH2_FINGERPRINT_SHA1, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SSH2_FINGERPRINT_HEX", PHP_SSH2_FINGERPRINT_HEX, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SSH2_FINGERPRINT_RAW", PHP_SSH2_FINGERPRINT_RAW, CONST_CS | CONST_PERSISTENT); - - REGISTER_LONG_CONSTANT("SSH2_TERM_UNIT_CHARS", PHP_SSH2_TERM_UNIT_CHARS, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SSH2_TERM_UNIT_PIXELS", PHP_SSH2_TERM_UNIT_PIXELS, CONST_CS | CONST_PERSISTENT); - - REGISTER_STRING_CONSTANT("SSH2_DEFAULT_TERMINAL", PHP_SSH2_DEFAULT_TERMINAL, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SSH2_DEFAULT_TERM_WIDTH", PHP_SSH2_DEFAULT_TERM_WIDTH, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SSH2_DEFAULT_TERM_HEIGHT", PHP_SSH2_DEFAULT_TERM_HEIGHT, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SSH2_DEFAULT_TERM_UNIT", PHP_SSH2_DEFAULT_TERM_UNIT, CONST_CS | CONST_PERSISTENT); - - REGISTER_LONG_CONSTANT("SSH2_STREAM_STDIO", 0, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SSH2_STREAM_STDERR", SSH_EXTENDED_DATA_STDERR, CONST_CS | CONST_PERSISTENT); - -#ifdef PHP_SSH2_POLL - /* events/revents */ - REGISTER_LONG_CONSTANT("SSH2_POLLIN", LIBSSH2_POLLFD_POLLIN, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SSH2_POLLEXT", LIBSSH2_POLLFD_POLLEXT, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SSH2_POLLOUT", LIBSSH2_POLLFD_POLLOUT, CONST_CS | CONST_PERSISTENT); - - /* revents only */ - REGISTER_LONG_CONSTANT("SSH2_POLLERR", LIBSSH2_POLLFD_POLLERR, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SSH2_POLLHUP", LIBSSH2_POLLFD_POLLHUP, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SSH2_POLLNVAL", LIBSSH2_POLLFD_POLLNVAL, CONST_CS | CONST_PERSISTENT); -#if (defined(LIBSSH2_APINO) && (LIBSSH2_APINO > 200503221619)) || (defined(LIBSSH2_VERSION_NUM) && LIBSSH2_VERSION_NUM >= 0x001000) - REGISTER_LONG_CONSTANT("SSH2_POLL_SESSION_CLOSED", LIBSSH2_POLLFD_SESSION_CLOSED, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SSH2_POLL_CHANNEL_CLOSED", LIBSSH2_POLLFD_CHANNEL_CLOSED, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SSH2_POLL_LISTENER_CLOSED", LIBSSH2_POLLFD_LISTENER_CLOSED, CONST_CS | CONST_PERSISTENT); -#endif /* >= LIBSSH2-0.10 */ -#endif /* POLL */ - - return (php_register_url_stream_wrapper("ssh2.shell", &php_ssh2_stream_wrapper_shell TSRMLS_CC) == SUCCESS && - php_register_url_stream_wrapper("ssh2.exec", &php_ssh2_stream_wrapper_exec TSRMLS_CC) == SUCCESS && - php_register_url_stream_wrapper("ssh2.tunnel", &php_ssh2_stream_wrapper_tunnel TSRMLS_CC) == SUCCESS && - php_register_url_stream_wrapper("ssh2.scp", &php_ssh2_stream_wrapper_scp TSRMLS_CC) == SUCCESS && - php_register_url_stream_wrapper("ssh2.sftp", &php_ssh2_sftp_wrapper TSRMLS_CC) == SUCCESS) ? SUCCESS : FAILURE; -} -/* }}} */ - -/* {{{ PHP_MSHUTDOWN_FUNCTION - */ -PHP_MSHUTDOWN_FUNCTION(ssh2) -{ - return (php_unregister_url_stream_wrapper("ssh2.shell" TSRMLS_CC) == SUCCESS && - php_unregister_url_stream_wrapper("ssh2.exec" TSRMLS_CC) == SUCCESS && - php_unregister_url_stream_wrapper("ssh2.tunnel" TSRMLS_CC) == SUCCESS && - php_unregister_url_stream_wrapper("ssh2.scp" TSRMLS_CC) == SUCCESS && - php_unregister_url_stream_wrapper("ssh2.sftp" TSRMLS_CC) == SUCCESS) ? SUCCESS : FAILURE; -} -/* }}} */ - -/* {{{ PHP_MINFO_FUNCTION - */ -PHP_MINFO_FUNCTION(ssh2) -{ - php_info_print_table_start(); - php_info_print_table_header(2, "SSH2 support", "enabled"); - php_info_print_table_row(2, "extension version", PHP_SSH2_VERSION); - php_info_print_table_row(2, "libssh2 version", LIBSSH2_VERSION); - php_info_print_table_row(2, "banner", LIBSSH2_SSH_BANNER); -#ifdef PHP_SSH2_REMOTE_FORWARDING - php_info_print_table_row(2, "remote forwarding", "enabled"); -#endif -#ifdef PHP_SSH2_HOSTBASED_AUTH - php_info_print_table_row(2, "hostbased auth", "enabled"); -#endif -#ifdef PHP_SSH2_POLL - php_info_print_table_row(2, "polling support", "enabled"); -#endif -#ifdef PHP_SSH2_PUBLICKEY_SUBSYSTEM - php_info_print_table_row(2, "publickey subsystem", "enabled"); -#endif - php_info_print_table_end(); -} -/* }}} */ - -/* {{{ ssh2_functions[] - */ -zend_function_entry ssh2_functions[] = { - PHP_FE(ssh2_connect, NULL) - PHP_FE(ssh2_methods_negotiated, NULL) - PHP_FE(ssh2_fingerprint, NULL) - - PHP_FE(ssh2_auth_none, NULL) - PHP_FE(ssh2_auth_password, NULL) - PHP_FE(ssh2_auth_pubkey_file, NULL) -#ifdef PHP_SSH2_HOSTBASED_AUTH - PHP_FE(ssh2_auth_hostbased_file, NULL) -#endif /* PHP_SSH2_HOSTBASED_AUTH */ - -#ifdef PHP_SSH2_REMOTE_FORWARDING - PHP_FE(ssh2_forward_listen, NULL) - PHP_FE(ssh2_forward_accept, NULL) -#endif /* PHP_SSH2_REMOTE_FORWARDING */ - - /* Stream Stuff */ - PHP_FE(ssh2_shell, NULL) - PHP_FE(ssh2_exec, NULL) - PHP_FE(ssh2_tunnel, NULL) - PHP_FE(ssh2_scp_recv, NULL) - PHP_FE(ssh2_scp_send, NULL) - PHP_FE(ssh2_fetch_stream, NULL) -#ifdef PHP_SSH2_POLL - PHP_FE(ssh2_poll, php_ssh2_first_arg_force_ref) -#endif - - /* SFTP Stuff */ - PHP_FE(ssh2_sftp, NULL) - - /* SFTP Wrapper Ops */ - PHP_FE(ssh2_sftp_rename, NULL) - PHP_FE(ssh2_sftp_unlink, NULL) - PHP_FE(ssh2_sftp_mkdir, NULL) - PHP_FE(ssh2_sftp_rmdir, NULL) - PHP_FE(ssh2_sftp_stat, NULL) - PHP_FE(ssh2_sftp_lstat, NULL) - PHP_FE(ssh2_sftp_symlink, NULL) - PHP_FE(ssh2_sftp_readlink, NULL) - PHP_FE(ssh2_sftp_realpath, NULL) - - /* Publickey subsystem */ -#ifdef PHP_SSH2_PUBLICKEY_SUBSYSTEM - PHP_FE(ssh2_publickey_init, NULL) - PHP_FE(ssh2_publickey_add, NULL) - PHP_FE(ssh2_publickey_remove, NULL) - PHP_FE(ssh2_publickey_list, NULL) -#endif - - {NULL, NULL, NULL} -}; -/* }}} */ - -/* {{{ ssh2_module_entry - */ -zend_module_entry ssh2_module_entry = { -#if ZEND_MODULE_API_NO >= 20010901 - STANDARD_MODULE_HEADER, -#endif - "ssh2", - ssh2_functions, - PHP_MINIT(ssh2), - PHP_MSHUTDOWN(ssh2), - NULL, /* RINIT */ - NULL, /* RSHUTDOWN */ - PHP_MINFO(ssh2), -#if ZEND_MODULE_API_NO >= 20010901 - PHP_SSH2_VERSION, -#endif - STANDARD_MODULE_PROPERTIES -}; -/* }}} */ - -#ifdef COMPILE_DL_SSH2 -ZEND_GET_MODULE(ssh2) -#endif - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * End: - * vim600: noet sw=4 ts=4 fdm=marker - * vim<600: noet sw=4 ts=4 - */ diff -Nru php-ssh2-0.11.2/ssh2-0.11.2/ssh2_fopen_wrappers.c php-ssh2-0.11.3/ssh2-0.11.2/ssh2_fopen_wrappers.c --- php-ssh2-0.11.2/ssh2-0.11.2/ssh2_fopen_wrappers.c 2010-11-03 22:24:33.000000000 +0000 +++ php-ssh2-0.11.3/ssh2-0.11.2/ssh2_fopen_wrappers.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1268 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | PHP Version 4 | - +----------------------------------------------------------------------+ - | Copyright (c) 1997-2006 The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 2.02 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available at through the world-wide-web at | - | http://www.php.net/license/2_02.txt. | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Author: Sara Golemon | - +----------------------------------------------------------------------+ - - $Id: ssh2_fopen_wrappers.c 305068 2010-11-03 22:24:19Z pajoye $ -*/ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "php.h" -#include "php_ssh2.h" - -/* ********************** - * channel_stream_ops * - ********************** */ - -static size_t php_ssh2_channel_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) -{ - php_ssh2_channel_data *abstract = (php_ssh2_channel_data*)stream->abstract; - - libssh2_channel_set_blocking(abstract->channel, abstract->is_blocking); - return libssh2_channel_write_ex(abstract->channel, abstract->streamid, buf, count); -} - -static size_t php_ssh2_channel_stream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) -{ - php_ssh2_channel_data *abstract = (php_ssh2_channel_data*)stream->abstract; - ssize_t readstate; - - stream->eof = libssh2_channel_eof(abstract->channel); - libssh2_channel_set_blocking(abstract->channel, abstract->is_blocking); - - readstate = libssh2_channel_read_ex(abstract->channel, abstract->streamid, buf, count); - return (readstate < 0 ? 0 : readstate); -} - -static int php_ssh2_channel_stream_close(php_stream *stream, int close_handle TSRMLS_DC) -{ - php_ssh2_channel_data *abstract = (php_ssh2_channel_data*)stream->abstract; - - if (!abstract->refcount || (--(*(abstract->refcount)) == 0)) { - /* Last one out, turn off the lights */ - if (abstract->refcount) { - efree(abstract->refcount); - } - libssh2_channel_eof(abstract->channel); - libssh2_channel_free(abstract->channel); - zend_list_delete(abstract->session_rsrc); - } - efree(abstract); - - return 0; -} - -static int php_ssh2_channel_stream_flush(php_stream *stream TSRMLS_DC) -{ - php_ssh2_channel_data *abstract = (php_ssh2_channel_data*)stream->abstract; - - return libssh2_channel_flush_ex(abstract->channel, abstract->streamid); -} - -static int php_ssh2_channel_stream_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC) -{ - php_ssh2_channel_data *abstract = (php_ssh2_channel_data*)stream->abstract; - int ret; - - switch (option) { - case PHP_STREAM_OPTION_BLOCKING: - ret = abstract->is_blocking; - abstract->is_blocking = value; - return ret; - break; -#if PHP_MAJOR_VERSION >= 5 - case PHP_STREAM_OPTION_CHECK_LIVENESS: - return stream->eof = libssh2_channel_eof(abstract->channel); - break; -#endif - } - - return -1; -} - -php_stream_ops php_ssh2_channel_stream_ops = { - php_ssh2_channel_stream_write, - php_ssh2_channel_stream_read, - php_ssh2_channel_stream_close, - php_ssh2_channel_stream_flush, - PHP_SSH2_CHANNEL_STREAM_NAME, - NULL, /* seek */ - NULL, /* cast */ - NULL, /* stat */ - php_ssh2_channel_stream_set_option, -}; - -/* ********************* - * Magic Path Helper * - ********************* */ - -/* {{{ php_ssh2_fopen_wraper_parse_path - * Parse an ssh2.*:// path - */ -php_url *php_ssh2_fopen_wraper_parse_path( char *path, char *type, php_stream_context *context, - LIBSSH2_SESSION **psession, int *presource_id, - LIBSSH2_SFTP **psftp, int *psftp_rsrcid - TSRMLS_DC) -{ - php_ssh2_sftp_data *sftp_data = NULL; - LIBSSH2_SESSION *session; - php_url *resource; - zval *methods = NULL, *callbacks = NULL, zsession, **tmpzval; - long resource_id; - char *s, *username = NULL, *password = NULL, *pubkey_file = NULL, *privkey_file = NULL; - int username_len = 0, password_len = 0; - - resource = php_url_parse(path); - if (!resource) { - return NULL; - } - - if (strncmp(resource->scheme, "ssh2.", sizeof("ssh2.") - 1)) { - /* Not an ssh wrapper */ - php_url_free(resource); - return NULL; - } - - if (strcmp(resource->scheme + sizeof("ssh2.") - 1, type)) { - /* Wrong ssh2. wrapper type */ - php_url_free(resource); - return NULL; - } - - if (!resource->host) { - return NULL; - } - - /* Look for a resource ID to reuse a session */ - s = resource->host; - if (strncmp(resource->host, "Resource id #", sizeof("Resource id #") - 1) == 0) { - s = resource->host + sizeof("Resource id #") - 1; - } - if (is_numeric_string(s, strlen(s), &resource_id, NULL, 0) == IS_LONG) { - php_ssh2_sftp_data *sftp_data; - - if (psftp) { - sftp_data = (php_ssh2_sftp_data*)zend_fetch_resource(NULL TSRMLS_CC, resource_id, PHP_SSH2_SFTP_RES_NAME, NULL, 1, le_ssh2_sftp); - if (sftp_data) { - /* Want the sftp layer */ - zend_list_addref(resource_id); - *psftp_rsrcid = resource_id; - *psftp = sftp_data->sftp; - *presource_id = sftp_data->session_rsrcid; - *psession = sftp_data->session; - return resource; - } - } - session = (LIBSSH2_SESSION *)zend_fetch_resource(NULL TSRMLS_CC, resource_id, PHP_SSH2_SESSION_RES_NAME, NULL, 1, le_ssh2_session); - if (session) { - if (psftp) { - /* We need an sftp layer too */ - LIBSSH2_SFTP *sftp = libssh2_sftp_init(session); - - if (!sftp) { - php_url_free(resource); - return NULL; - } - sftp_data = emalloc(sizeof(php_ssh2_sftp_data)); - sftp_data->sftp = sftp; - sftp_data->session = session; - sftp_data->session_rsrcid = resource_id; - zend_list_addref(resource_id); - *psftp_rsrcid = ZEND_REGISTER_RESOURCE(NULL, sftp_data, le_ssh2_sftp); - *psftp = sftp; - *presource_id = resource_id; - *psession = session; - return resource; - } - zend_list_addref(resource_id); - *presource_id = resource_id; - *psession = session; - return resource; - } - } - - /* Fallback on finding it in the context */ - if (resource->host[0] == 0 && context && psftp && - php_stream_context_get_option(context, "ssh2", "sftp", &tmpzval) == SUCCESS && - Z_TYPE_PP(tmpzval) == IS_RESOURCE) { - php_ssh2_sftp_data *sftp_data; - sftp_data = (php_ssh2_sftp_data *)zend_fetch_resource(tmpzval TSRMLS_CC, -1, PHP_SSH2_SFTP_RES_NAME, NULL, 1, le_ssh2_sftp); - if (sftp_data) { - zend_list_addref(Z_LVAL_PP(tmpzval)); - *psftp_rsrcid = Z_LVAL_PP(tmpzval); - *psftp = sftp_data->sftp; - *presource_id = sftp_data->session_rsrcid; - *psession = sftp_data->session; - return resource; - } - } - if (resource->host[0] == 0 && context && - php_stream_context_get_option(context, "ssh2", "session", &tmpzval) == SUCCESS && - Z_TYPE_PP(tmpzval) == IS_RESOURCE) { - session = (LIBSSH2_SESSION *)zend_fetch_resource(tmpzval TSRMLS_CC, -1, PHP_SSH2_SESSION_RES_NAME, NULL, 1, le_ssh2_session); - if (session) { - if (psftp) { - /* We need an SFTP layer too! */ - LIBSSH2_SFTP *sftp = libssh2_sftp_init(session); - php_ssh2_sftp_data *sftp_data; - - if (!sftp) { - php_url_free(resource); - return NULL; - } - sftp_data = emalloc(sizeof(php_ssh2_sftp_data)); - sftp_data->sftp = sftp; - sftp_data->session = session; - sftp_data->session_rsrcid = Z_LVAL_PP(tmpzval); - zend_list_addref(Z_LVAL_PP(tmpzval)); - *psftp_rsrcid = ZEND_REGISTER_RESOURCE(NULL, sftp_data, le_ssh2_sftp); - *psftp = sftp; - *presource_id = Z_LVAL_PP(tmpzval); - *psession = session; - return resource; - } - zend_list_addref(Z_LVAL_PP(tmpzval)); - *psession = session; - *presource_id = Z_LVAL_PP(tmpzval); - return resource; - } - } - - /* Make our own connection then */ - if (!resource->port) { - resource->port = 22; - } - - if (context && - php_stream_context_get_option(context, "ssh2", "methods", &tmpzval) == SUCCESS && - Z_TYPE_PP(tmpzval) == IS_ARRAY) { - methods = *tmpzval; - } - - if (context && - php_stream_context_get_option(context, "ssh2", "callbacks", &tmpzval) == SUCCESS && - Z_TYPE_PP(tmpzval) == IS_ARRAY) { - callbacks = *tmpzval; - } - - if (context && - php_stream_context_get_option(context, "ssh2", "username", &tmpzval) == SUCCESS && - Z_TYPE_PP(tmpzval) == IS_STRING) { - username = Z_STRVAL_PP(tmpzval); - username_len = Z_STRLEN_PP(tmpzval); - } - - if (context && - php_stream_context_get_option(context, "ssh2", "password", &tmpzval) == SUCCESS && - Z_TYPE_PP(tmpzval) == IS_STRING) { - password = Z_STRVAL_PP(tmpzval); - password_len = Z_STRLEN_PP(tmpzval); - } - - if (context && - php_stream_context_get_option(context, "ssh2", "pubkey_file", &tmpzval) == SUCCESS && - Z_TYPE_PP(tmpzval) == IS_STRING) { - pubkey_file = Z_STRVAL_PP(tmpzval); - } - - if (context && - php_stream_context_get_option(context, "ssh2", "privkey_file", &tmpzval) == SUCCESS && - Z_TYPE_PP(tmpzval) == IS_STRING) { - privkey_file = Z_STRVAL_PP(tmpzval); - } - - if (resource->user) { - int len = strlen(resource->user); - - if (len) { - username = resource->user; - username_len = len; - } - } - - if (resource->pass) { - int len = strlen(resource->pass); - - if (len) { - password = resource->pass; - password_len = len; - } - } - - if (!username) { - /* username is a minimum */ - php_url_free(resource); - return NULL; - } - - session = php_ssh2_session_connect(resource->host, resource->port, methods, callbacks TSRMLS_CC); - if (!session) { - /* Unable to connect! */ - php_url_free(resource); - return NULL; - } - ZEND_REGISTER_RESOURCE(&zsession, session, le_ssh2_session); - - /* Authenticate */ - if (pubkey_file && privkey_file) { - /* Attempt pubkey authentication */ - if ((!PG(safe_mode) || php_checkuid(pubkey_file, NULL, CHECKUID_CHECK_FILE_AND_DIR)) && - (!PG(safe_mode) || php_checkuid(privkey_file, NULL, CHECKUID_CHECK_FILE_AND_DIR)) && - !php_check_open_basedir(pubkey_file TSRMLS_CC) && - !php_check_open_basedir(privkey_file TSRMLS_CC) && - !libssh2_userauth_publickey_fromfile(session, username, pubkey_file, privkey_file, password)) { - goto session_authed; - } - } - - if (password) { - /* Attempt password authentication */ - if (libssh2_userauth_password_ex(session, username, username_len, password, password_len, NULL) == 0) { - goto session_authed; - } - } - - /* Auth failure */ - php_url_free(resource); - zend_list_delete(Z_LVAL(zsession)); - return NULL; - - session_authed: - - if (psftp) { - LIBSSH2_SFTP *sftp; - zval zsftp; - - sftp = libssh2_sftp_init(session); - if (!sftp) { - php_url_free(resource); - zend_list_delete(Z_LVAL(zsession)); - return NULL; - } - - sftp_data = emalloc(sizeof(php_ssh2_sftp_data)); - sftp_data->session = session; - sftp_data->sftp = sftp; - sftp_data->session_rsrcid = Z_LVAL(zsession); - - ZEND_REGISTER_RESOURCE(&zsftp, sftp_data, le_ssh2_sftp); - *psftp_rsrcid = Z_LVAL(zsftp); - *psftp = sftp; - } - - *presource_id = Z_LVAL(zsession); - *psession = session; - - return resource; -} -/* }}} */ - -/* ***************** - * Shell Wrapper * - ***************** */ - -/* {{{ php_ssh2_shell_open - * Make a stream from a session - */ -static php_stream *php_ssh2_shell_open(LIBSSH2_SESSION *session, int resource_id, char *term, int term_len, zval *environment, long width, long height, long type TSRMLS_DC) -{ - LIBSSH2_CHANNEL *channel; - php_ssh2_channel_data *channel_data; - php_stream *stream; - - channel = libssh2_channel_open_session(session); - if (!channel) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to request a channel from remote host"); - return NULL; - } - - if (environment) { - char *key; - int key_type, key_len; - long idx; - - for(zend_hash_internal_pointer_reset(HASH_OF(environment)); - (key_type = zend_hash_get_current_key_ex(HASH_OF(environment), &key, &key_len, &idx, 0, NULL)) != HASH_KEY_NON_EXISTANT; - zend_hash_move_forward(HASH_OF(environment))) { - if (key_type == HASH_KEY_IS_STRING) { - zval **value; - - if (zend_hash_get_current_data(HASH_OF(environment), (void**)&value) == SUCCESS) { - zval copyval = **value; - - zval_copy_ctor(©val); - convert_to_string(©val); - if (libssh2_channel_setenv_ex(channel, key, key_len, Z_STRVAL(copyval), Z_STRLEN(copyval))) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed setting %s=%s on remote end", key, Z_STRVAL(copyval)); - } - zval_dtor(©val); - } - } else { - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Skipping numeric index in environment array"); - } - } - } - - if (type == PHP_SSH2_TERM_UNIT_CHARS) { - if (libssh2_channel_request_pty_ex(channel, term, term_len, NULL, 0, width, height, 0, 0)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating %s pty at %ldx%ld characters", term, width, height); - libssh2_channel_free(channel); - return NULL; - } - } else { - if (libssh2_channel_request_pty_ex(channel, term, term_len, NULL, 0, 0, 0, width, height)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating %s pty at %ldx%ld pixels", term, width, height); - libssh2_channel_free(channel); - return NULL; - } - } - - if (libssh2_channel_shell(channel)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to request shell from remote host"); - libssh2_channel_free(channel); - return NULL; - } - - /* Turn it into a stream */ - channel_data = emalloc(sizeof(php_ssh2_channel_data)); - channel_data->channel = channel; - channel_data->streamid = 0; - channel_data->is_blocking = 0; - channel_data->session_rsrc = resource_id; - channel_data->refcount = NULL; - - stream = php_stream_alloc(&php_ssh2_channel_stream_ops, channel_data, 0, "r+"); - - return stream; -} -/* }}} */ - -/* {{{ php_ssh2_fopen_wrapper_shell - * ssh2.shell:// fopen wrapper - */ -static php_stream *php_ssh2_fopen_wrapper_shell(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) -{ - LIBSSH2_SESSION *session = NULL; - php_stream *stream; - zval **tmpzval, *environment = NULL; - char *terminal = PHP_SSH2_DEFAULT_TERMINAL; - long width = PHP_SSH2_DEFAULT_TERM_WIDTH; - long height = PHP_SSH2_DEFAULT_TERM_HEIGHT; - long type = PHP_SSH2_DEFAULT_TERM_UNIT; - int resource_id = 0, terminal_len = sizeof(PHP_SSH2_DEFAULT_TERMINAL) - 1; - php_url *resource; - char *s, *e; - - resource = php_ssh2_fopen_wraper_parse_path(path, "shell", context, &session, &resource_id, NULL, NULL TSRMLS_CC); - if (!resource || !session) { - return NULL; - } - - if (context && - php_stream_context_get_option(context, "ssh2", "env", &tmpzval) == SUCCESS && - tmpzval && *tmpzval && Z_TYPE_PP(tmpzval) == IS_ARRAY) { - environment = *tmpzval; - } - - if (context && - php_stream_context_get_option(context, "ssh2", "term", &tmpzval) == SUCCESS && - tmpzval && *tmpzval && Z_TYPE_PP(tmpzval) == IS_STRING) { - terminal = Z_STRVAL_PP(tmpzval); - terminal_len = Z_STRLEN_PP(tmpzval); - } - - if (context && - php_stream_context_get_option(context, "ssh2", "term_width", &tmpzval) == SUCCESS && - tmpzval && *tmpzval) { - zval *copyval; - ALLOC_INIT_ZVAL(copyval); - *copyval = **tmpzval; - convert_to_long(copyval); - width = Z_LVAL_P(copyval); - zval_ptr_dtor(©val); - } - - if (context && - php_stream_context_get_option(context, "ssh2", "term_height", &tmpzval) == SUCCESS && - tmpzval && *tmpzval) { - zval *copyval; - ALLOC_INIT_ZVAL(copyval); - *copyval = **tmpzval; - convert_to_long(copyval); - height = Z_LVAL_P(copyval); - zval_ptr_dtor(©val); - } - - if (context && - php_stream_context_get_option(context, "ssh2", "term_units", &tmpzval) == SUCCESS && - tmpzval && *tmpzval) { - zval *copyval; - ALLOC_INIT_ZVAL(copyval); - *copyval = **tmpzval; - convert_to_long(copyval); - type = Z_LVAL_P(copyval); - zval_ptr_dtor(©val); - } - - s = resource->path ? resource->path : NULL; - e = s ? s + strlen(s) : NULL; - - if (s && s[0] == '/') { - /* Terminal type encoded into URL overrides context terminal type */ - char *p; - - s++; - p = strchr(s, '/'); - if (p) { - if (p - s) { - terminal = s; - terminal_len = p - terminal; - s += terminal_len + 1; - } else { - /* "null" terminal given, skip it */ - s++; - } - } else { - int len; - - if ((len = strlen(path + 1))) { - terminal = s; - terminal_len = len; - s += len; - } - } - } - - /* TODO: Accept resolution and environment vars as URL style parameters - * ssh2.shell://hostorresource/terminal/99x99c?envvar=envval&envvar=envval.... - */ - stream = php_ssh2_shell_open(session, resource_id, terminal, terminal_len, environment, width, height, type TSRMLS_CC); - if (!stream) { - zend_list_delete(resource_id); - } - php_url_free(resource); - - return stream; -} -/* }}} */ - -static php_stream_wrapper_ops php_ssh2_shell_stream_wops = { - php_ssh2_fopen_wrapper_shell, - NULL, /* stream_close */ - NULL, /* stat */ - NULL, /* stat_url */ - NULL, /* opendir */ - "ssh2.shell" -}; - -php_stream_wrapper php_ssh2_stream_wrapper_shell = { - &php_ssh2_shell_stream_wops, - NULL, - 0 -}; - -/* {{{ proto stream ssh2_shell(resource session[, string term_type[, array env[, int width, int height[, int width_height_type]]]]) - * Open a shell at the remote end and allocate a channel for it - */ -PHP_FUNCTION(ssh2_shell) -{ - LIBSSH2_SESSION *session; - php_stream *stream; - zval *zsession; - zval *environment = NULL; - char *term = PHP_SSH2_DEFAULT_TERMINAL; - int term_len = sizeof(PHP_SSH2_DEFAULT_TERMINAL) - 1; - long width = PHP_SSH2_DEFAULT_TERM_WIDTH; - long height = PHP_SSH2_DEFAULT_TERM_HEIGHT; - long type = PHP_SSH2_DEFAULT_TERM_UNIT; - int argc = ZEND_NUM_ARGS(); - - if (argc == 5) { - php_error_docref(NULL TSRMLS_CC, E_ERROR, "width specified without height paramter"); - RETURN_FALSE; - } - - if (zend_parse_parameters(argc TSRMLS_CC, "r|sa!lll", &zsession, &term, &term_len, &environment, &width, &height, &type) == FAILURE) { - RETURN_FALSE; - } - - ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); - - stream = php_ssh2_shell_open(session, Z_LVAL_P(zsession), term, term_len, environment, width, height, type TSRMLS_CC); - if (!stream) { - RETURN_FALSE; - } - - /* Ensure that channels are freed BEFORE the sessions they belong to */ - zend_list_addref(Z_LVAL_P(zsession)); - - php_stream_to_zval(stream, return_value); -} -/* }}} */ - -/* **************** - * Exec Wrapper * - **************** */ - -/* {{{ php_ssh2_exec_command - * Make a stream from a session - */ -static php_stream *php_ssh2_exec_command(LIBSSH2_SESSION *session, int resource_id, char *command, char *term, int term_len, zval *environment, long width, long height, long type TSRMLS_DC) -{ - LIBSSH2_CHANNEL *channel; - php_ssh2_channel_data *channel_data; - php_stream *stream; - - channel = libssh2_channel_open_session(session); - if (!channel) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to request a channel from remote host"); - return NULL; - } - - if (environment) { - char *key; - int key_type, key_len; - long idx; - - for(zend_hash_internal_pointer_reset(HASH_OF(environment)); - (key_type = zend_hash_get_current_key_ex(HASH_OF(environment), &key, &key_len, &idx, 0, NULL)) != HASH_KEY_NON_EXISTANT; - zend_hash_move_forward(HASH_OF(environment))) { - if (key_type == HASH_KEY_IS_STRING) { - zval **value; - - if (zend_hash_get_current_data(HASH_OF(environment), (void**)&value) == SUCCESS) { - zval copyval = **value; - - zval_copy_ctor(©val); - convert_to_string(©val); - if (libssh2_channel_setenv_ex(channel, key, key_len, Z_STRVAL(copyval), Z_STRLEN(copyval))) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed setting %s=%s on remote end", key, Z_STRVAL(copyval)); - } - zval_dtor(©val); - } - } else { - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Skipping numeric index in environment array"); - } - } - } - - if (term) { - if (type == PHP_SSH2_TERM_UNIT_CHARS) { - if (libssh2_channel_request_pty_ex(channel, term, term_len, NULL, 0, width, height, 0, 0)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating %s pty at %ldx%ld characters", term, width, height); - libssh2_channel_free(channel); - return NULL; - } - } else { - if (libssh2_channel_request_pty_ex(channel, term, term_len, NULL, 0, 0, 0, width, height)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating %s pty at %ldx%ld pixels", term, width, height); - libssh2_channel_free(channel); - return NULL; - } - } - } - - if (libssh2_channel_exec(channel, command)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to request command execution on remote host"); - libssh2_channel_free(channel); - return NULL; - } - - /* Turn it into a stream */ - channel_data = emalloc(sizeof(php_ssh2_channel_data)); - channel_data->channel = channel; - channel_data->streamid = 0; - channel_data->is_blocking = 0; - channel_data->session_rsrc = resource_id; - channel_data->refcount = NULL; - - stream = php_stream_alloc(&php_ssh2_channel_stream_ops, channel_data, 0, "r+"); - - return stream; -} -/* }}} */ - -/* {{{ php_ssh2_fopen_wrapper_exec - * ssh2.exec:// fopen wrapper - */ -static php_stream *php_ssh2_fopen_wrapper_exec(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) -{ - LIBSSH2_SESSION *session = NULL; - php_stream *stream; - zval **tmpzval, *environment = NULL; - int resource_id = 0; - php_url *resource; - char *terminal = NULL; - int terminal_len = 0; - long width = PHP_SSH2_DEFAULT_TERM_WIDTH; - long height = PHP_SSH2_DEFAULT_TERM_HEIGHT; - long type = PHP_SSH2_DEFAULT_TERM_UNIT; - - resource = php_ssh2_fopen_wraper_parse_path(path, "exec", context, &session, &resource_id, NULL, NULL TSRMLS_CC); - if (!resource || !session) { - return NULL; - } - if (!resource->path) { - php_url_free(resource); - zend_list_delete(resource_id); - return NULL; - } - - if (context && - php_stream_context_get_option(context, "ssh2", "env", &tmpzval) == SUCCESS && - tmpzval && *tmpzval && Z_TYPE_PP(tmpzval) == IS_ARRAY) { - environment = *tmpzval; - } - - if (context && - php_stream_context_get_option(context, "ssh2", "term", &tmpzval) == SUCCESS && - tmpzval && *tmpzval && Z_TYPE_PP(tmpzval) == IS_STRING) { - terminal = Z_STRVAL_PP(tmpzval); - terminal_len = Z_STRLEN_PP(tmpzval); - } - - if (context && - php_stream_context_get_option(context, "ssh2", "term_width", &tmpzval) == SUCCESS && - tmpzval && *tmpzval) { - zval *copyval; - ALLOC_INIT_ZVAL(copyval); - *copyval = **tmpzval; - convert_to_long(copyval); - width = Z_LVAL_P(copyval); - zval_ptr_dtor(©val); - } - - if (context && - php_stream_context_get_option(context, "ssh2", "term_height", &tmpzval) == SUCCESS && - tmpzval && *tmpzval) { - zval *copyval; - ALLOC_INIT_ZVAL(copyval); - *copyval = **tmpzval; - convert_to_long(copyval); - height = Z_LVAL_P(copyval); - zval_ptr_dtor(©val); - } - - if (context && - php_stream_context_get_option(context, "ssh2", "term_units", &tmpzval) == SUCCESS && - tmpzval && *tmpzval) { - zval *copyval; - ALLOC_INIT_ZVAL(copyval); - *copyval = **tmpzval; - convert_to_long(copyval); - type = Z_LVAL_P(copyval); - zval_ptr_dtor(©val); - } - - stream = php_ssh2_exec_command(session, resource_id, resource->path + 1, terminal, terminal_len, environment, width, height, type TSRMLS_CC); - if (!stream) { - zend_list_delete(resource_id); - } - php_url_free(resource); - - return stream; -} -/* }}} */ - -static php_stream_wrapper_ops php_ssh2_exec_stream_wops = { - php_ssh2_fopen_wrapper_exec, - NULL, /* stream_close */ - NULL, /* stat */ - NULL, /* stat_url */ - NULL, /* opendir */ - "ssh2.exec" -}; - -php_stream_wrapper php_ssh2_stream_wrapper_exec = { - &php_ssh2_exec_stream_wops, - NULL, - 0 -}; - -/* {{{ proto stream ssh2_exec(resource session, string command[, string pty[, array env[, int width[, int heightp[, int width_height_type]]]]]) - * Execute a command at the remote end and allocate a channel for it - * - * This function has a dirty little secret.... pty and env can be in either order.... shhhh... don't tell anyone - */ -PHP_FUNCTION(ssh2_exec) -{ - LIBSSH2_SESSION *session; - php_stream *stream; - zval *zsession; - zval *environment = NULL; - zval *zpty = NULL; - char *command; - int command_len; - long width = PHP_SSH2_DEFAULT_TERM_WIDTH; - long height = PHP_SSH2_DEFAULT_TERM_HEIGHT; - long type = PHP_SSH2_DEFAULT_TERM_UNIT; - char *term = NULL; - int term_len = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|z!z!lll", &zsession, &command, &command_len, &zpty, &environment, &width, &height, &type) == FAILURE) { - RETURN_FALSE; - } - - if (zpty && Z_TYPE_P(zpty) == IS_ARRAY) { - /* Swap pty and environment -- old call style */ - zval *tmp = zpty; - zpty = environment; - environment = tmp; - } - - if (environment && Z_TYPE_P(environment) != IS_ARRAY) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "ssh2_exec() expects arg 4 to be of type array"); - RETURN_FALSE; - } - - if (zpty) { - convert_to_string(zpty); - term = Z_STRVAL_P(zpty); - term_len = Z_STRLEN_P(zpty); - } - - ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); - - stream = php_ssh2_exec_command(session, Z_LVAL_P(zsession), command, term, term_len, environment, width, height, type TSRMLS_CC); - if (!stream) { - RETURN_FALSE; - } - - /* Ensure that channels are freed BEFORE the sessions they belong to */ - zend_list_addref(Z_LVAL_P(zsession)); - - php_stream_to_zval(stream, return_value); -} -/* }}} */ - -/* *************** - * SCP Wrapper * - *************** */ - -/* {{{ php_ssh2_scp_xfer - * Make a stream from a session - */ -static php_stream *php_ssh2_scp_xfer(LIBSSH2_SESSION *session, int resource_id, char *filename TSRMLS_DC) -{ - LIBSSH2_CHANNEL *channel; - php_ssh2_channel_data *channel_data; - php_stream *stream; - - channel = libssh2_scp_recv(session, filename, NULL); - if (!channel) { - char *error = ""; - libssh2_session_last_error(session, &error, NULL, 0); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to request a channel from remote host: %s", error); - return NULL; - } - - /* Turn it into a stream */ - channel_data = emalloc(sizeof(php_ssh2_channel_data)); - channel_data->channel = channel; - channel_data->streamid = 0; - channel_data->is_blocking = 0; - channel_data->session_rsrc = resource_id; - channel_data->refcount = NULL; - - stream = php_stream_alloc(&php_ssh2_channel_stream_ops, channel_data, 0, "r"); - - return stream; -} -/* }}} */ - -/* {{{ php_ssh2_fopen_wrapper_scp - * ssh2.scp:// fopen wrapper (Read mode only, if you want to know why write mode isn't supported as a stream, take a look at the SCP protocol) - */ -static php_stream *php_ssh2_fopen_wrapper_scp(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) -{ - LIBSSH2_SESSION *session = NULL; - php_stream *stream; - int resource_id = 0; - php_url *resource; - - if (strchr(mode, '+') || strchr(mode, 'a') || strchr(mode, 'w')) { - return NULL; - } - - resource = php_ssh2_fopen_wraper_parse_path(path, "scp", context, &session, &resource_id, NULL, NULL TSRMLS_CC); - if (!resource || !session) { - return NULL; - } - if (!resource->path) { - php_url_free(resource); - zend_list_delete(resource_id); - return NULL; - } - - stream = php_ssh2_scp_xfer(session, resource_id, resource->path TSRMLS_CC); - if (!stream) { - zend_list_delete(resource_id); - } - php_url_free(resource); - - return stream; -} -/* }}} */ - -static php_stream_wrapper_ops php_ssh2_scp_stream_wops = { - php_ssh2_fopen_wrapper_scp, - NULL, /* stream_close */ - NULL, /* stat */ - NULL, /* stat_url */ - NULL, /* opendir */ - "ssh2.scp" -}; - -php_stream_wrapper php_ssh2_stream_wrapper_scp = { - &php_ssh2_scp_stream_wops, - NULL, - 0 -}; - -/* {{{ proto bool ssh2_scp_recv(resource session, string remote_file, string local_file) - * Request a file via SCP - */ -PHP_FUNCTION(ssh2_scp_recv) -{ - LIBSSH2_SESSION *session; - LIBSSH2_CHANNEL *remote_file; - struct stat sb; - php_stream *local_file; - zval *zsession; - char *remote_filename, *local_filename; - int remote_filename_len, local_filename_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rss", &zsession, &remote_filename, &remote_filename_len, - &local_filename, &local_filename_len) == FAILURE) { - RETURN_FALSE; - } - - ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); - - remote_file = libssh2_scp_recv(session, remote_filename, &sb); - if (!remote_file) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to receive remote file"); - RETURN_FALSE; - } - libssh2_channel_set_blocking(remote_file, 1); - - local_file = php_stream_open_wrapper(local_filename, "wb", ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL); - if (!local_file) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to write to local file"); - libssh2_channel_free(remote_file); - RETURN_FALSE; - } - - while (sb.st_size) { - char buffer[8192]; - int bytes_read; - - bytes_read = libssh2_channel_read(remote_file, buffer, sb.st_size > 8192 ? 8192 : sb.st_size); - if (bytes_read < 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error reading from remote file"); - libssh2_channel_free(remote_file); - php_stream_close(local_file); - RETURN_FALSE; - } - php_stream_write(local_file, buffer, bytes_read); - sb.st_size -= bytes_read; - } - - libssh2_channel_free(remote_file); - php_stream_close(local_file); - - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto stream ssh2_scp_send(resource session, string local_file, string remote_file[, int create_mode = 0644]) - * Send a file via SCP - */ -PHP_FUNCTION(ssh2_scp_send) -{ - LIBSSH2_SESSION *session; - LIBSSH2_CHANNEL *remote_file; - php_stream *local_file; - zval *zsession; - char *local_filename, *remote_filename; - int local_filename_len, remote_filename_len; - long create_mode = 0644; - php_stream_statbuf ssb; - int argc = ZEND_NUM_ARGS(); - - if (zend_parse_parameters(argc TSRMLS_CC, "rss|l", &zsession, &local_filename, &local_filename_len, - &remote_filename, &remote_filename_len, &create_mode) == FAILURE) { - RETURN_FALSE; - } - - ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); - - local_file = php_stream_open_wrapper(local_filename, "rb", ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL); - if (!local_file) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to read source file"); - RETURN_FALSE; - } - - if (php_stream_stat(local_file, &ssb)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed statting local file"); - php_stream_close(local_file); - RETURN_FALSE; - } - - if (argc < 4) { - create_mode = ssb.sb.st_mode & 0777; - } - - remote_file = libssh2_scp_send_ex(session, remote_filename, create_mode, ssb.sb.st_size, ssb.sb.st_atime, ssb.sb.st_mtime); - if (!remote_file) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failure creating remote file"); - php_stream_close(local_file); - RETURN_FALSE; - } - libssh2_channel_set_blocking(remote_file, 1); - - while (ssb.sb.st_size) { - char buffer[8192]; - size_t toread = MIN(8192, ssb.sb.st_size); - size_t bytesread = php_stream_read(local_file, buffer, toread); - - if (bytesread <= 0 || bytesread > toread) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed copying file"); - php_stream_close(local_file); - libssh2_channel_free(remote_file); - RETURN_FALSE; - } - - if (bytesread != libssh2_channel_write(remote_file, buffer, bytesread)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed copying file"); - php_stream_close(local_file); - libssh2_channel_free(remote_file); - RETURN_FALSE; - } - ssb.sb.st_size -= bytesread; - } - - php_stream_close(local_file); - libssh2_channel_free(remote_file); - RETURN_TRUE; -} -/* }}} */ - -/* *************************** - * Direct TCP/IP Transport * - *************************** */ - -/* {{{ php_ssh2_direct_tcpip - * Make a stream from a session - */ -static php_stream *php_ssh2_direct_tcpip(LIBSSH2_SESSION *session, int resource_id, char *host, int port TSRMLS_DC) -{ - LIBSSH2_CHANNEL *channel; - php_ssh2_channel_data *channel_data; - php_stream *stream; - - channel = libssh2_channel_direct_tcpip(session, host, port); - if (!channel) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to request a channel from remote host"); - return NULL; - } - - /* Turn it into a stream */ - channel_data = emalloc(sizeof(php_ssh2_channel_data)); - channel_data->channel = channel; - channel_data->streamid = 0; - channel_data->is_blocking = 0; - channel_data->session_rsrc = resource_id; - channel_data->refcount = NULL; - - stream = php_stream_alloc(&php_ssh2_channel_stream_ops, channel_data, 0, "r+"); - - return stream; -} -/* }}} */ - -/* {{{ php_ssh2_fopen_wrapper_tunnel - * ssh2.tunnel:// fopen wrapper - */ -static php_stream *php_ssh2_fopen_wrapper_tunnel(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) -{ - LIBSSH2_SESSION *session = NULL; - php_stream *stream = NULL; - php_url *resource; - char *host = NULL; - int port = 0; - int resource_id = 0; - - resource = php_ssh2_fopen_wraper_parse_path(path, "tunnel", context, &session, &resource_id, NULL, NULL TSRMLS_CC); - if (!resource || !session) { - return NULL; - } - - if (resource->path && resource->path[0] == '/') { - char *colon; - - host = resource->path + 1; - if (*host == '[') { - /* IPv6 Encapsulated Format */ - host++; - colon = strstr(host, "]:"); - if (colon) { - *colon = 0; - colon += 2; - } - } else { - colon = strchr(host, ':'); - if (colon) { - *(colon++) = 0; - } - } - if (colon) { - port = atoi(colon); - } - } - - if ((port <= 0) || (port > 65535) || !host || (strlen(host) == 0)) { - /* Invalid connection criteria */ - php_url_free(resource); - zend_list_delete(resource_id); - return NULL; - } - - stream = php_ssh2_direct_tcpip(session, resource_id, host, port TSRMLS_CC); - if (!stream) { - zend_list_delete(resource_id); - } - php_url_free(resource); - - return stream; -} -/* }}} */ - -static php_stream_wrapper_ops php_ssh2_tunnel_stream_wops = { - php_ssh2_fopen_wrapper_tunnel, - NULL, /* stream_close */ - NULL, /* stat */ - NULL, /* stat_url */ - NULL, /* opendir */ - "ssh2.tunnel" -}; - -php_stream_wrapper php_ssh2_stream_wrapper_tunnel = { - &php_ssh2_tunnel_stream_wops, - NULL, - 0 -}; - -/* {{{ proto stream ssh2_tunnel(resource session, string host, int port) - * Tunnel to remote TCP/IP host/port - */ -PHP_FUNCTION(ssh2_tunnel) -{ - LIBSSH2_SESSION *session; - php_stream *stream; - zval *zsession; - char *host; - int host_len; - long port; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsl", &zsession, &host, &host_len, &port) == FAILURE) { - RETURN_FALSE; - } - - ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); - - stream = php_ssh2_direct_tcpip(session, Z_LVAL_P(zsession), host, port TSRMLS_CC); - if (!stream) { - RETURN_FALSE; - } - - /* Ensure that channels are freed BEFORE the sessions they belong to */ - zend_list_addref(Z_LVAL_P(zsession)); - - php_stream_to_zval(stream, return_value); -} -/* }}} */ - -/* ****************** - * Generic Helper * - ****************** */ - -/* {{{ proto stream ssh2_fetch_stream(stream channel, int streamid) - * Fetch an extended data stream - */ -PHP_FUNCTION(ssh2_fetch_stream) -{ - php_ssh2_channel_data *data, *stream_data; - php_stream *parent, *stream; - zval *zparent; - long streamid; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &zparent, &streamid) == FAILURE) { - RETURN_FALSE; - } - - if (streamid < 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid stream ID requested"); - RETURN_FALSE; - } - - php_stream_from_zval(parent, &zparent); - - if (parent->ops != &php_ssh2_channel_stream_ops) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Provided stream is not of type " PHP_SSH2_CHANNEL_STREAM_NAME); - RETURN_FALSE; - } - - data = (php_ssh2_channel_data*)parent->abstract; - - if (!data->refcount) { - data->refcount = emalloc(sizeof(unsigned char)); - *(data->refcount) = 1; - } - - if (*(data->refcount) == 255) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Too many streams associated to a single channel"); - RETURN_FALSE; - } - - (*(data->refcount))++; - - stream_data = emalloc(sizeof(php_ssh2_channel_data)); - memcpy(stream_data, data, sizeof(php_ssh2_channel_data)); - stream_data->streamid = streamid; - - stream = php_stream_alloc(&php_ssh2_channel_stream_ops, stream_data, 0, "r+"); - if (!stream) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error opening substream"); - efree(stream_data); - *(data->refcount)--; - RETURN_FALSE; - } - - php_stream_to_zval(stream, return_value); -} -/* }}} */ - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * End: - * vim600: noet sw=4 ts=4 fdm=marker - * vim<600: noet sw=4 ts=4 - */ diff -Nru php-ssh2-0.11.2/ssh2-0.11.2/ssh2_sftp.c php-ssh2-0.11.3/ssh2-0.11.2/ssh2_sftp.c --- php-ssh2-0.11.2/ssh2-0.11.2/ssh2_sftp.c 2010-11-03 22:24:33.000000000 +0000 +++ php-ssh2-0.11.3/ssh2-0.11.2/ssh2_sftp.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,852 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | PHP Version 4 | - +----------------------------------------------------------------------+ - | Copyright (c) 1997-2006 The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 2.02 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available at through the world-wide-web at | - | http://www.php.net/license/2_02.txt. | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Author: Sara Golemon | - +----------------------------------------------------------------------+ - - $Id: ssh2_sftp.c 305068 2010-11-03 22:24:19Z pajoye $ -*/ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "php.h" -#include "php_ssh2.h" -#include "ext/standard/php_string.h" - -/* ************************* - * Resource Housekeeping * - ************************* */ - -void php_ssh2_sftp_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) -{ - php_ssh2_sftp_data *data = (php_ssh2_sftp_data*)rsrc->ptr; - - if (!data) { - return; - } - - libssh2_sftp_shutdown(data->sftp); - - zend_list_delete(data->session_rsrcid); - - efree(data); -} - -/* ***************** - * SFTP File Ops * - ***************** */ - -inline unsigned long php_ssh2_parse_fopen_modes(char *openmode) { - unsigned long flags = 0; - - if (strchr(openmode, 'a')) { - flags |= LIBSSH2_FXF_APPEND; - } - - if (strchr(openmode, 'w')) { - flags |= LIBSSH2_FXF_WRITE | LIBSSH2_FXF_TRUNC | LIBSSH2_FXF_CREAT; - } - - if (strchr(openmode, 'r')) { - flags |= LIBSSH2_FXF_READ; - } - - if (strchr(openmode, '+')) { - flags |= LIBSSH2_FXF_READ | LIBSSH2_FXF_WRITE; - } - - if (strchr(openmode, 'x')) { - flags |= LIBSSH2_FXF_WRITE | LIBSSH2_FXF_TRUNC | LIBSSH2_FXF_EXCL | LIBSSH2_FXF_CREAT; - } - - return flags; -} - -inline int php_ssh2_sftp_attr2ssb(php_stream_statbuf *ssb, LIBSSH2_SFTP_ATTRIBUTES *attrs) -{ - memset(ssb, 0, sizeof(php_stream_statbuf)); - if (attrs->flags & LIBSSH2_SFTP_ATTR_SIZE) { - ssb->sb.st_size = attrs->filesize; - } - if (attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) { - ssb->sb.st_uid = attrs->uid; - ssb->sb.st_gid = attrs->gid; - } - if (attrs->flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) { - ssb->sb.st_mode = attrs->permissions; - } - if (attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) { - ssb->sb.st_atime = attrs->atime; - ssb->sb.st_mtime = attrs->mtime; - } - - return 0; -} - -typedef struct _php_ssh2_sftp_handle_data { - LIBSSH2_SFTP_HANDLE *handle; - - long sftp_rsrcid; -} php_ssh2_sftp_handle_data; - -/* {{{ php_ssh2_sftp_stream_write - */ -static size_t php_ssh2_sftp_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) -{ - php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data*)stream->abstract; - - return libssh2_sftp_write(data->handle, buf, count); -} -/* }}} */ - -/* {{{ php_ssh2_sftp_stream_read - */ -static size_t php_ssh2_sftp_stream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) -{ - php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data*)stream->abstract; - - return libssh2_sftp_read(data->handle, buf, count); -} -/* }}} */ - -/* {{{ php_ssh2_sftp_stream_close - */ -static int php_ssh2_sftp_stream_close(php_stream *stream, int close_handle TSRMLS_DC) -{ - php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data*)stream->abstract; - - libssh2_sftp_close(data->handle); - zend_list_delete(data->sftp_rsrcid); - efree(data); - - return 0; -} -/* }}} */ - -/* {{{ php_ssh2_sftp_stream_seek - */ -static int php_ssh2_sftp_stream_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC) -{ - php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data*)stream->abstract; - - switch (whence) { - case SEEK_END: - { - LIBSSH2_SFTP_ATTRIBUTES attrs; - - if (libssh2_sftp_fstat(data->handle, &attrs)) { - return -1; - } - if ((attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) == 0) { - return -1; - } - offset += attrs.filesize; - } - case SEEK_CUR: - { - off_t current_offset = libssh2_sftp_tell(data->handle); - - if (current_offset < 0) { - return -1; - } - - offset += current_offset; - } - } - - libssh2_sftp_seek(data->handle, offset); - - if (newoffset) { - *newoffset = offset; - } - - return 0; -} -/* }}} */ - -/* {{{ php_ssh2_sftp_stream_fstat - */ -static int php_ssh2_sftp_stream_fstat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) -{ - php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data*)stream->abstract; - LIBSSH2_SFTP_ATTRIBUTES attrs; - - if (libssh2_sftp_fstat(data->handle, &attrs)) { - return -1; - } - - return php_ssh2_sftp_attr2ssb(ssb, &attrs); -} -/* }}} */ - -static php_stream_ops php_ssh2_sftp_stream_ops = { - php_ssh2_sftp_stream_write, - php_ssh2_sftp_stream_read, - php_ssh2_sftp_stream_close, - NULL, /* flush */ - PHP_SSH2_SFTP_STREAM_NAME, - php_ssh2_sftp_stream_seek, - NULL, /* cast */ - php_ssh2_sftp_stream_fstat, - NULL, /* set_option */ -}; - -/* {{{ php_ssh2_sftp_stream_opener - */ -static php_stream *php_ssh2_sftp_stream_opener( php_stream_wrapper *wrapper, char *filename, char *mode, - int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) -{ - php_ssh2_sftp_handle_data *data; - LIBSSH2_SESSION *session = NULL; - LIBSSH2_SFTP *sftp = NULL; - LIBSSH2_SFTP_HANDLE *handle; - php_stream *stream; - int resource_id = 0, sftp_rsrcid = 0; - php_url *resource; - unsigned long flags; - long perms = 0644; - - resource = php_ssh2_fopen_wraper_parse_path(filename, "sftp", context, &session, &resource_id, &sftp, &sftp_rsrcid TSRMLS_CC); - if (!resource || !session || !sftp) { - return NULL; - } - - flags = php_ssh2_parse_fopen_modes(mode); - - handle = libssh2_sftp_open(sftp, resource->path, flags, perms); - if (!handle) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to open %s on remote host", filename); - php_url_free(resource); - zend_list_delete(sftp_rsrcid); - return NULL; - } - - data = emalloc(sizeof(php_ssh2_sftp_handle_data)); - data->handle = handle; - data->sftp_rsrcid = sftp_rsrcid; - - stream = php_stream_alloc(&php_ssh2_sftp_stream_ops, data, 0, mode); - if (!stream) { - libssh2_sftp_close(handle); - zend_list_delete(sftp_rsrcid); - efree(data); - } - php_url_free(resource); - - return stream; -} -/* }}} */ - -/* ********************** - * SFTP Directory Ops * - ********************** */ - -/* {{{ php_ssh2_sftp_dirstream_read - */ -static size_t php_ssh2_sftp_dirstream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) -{ - php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data*)stream->abstract; - php_stream_dirent *ent = (php_stream_dirent*)buf; - size_t bytesread = libssh2_sftp_readdir(data->handle, ent->d_name, sizeof(ent->d_name) - 1, NULL); - char *basename = NULL; - int basename_len = 0; - - if (bytesread <= 0) { - return 0; - } - ent->d_name[bytesread] = 0; - -#ifdef ZEND_ENGINE_2 - php_basename(ent->d_name, bytesread, NULL, 0, &basename, &basename_len TSRMLS_CC); -#else -/* CURSE YOU BC BREAKS! */ - basename = php_basename(ent->d_name, bytesread, NULL, 0); - if (basename) { - basename_len = strlen(basename); - } -#endif - if (!basename) { - return 0; - } - - if (!basename_len) { - efree(basename); - return 0; - } - bytesread = MIN(sizeof(ent->d_name) - 1, basename_len); - memcpy(ent->d_name, basename, bytesread); - ent->d_name[bytesread] = 0; - efree(basename); - - /* Trim off trailing whitespace characters */ - bytesread--; - while (bytesread >= 0 && - (ent->d_name[bytesread] == '\n' || ent->d_name[bytesread] == '\r' || - ent->d_name[bytesread] == '\t' || ent->d_name[bytesread] == ' ')) { - ent->d_name[bytesread--] = '\0'; - } - - return sizeof(php_stream_dirent); -} -/* }}} */ - -/* {{{ php_ssh2_sftp_dirstream_close - */ -static int php_ssh2_sftp_dirstream_close(php_stream *stream, int close_handle TSRMLS_DC) -{ - php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data*)stream->abstract; - - libssh2_sftp_close(data->handle); - zend_list_delete(data->sftp_rsrcid); - efree(data); - - return 0; -} -/* }}} */ - -static php_stream_ops php_ssh2_sftp_dirstream_ops = { - NULL, /* write */ - php_ssh2_sftp_dirstream_read, - php_ssh2_sftp_dirstream_close, - NULL, /* flush */ - PHP_SSH2_SFTP_DIRSTREAM_NAME, - NULL, /* seek */ - NULL, /* cast */ - NULL, /* fstat */ - NULL, /* set_option */ -}; - -/* {{{ php_ssh2_sftp_dirstream_opener - */ -static php_stream *php_ssh2_sftp_dirstream_opener( php_stream_wrapper *wrapper, char *filename, char *mode, - int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) -{ - php_ssh2_sftp_handle_data *data; - LIBSSH2_SESSION *session = NULL; - LIBSSH2_SFTP *sftp = NULL; - LIBSSH2_SFTP_HANDLE *handle; - php_stream *stream; - int resource_id = 0, sftp_rsrcid = 0; - php_url *resource; - - resource = php_ssh2_fopen_wraper_parse_path(filename, "sftp", context, &session, &resource_id, &sftp, &sftp_rsrcid TSRMLS_CC); - if (!resource || !session || !sftp) { - return NULL; - } - - handle = libssh2_sftp_opendir(sftp, resource->path); - if (!handle) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to open %s on remote host", filename); - php_url_free(resource); - zend_list_delete(sftp_rsrcid); - return NULL; - } - - data = emalloc(sizeof(php_ssh2_sftp_handle_data)); - data->handle = handle; - data->sftp_rsrcid = sftp_rsrcid; - - stream = php_stream_alloc(&php_ssh2_sftp_dirstream_ops, data, 0, mode); - if (!stream) { - libssh2_sftp_close(handle); - zend_list_delete(sftp_rsrcid); - efree(data); - } - php_url_free(resource); - - return stream; -} -/* }}} */ - -/* **************** - * SFTP Wrapper * - **************** */ - -#ifdef ZEND_ENGINE_2 -/* {{{ php_ssh2_sftp_urlstat - */ -static int php_ssh2_sftp_urlstat(php_stream_wrapper *wrapper, char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC) -{ - LIBSSH2_SFTP_ATTRIBUTES attrs; - LIBSSH2_SESSION *session = NULL; - LIBSSH2_SFTP *sftp = NULL; - int resource_id = 0, sftp_rsrcid = 0; - php_url *resource; - - resource = php_ssh2_fopen_wraper_parse_path(url, "sftp", context, &session, &resource_id, &sftp, &sftp_rsrcid TSRMLS_CC); - if (!resource || !session || !sftp || !resource->path) { - return -1; - } - - if (libssh2_sftp_stat_ex(sftp, resource->path, strlen(resource->path), - (flags & PHP_STREAM_URL_STAT_LINK) ? LIBSSH2_SFTP_LSTAT : LIBSSH2_SFTP_STAT, &attrs)) { - php_url_free(resource); - zend_list_delete(sftp_rsrcid); - return -1; - } - - php_url_free(resource); - - /* parse_path addrefs the resource, but we're not holding on to it so we have to delref it before we leave */ - zend_list_delete(sftp_rsrcid); - - return php_ssh2_sftp_attr2ssb(ssb, &attrs); -} -/* }}} */ - -/* {{{ php_ssh2_sftp_unlink - */ -static int php_ssh2_sftp_unlink(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC) -{ - LIBSSH2_SESSION *session = NULL; - LIBSSH2_SFTP *sftp = NULL; - int resource_id = 0, sftp_rsrcid = 0; - php_url *resource; - int result; - - resource = php_ssh2_fopen_wraper_parse_path(url, "sftp", context, &session, &resource_id, &sftp, &sftp_rsrcid TSRMLS_CC); - if (!resource || !session || !sftp || !resource->path) { - if (resource) { - php_url_free(resource); - } - return 0; - } - - result = libssh2_sftp_unlink(sftp, resource->path); - php_url_free(resource); - - zend_list_delete(sftp_rsrcid); - - /* libssh2 uses 0 for success and the streams API uses 0 for failure, so invert */ - return (result == 0) ? -1 : 0; -} -/* }}} */ - -/* {{{ php_ssh2_sftp_rename - */ -static int php_ssh2_sftp_rename(php_stream_wrapper *wrapper, char *url_from, char *url_to, int options, php_stream_context *context TSRMLS_DC) -{ - LIBSSH2_SESSION *session = NULL; - LIBSSH2_SFTP *sftp = NULL; - int resource_id = 0, sftp_rsrcid = 0; - php_url *resource, *resource_to; - int result; - - if (strncmp(url_from, "ssh2.sftp://", sizeof("ssh2.sftp://") - 1) || - strncmp(url_to, "ssh2.sftp://", sizeof("ssh2.sftp://") - 1)) { - return 0; - } - - resource_to = php_url_parse(url_to); - if (!resource_to || !resource_to->path) { - if (resource_to) { - php_url_free(resource_to); - } - return 0; - } - - resource = php_ssh2_fopen_wraper_parse_path(url_from, "sftp", context, &session, &resource_id, &sftp, &sftp_rsrcid TSRMLS_CC); - if (!resource || !session || !sftp || !resource->path) { - if (resource) { - php_url_free(resource); - } - php_url_free(resource_to); - return 0; - } - - result = libssh2_sftp_rename(sftp, resource->path, resource_to->path); - php_url_free(resource); - php_url_free(resource_to); - - zend_list_delete(sftp_rsrcid); - - /* libssh2 uses 0 for success and the streams API uses 0 for failure, so invert */ - return (result == 0) ? -1 : 0; -} -/* }}} */ - -/* {{{ php_ssh2_sftp_mkdir - */ -static int php_ssh2_sftp_mkdir(php_stream_wrapper *wrapper, char *url, int mode, int options, php_stream_context *context TSRMLS_DC) -{ - LIBSSH2_SESSION *session = NULL; - LIBSSH2_SFTP *sftp = NULL; - int resource_id = 0, sftp_rsrcid = 0; - php_url *resource; - int result; - - resource = php_ssh2_fopen_wraper_parse_path(url, "sftp", context, &session, &resource_id, &sftp, &sftp_rsrcid TSRMLS_CC); - if (!resource || !session || !sftp || !resource->path) { - if (resource) { - php_url_free(resource); - } - return 0; - } - - if (options & PHP_STREAM_MKDIR_RECURSIVE) { - /* Just attempt to make every directory, some will fail, but we only care about the last success/failure */ - char *p = resource->path; - while ((p = strchr(p + 1, '/'))) { - libssh2_sftp_mkdir_ex(sftp, resource->path, p - resource->path, mode); - } - } - - result = libssh2_sftp_mkdir(sftp, resource->path, mode); - php_url_free(resource); - - zend_list_delete(sftp_rsrcid); - - /* libssh2 uses 0 for success and the streams API uses 0 for failure, so invert */ - return (result == 0) ? -1 : 0; -} -/* }}} */ - -/* {{{ php_ssh2_sftp_rmdir - */ -static int php_ssh2_sftp_rmdir(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC) -{ - LIBSSH2_SESSION *session = NULL; - LIBSSH2_SFTP *sftp = NULL; - int resource_id = 0, sftp_rsrcid = 0; - php_url *resource; - int result; - - resource = php_ssh2_fopen_wraper_parse_path(url, "sftp", context, &session, &resource_id, &sftp, &sftp_rsrcid TSRMLS_CC); - if (!resource || !session || !sftp || !resource->path) { - if (resource) { - php_url_free(resource); - } - return 0; - } - - result = libssh2_sftp_rmdir(sftp, resource->path); - php_url_free(resource); - - zend_list_delete(sftp_rsrcid); - - /* libssh2 uses 0 for success and the streams API uses 0 for failure, so invert */ - return (result == 0) ? -1 : 0; -} -/* }}} */ -#endif - -static php_stream_wrapper_ops php_ssh2_sftp_wrapper_ops = { - php_ssh2_sftp_stream_opener, - NULL, /* close */ - NULL, /* stat */ -#ifdef ZEND_ENGINE_2 - php_ssh2_sftp_urlstat, -#else - NULL, /* url_stat isn't actually functional prior to PHP5 */ -#endif - php_ssh2_sftp_dirstream_opener, - PHP_SSH2_SFTP_WRAPPER_NAME, -#ifdef ZEND_ENGINE_2 - php_ssh2_sftp_unlink, - php_ssh2_sftp_rename, - php_ssh2_sftp_mkdir, - php_ssh2_sftp_rmdir, -#endif -}; - -php_stream_wrapper php_ssh2_sftp_wrapper = { - &php_ssh2_sftp_wrapper_ops, - NULL, - 1, - 0, - NULL, -}; - -/* ***************** - * Userspace API * - ***************** */ - - -/* {{{ proto resource ssh2_sftp(resource session) - * Request the SFTP subsystem from an already connected SSH2 server - */ -PHP_FUNCTION(ssh2_sftp) -{ - LIBSSH2_SESSION *session; - LIBSSH2_SFTP *sftp; - php_ssh2_sftp_data *data; - zval *zsession; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zsession) == FAILURE) { - RETURN_FALSE; - } - - ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); - - sftp = libssh2_sftp_init(session); - if (!sftp) { - char *sess_err = "Unknown"; - - libssh2_session_last_error(session, &sess_err, NULL, 0); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to startup SFTP subsystem: %s", sess_err); - RETURN_FALSE; - } - - data = emalloc(sizeof(php_ssh2_sftp_data)); - data->session = session; - data->sftp = sftp; - data->session_rsrcid = Z_LVAL_P(zsession); - zend_list_addref(Z_LVAL_P(zsession)); - - ZEND_REGISTER_RESOURCE(return_value, data, le_ssh2_sftp); -} -/* }}} */ - -/* Much of the stuff below can be done via wrapper ops as of PHP5, but is included here for PHP 4.3 users */ - -/* {{{ proto bool ssh2_sftp_rename(resource sftp, string from, string to) - */ -PHP_FUNCTION(ssh2_sftp_rename) -{ - php_ssh2_sftp_data *data; - zval *zsftp; - char *src, *dst; - int src_len, dst_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rss", &zsftp, &src, &src_len, &dst, &dst_len) == FAILURE) { - RETURN_FALSE; - } - - ZEND_FETCH_RESOURCE(data, php_ssh2_sftp_data*, &zsftp, -1, PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp); - - RETURN_BOOL(!libssh2_sftp_rename_ex(data->sftp, src, src_len, dst, dst_len, - LIBSSH2_SFTP_RENAME_OVERWRITE | LIBSSH2_SFTP_RENAME_ATOMIC | LIBSSH2_SFTP_RENAME_NATIVE)); -} -/* }}} */ - -/* {{{ proto bool ssh2_sftp_unlink(resource sftp, string filename) - */ -PHP_FUNCTION(ssh2_sftp_unlink) -{ - php_ssh2_sftp_data *data; - zval *zsftp; - char *filename; - int filename_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &zsftp, &filename, &filename_len) == FAILURE) { - RETURN_FALSE; - } - - ZEND_FETCH_RESOURCE(data, php_ssh2_sftp_data*, &zsftp, -1, PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp); - - RETURN_BOOL(!libssh2_sftp_unlink_ex(data->sftp, filename, filename_len)); -} -/* }}} */ - -/* {{{ proto bool ssh2_sftp_mkdir(resource sftp, string filename[, int mode[, int recursive]]) - */ -PHP_FUNCTION(ssh2_sftp_mkdir) -{ - php_ssh2_sftp_data *data; - zval *zsftp; - char *filename; - int filename_len; - long mode = 0777; - zend_bool recursive = 0; - char *p; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|lb", &zsftp, &filename, &filename_len, &mode, &recursive) == FAILURE) { - RETURN_FALSE; - } - - if (filename_len < 1) { - RETURN_FALSE; - } - - ZEND_FETCH_RESOURCE(data, php_ssh2_sftp_data*, &zsftp, -1, PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp); - - if (recursive) { - /* Just attempt to make every directory, some will fail, but we only care about the last success/failure */ - p = filename; - while ((p = strchr(p + 1, '/'))) { - libssh2_sftp_mkdir_ex(data->sftp, filename, p - filename, mode); - } - } - - - RETURN_BOOL(!libssh2_sftp_mkdir_ex(data->sftp, filename, filename_len, mode)); -} -/* }}} */ - -/* {{{ proto bool ssh2_sftp_rmdir(resource sftp, string filename) - */ -PHP_FUNCTION(ssh2_sftp_rmdir) -{ - php_ssh2_sftp_data *data; - zval *zsftp; - char *filename; - int filename_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &zsftp, &filename, &filename_len) == FAILURE) { - RETURN_FALSE; - } - - ZEND_FETCH_RESOURCE(data, php_ssh2_sftp_data*, &zsftp, -1, PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp); - - RETURN_BOOL(!libssh2_sftp_rmdir_ex(data->sftp, filename, filename_len)); -} -/* }}} */ - -/* {{{ php_ssh2_sftp_stat_func - * In PHP4.3 this is the only way to request stat into, in PHP >= 5 you can use the fopen wrapper approach - * Both methods will return identical structures - * (well, the other one will include other values set to 0 but they don't count) - */ -static void php_ssh2_sftp_stat_func(INTERNAL_FUNCTION_PARAMETERS, int stat_type) -{ - php_ssh2_sftp_data *data; - LIBSSH2_SFTP_ATTRIBUTES attrs; - zval *zsftp; - char *path; - int path_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &zsftp, &path, &path_len) == FAILURE) { - RETURN_FALSE; - } - - ZEND_FETCH_RESOURCE(data, php_ssh2_sftp_data*, &zsftp, -1, PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp); - - if (libssh2_sftp_stat_ex(data->sftp, path, path_len, stat_type, &attrs)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to stat remote file"); - RETURN_FALSE; - } - - array_init(return_value); - - if (attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) { - add_index_long(return_value, 7, attrs.filesize); - add_assoc_long(return_value, "size", attrs.filesize); - } - if (attrs.flags & LIBSSH2_SFTP_ATTR_UIDGID) { - add_index_long(return_value, 4, attrs.uid); - add_assoc_long(return_value, "uid", attrs.uid); - - add_index_long(return_value, 5, attrs.gid); - add_assoc_long(return_value, "gid", attrs.gid); - } - if (attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) { - add_index_long(return_value, 2, attrs.permissions); - add_assoc_long(return_value, "mode", attrs.permissions); - } - if (attrs.flags & LIBSSH2_SFTP_ATTR_ACMODTIME) { - add_index_long(return_value, 8, attrs.atime); - add_assoc_long(return_value, "atime", attrs.atime); - - add_index_long(return_value, 9, attrs.mtime); - add_assoc_long(return_value, "mtime", attrs.mtime); - } -} -/* }}} */ - -/* {{{ proto array ssh2_sftp_stat(resource sftp, string path) - */ -PHP_FUNCTION(ssh2_sftp_stat) -{ - php_ssh2_sftp_stat_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, LIBSSH2_SFTP_STAT); -} -/* }}} */ - -/* {{{ proto array ssh2_sftp_lstat(resource sftp, string path) - */ -PHP_FUNCTION(ssh2_sftp_lstat) -{ - php_ssh2_sftp_stat_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, LIBSSH2_SFTP_LSTAT); -} -/* }}} */ - -/* {{{ proto bool ssh2_sftp_symlink(resource sftp, string target, string link) - */ -PHP_FUNCTION(ssh2_sftp_symlink) -{ - php_ssh2_sftp_data *data; - zval *zsftp; - char *targ, *link; - int targ_len, link_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rss", &zsftp, &targ, &targ_len, &link, &link_len) == FAILURE) { - RETURN_FALSE; - } - - ZEND_FETCH_RESOURCE(data, php_ssh2_sftp_data*, &zsftp, -1, PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp); - - RETURN_BOOL(!libssh2_sftp_symlink_ex(data->sftp, targ, targ_len, link, link_len, LIBSSH2_SFTP_SYMLINK)); -} -/* }}} */ - -/* {{{ proto string ssh2_sftp_readlink(resource sftp, string link) - */ -PHP_FUNCTION(ssh2_sftp_readlink) -{ - php_ssh2_sftp_data *data; - zval *zsftp; - char *link; - int targ_len = 0, link_len; - char targ[8192]; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &zsftp, &link, &link_len) == FAILURE) { - RETURN_FALSE; - } - - ZEND_FETCH_RESOURCE(data, php_ssh2_sftp_data*, &zsftp, -1, PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp); - - if ((targ_len = libssh2_sftp_symlink_ex(data->sftp, link, link_len, targ, 8192, LIBSSH2_SFTP_READLINK)) < 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to read link '%s'", link); - RETURN_FALSE; - } - - RETURN_STRINGL(targ, targ_len, 1); -} -/* }}} */ - -/* {{{ proto string ssh2_sftp_realpath(resource sftp, string filename) - */ -PHP_FUNCTION(ssh2_sftp_realpath) -{ - php_ssh2_sftp_data *data; - zval *zsftp; - char *link; - int targ_len = 0, link_len; - char targ[8192]; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &zsftp, &link, &link_len) == FAILURE) { - RETURN_FALSE; - } - - ZEND_FETCH_RESOURCE(data, php_ssh2_sftp_data*, &zsftp, -1, PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp); - - if ((targ_len = libssh2_sftp_symlink_ex(data->sftp, link, link_len, targ, 8192, LIBSSH2_SFTP_REALPATH)) < 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to resolve realpath for '%s'", link); - RETURN_FALSE; - } - - RETURN_STRINGL(targ, targ_len, 1); -} -/* }}} */ - - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * indent-tabs-mode: t - * End: - */ - diff -Nru php-ssh2-0.11.2/ssh2-0.11.3/config.m4 php-ssh2-0.11.3/ssh2-0.11.3/config.m4 --- php-ssh2-0.11.2/ssh2-0.11.3/config.m4 1970-01-01 00:00:00.000000000 +0000 +++ php-ssh2-0.11.3/ssh2-0.11.3/config.m4 2011-09-27 20:05:05.000000000 +0000 @@ -0,0 +1,81 @@ +dnl $Id: config.m4 209383 2006-03-15 02:10:23Z pollita $ +dnl config.m4 for extension ssh2 + +PHP_ARG_WITH(ssh2, for ssh2 support, +[ --with-ssh2=[DIR] Include ssh2 support]) + +if test "$PHP_SSH2" != "no"; then + SEARCH_PATH="/usr/local /usr" + SEARCH_FOR="/include/libssh2.h" + if test -r $PHP_SSH2/$SEARCH_FOR; then # path given as parameter + SSH2_DIR=$PHP_SSH2 + else + AC_MSG_CHECKING([for ssh2 files in default path]) + for i in $SEARCH_PATH ; do + if test -r $i/$SEARCH_FOR; then + SSH2_DIR=$i + AC_MSG_RESULT(found in $i) + fi + done + fi + + if test -z "$SSH2_DIR"; then + AC_MSG_RESULT([not found]) + AC_MSG_ERROR([The required libssh2 library was not found. You can obtain that package from http://sourceforge.net/projects/libssh2/]) + fi + + PHP_ADD_INCLUDE($SSH2_DIR/include) + + LIBNAME=ssh2 + LIBSYMBOL=libssh2_banner_set + + PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, + [ + PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $SSH2_DIR/lib, SSH2_SHARED_LIBADD) + AC_DEFINE(HAVE_SSH2LIB,1,[Have libssh2]) + ],[ + AC_MSG_ERROR([libssh2 version >= 0.4 not found]) + ],[ + -L$SSH2_DIR/lib -lm + ]) + + PHP_CHECK_LIBRARY($LIBNAME,libssh2_channel_forward_listen_ex, + [ + AC_DEFINE(PHP_SSH2_REMOTE_FORWARDING, 1, [Have libssh2 with remote forwarding]) + ],[ + AC_MSG_WARN([libssh2 <= 0.4, remote forwarding not enabled]) + ],[ + -L$SSH2_DIR/lib -lm + ]) + + PHP_CHECK_LIBRARY($LIBNAME,libssh2_userauth_hostbased_fromfile_ex, + [ + AC_DEFINE(PHP_SSH2_HOSTBASED_AUTH, 1, [Have libssh2 with hostbased authentication]) + ],[ + AC_MSG_WARN([libssh2 <= 0.6, hostbased authentication not enabled]) + ],[ + -L$SSH2_DIR/lib -lm + ]) + + PHP_CHECK_LIBRARY($LIBNAME,libssh2_poll, + [ + AC_DEFINE(PHP_SSH2_POLL, 1, [Have libssh2 with poll() support]) + ],[ + AC_MSG_WARN([libssh2 <= 0.7, poll support not enabled]) + ],[ + -L$SSH2_DIR/lib -lm + ]) + + PHP_CHECK_LIBRARY($LIBNAME,libssh2_publickey_init, + [ + AC_DEFINE(PHP_SSH2_PUBLICKEY_SUBSYSTEM, 1, [Have libssh2 with publickey subsystem support]) + ],[ + AC_MSG_WARN([libssh2 <= 0.11, publickey subsystem support not enabled]) + ],[ + -L$SSH2_DIR/lib -lm + ]) + + PHP_SUBST(SSH2_SHARED_LIBADD) + + PHP_NEW_EXTENSION(ssh2, ssh2.c ssh2_fopen_wrappers.c ssh2_sftp.c, $ext_shared) +fi diff -Nru php-ssh2-0.11.2/ssh2-0.11.3/php_ssh2.h php-ssh2-0.11.3/ssh2-0.11.3/php_ssh2.h --- php-ssh2-0.11.2/ssh2-0.11.3/php_ssh2.h 1970-01-01 00:00:00.000000000 +0000 +++ php-ssh2-0.11.3/ssh2-0.11.3/php_ssh2.h 2011-09-27 20:05:05.000000000 +0000 @@ -0,0 +1,222 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Sara Golemon | + +----------------------------------------------------------------------+ + + $Id: php_ssh2.h 306892 2010-12-30 23:59:55Z pajoye $ +*/ + +#ifndef PHP_SSH2_H +#define PHP_SSH2_H + +#include +#include +#include "ext/standard/url.h" + +#define PHP_SSH2_VERSION "0.11.3-dev" +#define PHP_SSH2_DEFAULT_PORT 22 + +/* Exported Constants */ +#define PHP_SSH2_FINGERPRINT_MD5 0x0000 +#define PHP_SSH2_FINGERPRINT_SHA1 0x0001 +#define PHP_SSH2_FINGERPRINT_HEX 0x0000 +#define PHP_SSH2_FINGERPRINT_RAW 0x0002 + +#define PHP_SSH2_TERM_UNIT_CHARS 0x0000 +#define PHP_SSH2_TERM_UNIT_PIXELS 0x0001 + +#define PHP_SSH2_DEFAULT_TERMINAL "vanilla" +#define PHP_SSH2_DEFAULT_TERM_WIDTH 80 +#define PHP_SSH2_DEFAULT_TERM_HEIGHT 25 +#define PHP_SSH2_DEFAULT_TERM_UNIT PHP_SSH2_TERM_UNIT_CHARS + +#define PHP_SSH2_SESSION_RES_NAME "SSH2 Session" +#define PHP_SSH2_CHANNEL_STREAM_NAME "SSH2 Channel" +#define PHP_SSH2_LISTENER_RES_NAME "SSH2 Listener" +#define PHP_SSH2_SFTP_RES_NAME "SSH2 SFTP" +#define PHP_SSH2_PKEY_SUBSYS_RES_NAME "SSH2 Publickey Subsystem" + +#define PHP_SSH2_SFTP_STREAM_NAME "SSH2 SFTP File" +#define PHP_SSH2_SFTP_DIRSTREAM_NAME "SSH2 SFTP Directory" +#define PHP_SSH2_SFTP_WRAPPER_NAME "SSH2 SFTP" + +#define PHP_SSH2_LISTEN_MAX_QUEUED 16 + +#define PHP_SSH2_DEFAULT_POLL_TIMEOUT 30 + +extern zend_module_entry ssh2_module_entry; +#define phpext_ssh2_ptr &ssh2_module_entry + +typedef struct _php_ssh2_session_data { + /* Userspace callback functions */ + zval *ignore_cb; + zval *debug_cb; + zval *macerror_cb; + zval *disconnect_cb; + + int socket; + +#ifdef ZTS + /* Avoid unnecessary TSRMLS_FETCH() calls */ + TSRMLS_D; +#endif +} php_ssh2_session_data; + +typedef struct _php_ssh2_sftp_data { + LIBSSH2_SESSION *session; + LIBSSH2_SFTP *sftp; + + int session_rsrcid; +} php_ssh2_sftp_data; + +#ifdef PHP_SSH2_REMOTE_FORWARDING +typedef struct _php_ssh2_listener_data { + LIBSSH2_SESSION *session; + LIBSSH2_LISTENER *listener; + + int session_rsrcid; +} php_ssh2_listener_data; +#endif + +#ifdef PHP_SSH2_PUBLICKEY_SUBSYSTEM +#include "libssh2_publickey.h" + +typedef struct _php_ssh2_pkey_subsys_data { + LIBSSH2_SESSION *session; + LIBSSH2_PUBLICKEY *pkey; + + int session_rsrcid; +} php_ssh2_pkey_subsys_data; +#endif + +#ifndef PHP_WIN32 +#define closesocket(s) close(s) +#endif + +#ifdef ZTS +#define SSH2_TSRMLS_SET(datap) ((php_ssh2_session_data*)(datap))->tsrm_ls = TSRMLS_C +#define SSH2_TSRMLS_FETCH(datap) TSRMLS_D = ((php_ssh2_session_data*)(datap))->tsrm_ls +#else +#define SSH2_TSRMLS_SET(datap) +#define SSH2_TSRMLS_FETCH(datap) +#endif + +#if (PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION >= 3) +#define ZEND_IS_CALLABLE_TSRMLS_CC TSRMLS_CC +#else +#define ZEND_IS_CALLABLE_TSRMLS_CC +#endif + +/* < 5.3 compatibility */ +#ifndef Z_REFCOUNT_P +#define Z_REFCOUNT_P(pz) (pz)->refcount +#define Z_REFCOUNT_PP(ppz) Z_REFCOUNT_P(*(ppz)) +#endif + +#ifndef Z_SET_REFCOUNT_P +#define Z_SET_REFCOUNT_P(pz, rc) (pz)->refcount = rc +#define Z_SET_REFCOUNT_PP(ppz, rc) Z_SET_REFCOUNT_P(*(ppz), rc) +#endif + +#ifndef Z_ISREF_P +#define Z_ISREF_P(pz) (pz)->is_ref +#define Z_ISREF_PP(ppz) Z_ISREF_P(*(ppz)) +#endif + +#ifndef Z_SET_ISREF_P +#define Z_SET_ISREF_P(pz) (pz)->is_ref = 1 +#define Z_SET_ISREF_PP(ppz) Z_SET_ISREF_P(*(ppz)) +#endif + +#ifndef Z_UNSET_ISREF_P +#define Z_UNSET_ISREF_P(pz) (pz)->is_ref = 0 +#define Z_UNSET_ISREF_PP(ppz) Z_UNSET_ISREF_P(*(ppz)) +#endif + + +typedef struct _php_ssh2_channel_data { + LIBSSH2_CHANNEL *channel; + + /* Distinguish which stream we should read/write from/to */ + unsigned int streamid; + char is_blocking; + + /* Resource ID, zend_list_addref() when opening, zend_list_delete() when closing */ + long session_rsrc; + + /* Allow one stream to be closed while the other is kept open */ + unsigned char *refcount; + +} php_ssh2_channel_data; + +/* In ssh2_fopen_wrappers.c */ +PHP_FUNCTION(ssh2_shell); +PHP_FUNCTION(ssh2_exec); +PHP_FUNCTION(ssh2_tunnel); +PHP_FUNCTION(ssh2_scp_recv); +PHP_FUNCTION(ssh2_scp_send); +PHP_FUNCTION(ssh2_fetch_stream); + +/* In ssh2_sftp.c */ +PHP_FUNCTION(ssh2_sftp); + +PHP_FUNCTION(ssh2_sftp_rename); +PHP_FUNCTION(ssh2_sftp_unlink); +PHP_FUNCTION(ssh2_sftp_mkdir); +PHP_FUNCTION(ssh2_sftp_rmdir); +PHP_FUNCTION(ssh2_sftp_stat); +PHP_FUNCTION(ssh2_sftp_lstat); +PHP_FUNCTION(ssh2_sftp_symlink); +PHP_FUNCTION(ssh2_sftp_readlink); +PHP_FUNCTION(ssh2_sftp_realpath); + +LIBSSH2_SESSION *php_ssh2_session_connect(char *host, int port, zval *methods, zval *callbacks TSRMLS_DC); +void php_ssh2_sftp_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC); +php_url *php_ssh2_fopen_wraper_parse_path( char *path, char *type, php_stream_context *context, + LIBSSH2_SESSION **psession, int *presource_id, + LIBSSH2_SFTP **psftp, int *psftp_rsrcid + TSRMLS_DC); + +extern php_stream_ops php_ssh2_channel_stream_ops; + +extern php_stream_wrapper php_ssh2_stream_wrapper_shell; +extern php_stream_wrapper php_ssh2_stream_wrapper_exec; +extern php_stream_wrapper php_ssh2_stream_wrapper_tunnel; +extern php_stream_wrapper php_ssh2_stream_wrapper_scp; +extern php_stream_wrapper php_ssh2_sftp_wrapper; + +/* Resource list entries */ +extern int le_ssh2_session; +extern int le_ssh2_sftp; + +/* {{{ ZIP_OPENBASEDIR_CHECKPATH(filename) */ +#if PHP_API_VERSION < 20100412 +# define SSH2_OPENBASEDIR_CHECKPATH(filename) \ + (PG(safe_mode) && (!php_checkuid(filename, NULL, CHECKUID_CHECK_FILE_AND_DIR))) || php_check_open_basedir(filename TSRMLS_CC) +#else +#define SSH2_OPENBASEDIR_CHECKPATH(filename) \ + php_check_open_basedir(filename TSRMLS_CC) +#endif +/* }}} */ +#endif /* PHP_SSH2_H */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * indent-tabs-mode: t + * End: + */ diff -Nru php-ssh2-0.11.2/ssh2-0.11.3/ssh2.c php-ssh2-0.11.3/ssh2-0.11.3/ssh2.c --- php-ssh2-0.11.2/ssh2-0.11.3/ssh2.c 1970-01-01 00:00:00.000000000 +0000 +++ php-ssh2-0.11.3/ssh2-0.11.3/ssh2.c 2011-09-27 20:05:05.000000000 +0000 @@ -0,0 +1,1343 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Sara Golemon | + +----------------------------------------------------------------------+ + + $Id: ssh2.c 317115 2011-09-21 17:40:23Z bjori $ +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "ext/standard/info.h" +#include "ext/standard/file.h" +#include "php_ssh2.h" +#include "main/php_network.h" + +#if (OPENSSL_VERSION_NUMBER >= 0x00908000L) +#include +#endif + +/* Internal Constants */ +#ifndef SHA_DIGEST_LENGTH +#define SHA_DIGEST_LENGTH 20 +#endif + +#ifndef MD5_DIGEST_LENGTH +#define MD5_DIGEST_LENGTH 16 +#endif + +/* True global resources - no need for thread safety here */ +int le_ssh2_session; +#ifdef PHP_SSH2_REMOTE_FORWARDING +int le_ssh2_listener; +#endif +int le_ssh2_sftp; +#ifdef PHP_SSH2_PUBLICKEY_SUBSYSTEM +int le_ssh2_pkey_subsys; +#endif + +ZEND_BEGIN_ARG_INFO(php_ssh2_first_arg_force_ref, 0) + ZEND_ARG_PASS_INFO(1) +ZEND_END_ARG_INFO() + +/* ************* + * Callbacks * + ************* */ + +#ifdef ZTS +#define PHP_SSH2_TSRMLS_FETCH() TSRMLS_D = *(void****)abstract; +#else +#define PHP_SSH2_TSRMLS_FETCH() +#endif + +/* {{{ php_ssh2_alloc_cb + * Wrap emalloc() + */ +static LIBSSH2_ALLOC_FUNC(php_ssh2_alloc_cb) +{ + return emalloc(count); +} +/* }}} */ + +/* {{{ php_ssh2_free_cb + * Wrap efree() + */ +static LIBSSH2_FREE_FUNC(php_ssh2_free_cb) +{ + efree(ptr); +} +/* }}} */ + +/* {{{ php_ssh2_realloc_cb + * Wrap erealloc() + */ +static LIBSSH2_REALLOC_FUNC(php_ssh2_realloc_cb) +{ + return erealloc(ptr, count); +} +/* }}} */ + +/* {{{ php_ssh2_debug_cb + * Debug packets + */ +LIBSSH2_DEBUG_FUNC(php_ssh2_debug_cb) +{ + php_ssh2_session_data *data; + zval *zdisplay, *zmessage, *zlanguage; + zval **args[3]; + SSH2_TSRMLS_FETCH(*abstract); + + if (!abstract || !*abstract) { + return; + } + data = (php_ssh2_session_data*)*abstract; + if (!data->debug_cb) { + return; + } + + MAKE_STD_ZVAL(zmessage); + ZVAL_STRINGL(zmessage, (char*)message, message_len, 1); + args[0] = &zmessage; + + MAKE_STD_ZVAL(zlanguage); + ZVAL_STRINGL(zlanguage, (char*)language, language_len, 1); + args[1] = &zlanguage; + + MAKE_STD_ZVAL(zdisplay); + ZVAL_LONG(zdisplay, always_display); + args[2] = &zdisplay; + + if (FAILURE == call_user_function_ex(NULL, NULL, data->disconnect_cb, NULL, 3, args, 0, NULL TSRMLS_CC)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failure calling disconnect callback"); + } + zval_ptr_dtor(&zdisplay); + zval_ptr_dtor(&zmessage); + zval_ptr_dtor(&zlanguage); +} +/* }}} */ + +/* {{{ php_ssh2_ignore_cb + * Ignore packets + */ +LIBSSH2_IGNORE_FUNC(php_ssh2_ignore_cb) +{ + php_ssh2_session_data *data; + zval *zretval = NULL, *zmessage; + zval **args[1]; + SSH2_TSRMLS_FETCH(*abstract); + + if (!abstract || !*abstract) { + return; + } + data = (php_ssh2_session_data*)*abstract; + if (!data->ignore_cb) { + return; + } + + MAKE_STD_ZVAL(zmessage); + ZVAL_STRINGL(zmessage, (char*)message, message_len, 1); + args[0] = &zmessage; + + if (FAILURE == call_user_function_ex(NULL, NULL, data->ignore_cb, &zretval, 1, args, 0, NULL TSRMLS_CC)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failure calling ignore callback"); + } + zval_ptr_dtor(&zmessage); + if (zretval) { + zval_ptr_dtor(&zretval); + } +} +/* }}} */ + +/* {{{ php_ssh2_macerror_cb + * Called when a MAC error occurs, offers the chance to ignore + * WHY ARE YOU IGNORING MAC ERRORS?????? + */ +LIBSSH2_MACERROR_FUNC(php_ssh2_macerror_cb) +{ + php_ssh2_session_data *data; + zval *zretval = NULL, *zpacket; + zval **args[1]; + int retval = -1; + SSH2_TSRMLS_FETCH(*abstract); + + if (!abstract || !*abstract) { + return -1; + } + data = (php_ssh2_session_data*)*abstract; + if (!data->macerror_cb) { + return -1; + } + + MAKE_STD_ZVAL(zpacket); + ZVAL_STRINGL(zpacket, (char*)packet, packet_len, 1); + args[0] = &zpacket; + + if (FAILURE == call_user_function_ex(NULL, NULL, data->macerror_cb, &zretval, 1, args, 0, NULL TSRMLS_CC)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failure calling macerror callback"); + } else { + retval = zval_is_true(zretval) ? 0 : -1; + } + zval_ptr_dtor(&zpacket); + if (zretval) { + zval_ptr_dtor(&zretval); + } + + return retval; +} +/* }}} */ + +/* {{{ php_ssh2_disconnect_cb + * Connection closed by foreign host + */ +LIBSSH2_DISCONNECT_FUNC(php_ssh2_disconnect_cb) +{ + php_ssh2_session_data *data; + zval *zreason, *zmessage, *zlanguage; + zval **args[3]; + SSH2_TSRMLS_FETCH(*abstract); + + if (!abstract || !*abstract) { + return; + } + data = (php_ssh2_session_data*)*abstract; + if (!data->disconnect_cb) { + return; + } + + MAKE_STD_ZVAL(zreason); + ZVAL_LONG(zreason, reason); + args[0] = &zreason; + + MAKE_STD_ZVAL(zmessage); + ZVAL_STRINGL(zmessage, (char*)message, message_len, 1); + args[1] = &zmessage; + + MAKE_STD_ZVAL(zlanguage); + ZVAL_STRINGL(zlanguage, (char*)language, language_len, 1); + args[2] = &zlanguage; + + if (FAILURE == call_user_function_ex(NULL, NULL, data->disconnect_cb, NULL, 3, args, 0, NULL TSRMLS_CC)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failure calling disconnect callback"); + } + zval_ptr_dtor(&zreason); + zval_ptr_dtor(&zmessage); + zval_ptr_dtor(&zlanguage); +} +/* }}} */ + +/* ***************** + * Userspace API * + ***************** */ + +/* {{{ php_ssh2_set_callback + * Try to set a method if it's passed in with the hash table + */ +static int php_ssh2_set_callback(LIBSSH2_SESSION *session, HashTable *ht, char *callback, int callback_len, int callback_type, php_ssh2_session_data *data TSRMLS_DC) +{ + zval **handler, *copyval; + void *internal_handler; + + if (zend_hash_find(ht, callback, callback_len + 1, (void**)&handler) == FAILURE) { + return 0; + } + + if (!handler || !*handler || !zend_is_callable(*handler, 0, NULL ZEND_IS_CALLABLE_TSRMLS_CC)) { + return -1; + } + + ALLOC_INIT_ZVAL(copyval); + *copyval = **handler; + zval_copy_ctor(copyval); + + switch (callback_type) { + case LIBSSH2_CALLBACK_IGNORE: + internal_handler = php_ssh2_ignore_cb; + if (data->ignore_cb) { + zval_ptr_dtor(&data->ignore_cb); + } + data->ignore_cb = copyval; + break; + case LIBSSH2_CALLBACK_DEBUG: + internal_handler = php_ssh2_debug_cb; + if (data->debug_cb) { + zval_ptr_dtor(&data->debug_cb); + } + data->debug_cb = copyval; + break; + case LIBSSH2_CALLBACK_MACERROR: + internal_handler = php_ssh2_macerror_cb; + if (data->macerror_cb) { + zval_ptr_dtor(&data->macerror_cb); + } + data->macerror_cb = copyval; + break; + case LIBSSH2_CALLBACK_DISCONNECT: + internal_handler = php_ssh2_disconnect_cb; + if (data->disconnect_cb) { + zval_ptr_dtor(&data->disconnect_cb); + } + data->disconnect_cb = copyval; + break; + default: + zval_ptr_dtor(©val); + return -1; + } + + libssh2_session_callback_set(session, callback_type, internal_handler); + + return 0; +} +/* }}} */ + +/* {{{ php_ssh2_set_method + * Try to set a method if it's passed in with the hash table + */ +static int php_ssh2_set_method(LIBSSH2_SESSION *session, HashTable *ht, char *method, int method_len, int method_type) +{ + zval **value; + + if (zend_hash_find(ht, method, method_len + 1, (void**)&value) == FAILURE) { + return 0; + } + + if (!value || !*value || (Z_TYPE_PP(value) != IS_STRING)) { + return -1; + } + + return libssh2_session_method_pref(session, method_type, Z_STRVAL_PP(value)); +} +/* }}} */ + +/* {{{ php_ssh2_session_connect + * Connect to an SSH server with requested methods + */ +LIBSSH2_SESSION *php_ssh2_session_connect(char *host, int port, zval *methods, zval *callbacks TSRMLS_DC) +{ + LIBSSH2_SESSION *session; + int socket; + php_ssh2_session_data *data; + struct timeval tv; + + tv.tv_sec = FG(default_socket_timeout); + tv.tv_usec = 0; + +#if PHP_MAJOR_VERSION > 5 || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 0) + socket = php_network_connect_socket_to_host(host, port, SOCK_STREAM, 0, &tv, NULL, NULL, NULL, 0 TSRMLS_CC); +#elif PHP_MAJOR_VERSION == 5 + socket = php_network_connect_socket_to_host(host, port, SOCK_STREAM, 0, &tv, NULL, NULL TSRMLS_CC); +#else + socket = php_hostconnect(host, port, SOCK_STREAM, &tv TSRMLS_CC); +#endif + + if (socket <= 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to connect to %s on port %d", host, port); + return NULL; + } + + data = ecalloc(1, sizeof(php_ssh2_session_data)); + SSH2_TSRMLS_SET(data); + data->socket = socket; + + session = libssh2_session_init_ex(php_ssh2_alloc_cb, php_ssh2_free_cb, php_ssh2_realloc_cb, data); + if (!session) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to initialize SSH2 session"); + efree(data); + closesocket(socket); + return NULL; + } + libssh2_banner_set(session, LIBSSH2_SSH_DEFAULT_BANNER " PHP"); + + /* Override method preferences */ + if (methods) { + zval **container; + + if (php_ssh2_set_method(session, HASH_OF(methods), "kex", sizeof("kex") - 1, LIBSSH2_METHOD_KEX)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed overriding KEX method"); + } + if (php_ssh2_set_method(session, HASH_OF(methods), "hostkey", sizeof("hostkey") - 1, LIBSSH2_METHOD_HOSTKEY)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed overriding HOSTKEY method"); + } + + if (zend_hash_find(HASH_OF(methods), "client_to_server", sizeof("client_to_server"), (void**)&container) == SUCCESS && + container && *container && Z_TYPE_PP(container) == IS_ARRAY) { + if (php_ssh2_set_method(session, HASH_OF(*container), "crypt", sizeof("crypt") - 1, LIBSSH2_METHOD_CRYPT_CS)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed overriding client to server CRYPT method"); + } + if (php_ssh2_set_method(session, HASH_OF(*container), "mac", sizeof("mac") - 1, LIBSSH2_METHOD_MAC_CS)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed overriding client to server MAC method"); + } + if (php_ssh2_set_method(session, HASH_OF(*container), "comp", sizeof("comp") - 1, LIBSSH2_METHOD_COMP_CS)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed overriding client to server COMP method"); + } + if (php_ssh2_set_method(session, HASH_OF(*container), "lang", sizeof("lang") - 1, LIBSSH2_METHOD_LANG_CS)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed overriding client to server LANG method"); + } + } + + if (zend_hash_find(HASH_OF(methods), "server_to_client", sizeof("server_to_client"), (void**)&container) == SUCCESS && + container && *container && Z_TYPE_PP(container) == IS_ARRAY) { + if (php_ssh2_set_method(session, HASH_OF(*container), "crypt", sizeof("crypt") - 1, LIBSSH2_METHOD_CRYPT_SC)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed overriding server to client CRYPT method"); + } + if (php_ssh2_set_method(session, HASH_OF(*container), "mac", sizeof("mac") - 1, LIBSSH2_METHOD_MAC_SC)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed overriding server to client MAC method"); + } + if (php_ssh2_set_method(session, HASH_OF(*container), "comp", sizeof("comp") - 1, LIBSSH2_METHOD_COMP_SC)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed overriding server to client COMP method"); + } + if (php_ssh2_set_method(session, HASH_OF(*container), "lang", sizeof("lang") - 1, LIBSSH2_METHOD_LANG_SC)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed overriding server to client LANG method"); + } + } + } + + /* Register Callbacks */ + if (callbacks) { + /* ignore debug disconnect macerror */ + + if (php_ssh2_set_callback(session, HASH_OF(callbacks), "ignore", sizeof("ignore") - 1, LIBSSH2_CALLBACK_IGNORE, data TSRMLS_CC)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed setting IGNORE callback"); + } + + if (php_ssh2_set_callback(session, HASH_OF(callbacks), "debug", sizeof("debug") - 1, LIBSSH2_CALLBACK_DEBUG, data TSRMLS_CC)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed setting DEBUG callback"); + } + + if (php_ssh2_set_callback(session, HASH_OF(callbacks), "macerror", sizeof("macerror") - 1, LIBSSH2_CALLBACK_MACERROR, data TSRMLS_CC)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed setting MACERROR callback"); + } + + if (php_ssh2_set_callback(session, HASH_OF(callbacks), "disconnect", sizeof("disconnect") - 1, LIBSSH2_CALLBACK_DISCONNECT, data TSRMLS_CC)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed setting DISCONNECT callback"); + } + } + + if (libssh2_session_startup(session, socket)) { + int last_error = 0; + char *error_msg = NULL; + + last_error = libssh2_session_last_error(session, &error_msg, NULL, 0); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error starting up SSH connection(%d): %s", last_error, error_msg); + closesocket(socket); + libssh2_session_free(session); + efree(data); + return NULL; + } + + return session; +} +/* }}} */ + +/* {{{ proto resource ssh2_connect(string host[, int port[, array methods[, array callbacks]]]) + * Establish a connection to a remote SSH server and return a resource on success, false on error + */ +PHP_FUNCTION(ssh2_connect) +{ + LIBSSH2_SESSION *session; + zval *methods = NULL, *callbacks = NULL; + char *host; + long port = PHP_SSH2_DEFAULT_PORT; + int host_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|la!a!", &host, &host_len, &port, &methods, &callbacks) == FAILURE) { + RETURN_FALSE; + } + + session = php_ssh2_session_connect(host, port, methods, callbacks TSRMLS_CC); + if (!session) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to connect to %s", host); + RETURN_FALSE; + } + + ZEND_REGISTER_RESOURCE(return_value, session, le_ssh2_session); +} +/* }}} */ + +/* {{{ proto array ssh2_methods_negotiated(resource session) + * Return list of negotiaed methods + */ +PHP_FUNCTION(ssh2_methods_negotiated) +{ + LIBSSH2_SESSION *session; + zval *zsession, *endpoint; + char *kex, *hostkey, *crypt_cs, *crypt_sc, *mac_cs, *mac_sc, *comp_cs, *comp_sc, *lang_cs, *lang_sc; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zsession) == FAILURE) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); + +#if defined(LIBSSH2_APINO) && LIBSSH2_APINO < 200412301450 + libssh2_session_methods(session, &kex, &hostkey, &crypt_cs, &crypt_sc, &mac_cs, &mac_sc, &comp_cs, &comp_sc, &lang_cs, &lang_sc); +#else + kex = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_KEX); + hostkey = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_HOSTKEY); + crypt_cs = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_CRYPT_CS); + crypt_sc = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_CRYPT_SC); + mac_cs = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_MAC_CS); + mac_sc = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_MAC_SC); + comp_cs = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_COMP_CS); + comp_sc = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_COMP_SC); + lang_cs = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_LANG_CS); + lang_sc = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_LANG_SC); +#endif + + array_init(return_value); + add_assoc_string(return_value, "kex", kex, 1); + add_assoc_string(return_value, "hostkey", hostkey, 1); + + ALLOC_INIT_ZVAL(endpoint); + array_init(endpoint); + add_assoc_string(endpoint, "crypt", crypt_cs, 1); + add_assoc_string(endpoint, "mac", mac_cs, 1); + add_assoc_string(endpoint, "comp", comp_cs, 1); + add_assoc_string(endpoint, "lang", lang_cs, 1); + add_assoc_zval(return_value, "client_to_server", endpoint); + + ALLOC_INIT_ZVAL(endpoint); + array_init(endpoint); + add_assoc_string(endpoint, "crypt", crypt_sc, 1); + add_assoc_string(endpoint, "mac", mac_sc, 1); + add_assoc_string(endpoint, "comp", comp_sc, 1); + add_assoc_string(endpoint, "lang", lang_sc, 1); + add_assoc_zval(return_value, "server_to_client", endpoint); +} +/* }}} */ + +/* {{{ proto string ssh2_fingerprint(resource session[, int flags]) + * Returns a server hostkey hash from an active session + * Defaults to MD5 fingerprint encoded as ASCII hex values + */ +PHP_FUNCTION(ssh2_fingerprint) +{ + LIBSSH2_SESSION *session; + zval *zsession; + const char *fingerprint; + long flags = 0; + int i, fingerprint_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|l", &zsession, &flags) == FAILURE) { + RETURN_FALSE; + } + fingerprint_len = (flags & PHP_SSH2_FINGERPRINT_SHA1) ? SHA_DIGEST_LENGTH : MD5_DIGEST_LENGTH; + + ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); + + fingerprint = (char*)libssh2_hostkey_hash(session, (flags & PHP_SSH2_FINGERPRINT_SHA1) ? LIBSSH2_HOSTKEY_HASH_SHA1 : LIBSSH2_HOSTKEY_HASH_MD5); + if (!fingerprint) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to retreive fingerprint from specified session"); + RETURN_FALSE; + } + + for(i = 0; i < fingerprint_len; i++) { + if (fingerprint[i] != '\0') { + goto fingerprint_good; + } + } + php_error_docref(NULL TSRMLS_CC, E_WARNING, "No fingerprint available using specified hash"); + RETURN_NULL(); + fingerprint_good: + if (flags & PHP_SSH2_FINGERPRINT_RAW) { + RETURN_STRINGL(fingerprint, fingerprint_len, 1); + } else { + char *hexchars; + + hexchars = emalloc((fingerprint_len * 2) + 1); + for(i = 0; i < fingerprint_len; i++) { + snprintf(hexchars + (2 * i), 3, "%02X", (unsigned char)fingerprint[i]); + } + RETURN_STRINGL(hexchars, 2 * fingerprint_len, 0); + } +} +/* }}} */ + +/* {{{ proto array ssh2_auth_none(resource session, string username) + * Attempt "none" authentication, returns a list of allowed methods on failed authentication, + * false on utter failure, or true on success + */ +PHP_FUNCTION(ssh2_auth_none) +{ + LIBSSH2_SESSION *session; + zval *zsession; + char *username, *methods, *s, *p; + int username_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &zsession, &username, &username_len) == FAILURE) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); + + s = methods = libssh2_userauth_list(session, username, username_len); + if (!methods) { + /* Either bad failure, or unexpected success */ + RETURN_BOOL(libssh2_userauth_authenticated(session)); + } + + array_init(return_value); + while ((p = strchr(s, ','))) { + if ((p - s) > 0) { + add_next_index_stringl(return_value, s, p - s, 1); + } + s = p + 1; + } + if (strlen(s)) { + add_next_index_string(return_value, s, 1); + } +} +/* }}} */ + +/* {{{ proto bool ssh2_auth_password(resource session, string username, string password) + * Authenticate over SSH using a plain password + */ +PHP_FUNCTION(ssh2_auth_password) +{ + LIBSSH2_SESSION *session; + zval *zsession; + char *username, *password; + int username_len, password_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rss", &zsession, &username, &username_len, &password, &password_len) == FAILURE) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); + + /* TODO: Support password change callback */ + if (libssh2_userauth_password_ex(session, username, username_len, password, password_len, NULL)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Authentication failed for %s using password", username); + RETURN_FALSE; + } + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool ssh2_auth_pubkey_file(resource session, string username, string pubkeyfile, string privkeyfile[, string passphrase]) + * Authenticate using a public key + */ +PHP_FUNCTION(ssh2_auth_pubkey_file) +{ + LIBSSH2_SESSION *session; + zval *zsession; + char *username, *pubkey, *privkey, *passphrase = NULL; + int username_len, pubkey_len, privkey_len, passphrase_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsss|s", &zsession, &username, &username_len, + &pubkey, &pubkey_len, + &privkey, &privkey_len, + &passphrase, &passphrase_len) == FAILURE) { + RETURN_FALSE; + } + + if (SSH2_OPENBASEDIR_CHECKPATH(pubkey) || SSH2_OPENBASEDIR_CHECKPATH(privkey)) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); + + /* TODO: Support passphrase callback */ + if (libssh2_userauth_publickey_fromfile_ex(session, username, username_len, pubkey, privkey, passphrase)) { + char *buf; + int len; + libssh2_session_last_error(session, &buf, &len, 0); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Authentication failed for %s using public key: %s", username, buf); + RETURN_FALSE; + } + + RETURN_TRUE; +} +/* }}} */ + +#ifdef PHP_SSH2_HOSTBASED_AUTH +/* {{{ proto bool ssh2_auth_hostbased_file(resource session, string username, string local_hostname, string pubkeyfile, string privkeyfile[, string passphrase[, string local_username]]) + * Authenticate using a hostkey + */ +PHP_FUNCTION(ssh2_auth_hostbased_file) +{ + LIBSSH2_SESSION *session; + zval *zsession; + char *username, *hostname, *pubkey, *privkey, *passphrase = NULL, *local_username = NULL; + int username_len, hostname_len, pubkey_len, privkey_len, passphrase_len, local_username_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rssss|s!s!", &zsession, &username, &username_len, + &hostname, &hostname_len, + &pubkey, &pubkey_len, + &privkey, &privkey_len, + &passphrase, &passphrase_len, + &local_username, &local_username_len) == FAILURE) { + RETURN_FALSE; + } + + if (SSH2_OPENBASEDIR_CHECKPATH(pubkey) || SSH2_OPENBASEDIR_CHECKPATH(privkey)) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); + + if (!local_username) { + local_username = username; + local_username_len = username_len; + } + + /* TODO: Support passphrase callback */ + if (libssh2_userauth_hostbased_fromfile_ex(session, username, username_len, pubkey, privkey, passphrase, hostname, hostname_len, local_username, local_username_len)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Authentication failed for %s using hostbased public key", username); + RETURN_FALSE; + } + + RETURN_TRUE; +} +/* }}} */ +#endif /* PHP_SSH2_HOSTBASED_AUTH */ + +#ifdef PHP_SSH2_REMOTE_FORWARDING +/* {{{ proto resource ssh2_forward_listen(resource session, int port[, string host[, long max_connections]]) + * Bind a port on the remote server and listen for connections + */ +PHP_FUNCTION(ssh2_forward_listen) +{ + zval *zsession; + LIBSSH2_SESSION *session; + LIBSSH2_LISTENER *listener; + php_ssh2_listener_data *data; + long port; + char *host = NULL; + int host_len; + long max_connections = PHP_SSH2_LISTEN_MAX_QUEUED; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl|sl", &zsession, &port, &host, &host_len, &max_connections) == FAILURE) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); + + listener = libssh2_channel_forward_listen_ex(session, host, port, NULL, max_connections); + + if (!listener) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failure listening on remote port"); + RETURN_FALSE; + } + + data = emalloc(sizeof(php_ssh2_listener_data)); + data->session = session; + data->session_rsrcid = Z_LVAL_P(zsession); + zend_list_addref(data->session_rsrcid); + data->listener = listener; + + ZEND_REGISTER_RESOURCE(return_value, data, le_ssh2_listener); +} +/* }}} */ + +/* {{{ proto stream ssh2_forward_accept(resource listener[, string &shost[, long &sport]]) + * Accept a connection created by a listener + */ +PHP_FUNCTION(ssh2_forward_accept) +{ + zval *zlistener; + php_ssh2_listener_data *data; + LIBSSH2_CHANNEL *channel; + php_ssh2_channel_data *channel_data; + php_stream *stream; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zlistener) == FAILURE) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(data, php_ssh2_listener_data*, &zlistener, -1, PHP_SSH2_LISTENER_RES_NAME, le_ssh2_listener); + + channel = libssh2_channel_forward_accept(data->listener); + + if (!channel) { + RETURN_FALSE; + } + + channel_data = emalloc(sizeof(php_ssh2_channel_data)); + channel_data->channel = channel; + channel_data->streamid = 0; + channel_data->is_blocking = 0; + channel_data->session_rsrc = data->session_rsrcid; + channel_data->refcount = NULL; + + stream = php_stream_alloc(&php_ssh2_channel_stream_ops, channel_data, 0, "r+"); + if (!stream) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failure allocating stream"); + efree(channel_data); + libssh2_channel_free(channel); + RETURN_FALSE; + } + zend_list_addref(channel_data->session_rsrc); + + php_stream_to_zval(stream, return_value); +} +/* }}} */ +#endif /* PHP_SSH2_REMOTE_FORWARDING */ + +#ifdef PHP_SSH2_POLL +/* {{{ proto int ssh2_poll(array &polldes[, int timeout]) + * Poll the channels/listeners/streams for events + * Returns number of descriptors which returned non-zero revents + * Input array should be of the form: + * array( + * 0 => array( + * [resource] => $channel,$listener, or $stream + * [events] => SSH2_POLL* flags bitwise ORed together + * ), + * 1 => ... + * ) + * Each subarray will be populated with an revents element on return + */ +PHP_FUNCTION(ssh2_poll) +{ + zval *zdesc, **subarray; + long timeout = PHP_SSH2_DEFAULT_POLL_TIMEOUT; + LIBSSH2_POLLFD *pollfds; + int numfds, i = 0, fds_ready; + int le_stream = php_file_le_stream(); + int le_pstream = php_file_le_pstream(); + zval ***pollmap; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &zdesc, &timeout) == FAILURE) { + RETURN_NULL(); + } + + numfds = zend_hash_num_elements(Z_ARRVAL_P(zdesc)); + pollfds = safe_emalloc(sizeof(LIBSSH2_POLLFD), numfds, 0); + pollmap = safe_emalloc(sizeof(zval**), numfds, 0); + + for(zend_hash_internal_pointer_reset(Z_ARRVAL_P(zdesc)); + zend_hash_get_current_data(Z_ARRVAL_P(zdesc), (void**)&subarray) == SUCCESS; + zend_hash_move_forward(Z_ARRVAL_P(zdesc))) { + zval **tmpzval; + int res_type = 0; + void *res; + + if (Z_TYPE_PP(subarray) != IS_ARRAY) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid element in poll array, not a sub array"); + numfds--; + continue; + } + if (zend_hash_find(Z_ARRVAL_PP(subarray), "events", sizeof("events"), (void**)&tmpzval) == FAILURE || + Z_TYPE_PP(tmpzval) != IS_LONG) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid data in subarray, no events element, or not a bitmask"); + numfds--; + continue; + } + pollfds[i].events = Z_LVAL_PP(tmpzval); + if (zend_hash_find(Z_ARRVAL_PP(subarray), "resource", sizeof("resource"), (void**)&tmpzval) == FAILURE || + Z_TYPE_PP(tmpzval) != IS_RESOURCE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid data in subarray, no resource element, or not of type resource"); + numfds--; + continue; + } + zend_list_find(Z_LVAL_PP(tmpzval), &res_type); + res = zend_fetch_resource(tmpzval TSRMLS_CC, -1, "Poll Resource", NULL, 1, res_type); + if (res_type == le_ssh2_listener) { + pollfds[i].type = LIBSSH2_POLLFD_LISTENER; + pollfds[i].fd.listener = ((php_ssh2_listener_data*)res)->listener; + } else if ((res_type == le_stream || res_type == le_pstream) && + ((php_stream*)res)->ops == &php_ssh2_channel_stream_ops) { + pollfds[i].type = LIBSSH2_POLLFD_CHANNEL; + pollfds[i].fd.channel = ((php_ssh2_channel_data*)(((php_stream*)res)->abstract))->channel; + /* TODO: Add the ability to select against other stream types */ + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid resource type in subarray: %s", zend_rsrc_list_get_rsrc_type(Z_LVAL_PP(tmpzval) TSRMLS_CC)); + numfds--; + continue; + } + pollmap[i] = subarray; + i++; + } + + fds_ready = libssh2_poll(pollfds, numfds, timeout * 1000); + + for(i = 0; i < numfds; i++) { + zval *subarray = *pollmap[i]; + + if (!Z_ISREF_P(subarray) && Z_REFCOUNT_P(subarray) > 1) { + /* Make a new copy of the subarray zval* */ + MAKE_STD_ZVAL(subarray); + *subarray = **pollmap[i]; + + /* Point the pData to the new zval* and duplicate its resources */ + *pollmap[i] = subarray; + zval_copy_ctor(subarray); + + /* Fixup its refcount */ + Z_UNSET_ISREF_P(subarray); + Z_SET_REFCOUNT_P(subarray, 1); + } + zend_hash_del(Z_ARRVAL_P(subarray), "revents", sizeof("revents")); + add_assoc_long(subarray, "revents", pollfds[i].revents); + + } + efree(pollmap); + efree(pollfds); + + RETURN_LONG(fds_ready); +} +/* }}} */ +#endif /* PHP_SSH2_POLL */ + +#ifdef PHP_SSH2_PUBLICKEY_SUBSYSTEM +/* *********************** + * Publickey Subsystem * + *********************** */ + +/* {{{ proto resource ssh2_publickey_init(resource connection) +Initialize the publickey subsystem */ +PHP_FUNCTION(ssh2_publickey_init) +{ + zval *zsession; + LIBSSH2_SESSION *session; + LIBSSH2_PUBLICKEY *pkey; + php_ssh2_pkey_subsys_data *data; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zsession) == FAILURE) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); + + pkey = libssh2_publickey_init(session); + + if (!pkey) { + int last_error = 0; + char *error_msg = NULL; + + last_error = libssh2_session_last_error(session, &error_msg, NULL, 0); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to initialize publickey subsystem(%d) %s", last_error, error_msg); + RETURN_FALSE; + } + + data = emalloc(sizeof(php_ssh2_pkey_subsys_data)); + data->session = session; + data->session_rsrcid = Z_LVAL_P(zsession); + zend_list_addref(data->session_rsrcid); + data->pkey = pkey; + + ZEND_REGISTER_RESOURCE(return_value, data, le_ssh2_pkey_subsys); +} +/* }}} */ + +/* {{{ proto bool ssh2_publickey_add(resource pkey, string algoname, string blob[, bool overwrite=FALSE [,array attributes=NULL]]) +Add an additional publickey */ +PHP_FUNCTION(ssh2_publickey_add) +{ + zval *zpkey_data, *zattrs = NULL; + php_ssh2_pkey_subsys_data *data; + char *algo, *blob; + int algo_len, blob_len; + unsigned long num_attrs = 0; + libssh2_publickey_attribute *attrs = NULL; + zend_bool overwrite = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rss|ba", &zpkey_data, &algo, &algo_len, &blob, &blob_len, &overwrite, &zattrs) == FAILURE) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(data, php_ssh2_pkey_subsys_data*, &zpkey_data, -1, PHP_SSH2_PKEY_SUBSYS_RES_NAME, le_ssh2_pkey_subsys); + + if (zattrs) { + HashPosition pos; + zval **attr_val; + unsigned long current_attr = 0; + + num_attrs = zend_hash_num_elements(Z_ARRVAL_P(zattrs)); + attrs = safe_emalloc(num_attrs, sizeof(libssh2_publickey_attribute), 0); + + for(zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(zattrs), &pos); + zend_hash_get_current_data_ex(Z_ARRVAL_P(zattrs), (void**)&attr_val, &pos) == SUCCESS; + zend_hash_move_forward_ex(Z_ARRVAL_P(zattrs), &pos)) { + char *key; + int key_len, type; + long idx; + zval copyval = **attr_val; + + type = zend_hash_get_current_key_ex(Z_ARRVAL_P(zattrs), &key, &key_len, &idx, 0, &pos); + if (type == HASH_KEY_NON_EXISTANT) { + /* All but impossible */ + break; + } + if (type == HASH_KEY_IS_LONG) { + /* Malformed, ignore */ + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Malformed attirbute array, contains numeric index"); + num_attrs--; + continue; + } + + if (key_len == 0 || (key_len == 1 && *key == '*')) { + /* Empty key, ignore */ + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty attribute key"); + num_attrs--; + continue; + } + + zval_copy_ctor(©val); + Z_UNSET_ISREF_P(©val); + Z_SET_REFCOUNT_P(©val, 1); + convert_to_string(©val); + + if (*key == '*') { + attrs[current_attr].mandatory = 1; + attrs[current_attr].name = key + 1; + attrs[current_attr].name_len = key_len - 2; + } else { + attrs[current_attr].mandatory = 0; + attrs[current_attr].name = key; + attrs[current_attr].name_len = key_len - 1; + } + attrs[current_attr].value_len = Z_STRLEN(copyval); + attrs[current_attr].value = Z_STRVAL(copyval); + + /* copyval deliberately not dtor'd, we're stealing the string */ + current_attr++; + } + } + + if (libssh2_publickey_add_ex(data->pkey, algo, algo_len, blob, blob_len, overwrite, num_attrs, attrs)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to add %s key", algo); + RETVAL_FALSE; + } else { + RETVAL_TRUE; + } + + if (attrs) { + unsigned long i; + + for(i = 0; i < num_attrs; i++) { + /* name doesn't need freeing */ + efree(attrs[i].value); + } + efree(attrs); + } +} +/* }}} */ + +/* {{{ proto bool ssh2_publickey_remove(resource pkey, string algoname, string blob) +Remove a publickey entry */ +PHP_FUNCTION(ssh2_publickey_remove) +{ + zval *zpkey_data; + php_ssh2_pkey_subsys_data *data; + char *algo, *blob; + int algo_len, blob_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rss", &zpkey_data, &algo, &algo_len, &blob, &blob_len) == FAILURE) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(data, php_ssh2_pkey_subsys_data*, &zpkey_data, -1, PHP_SSH2_PKEY_SUBSYS_RES_NAME, le_ssh2_pkey_subsys); + + if (libssh2_publickey_remove_ex(data->pkey, algo, algo_len, blob, blob_len)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to remove %s key", algo); + RETURN_FALSE; + } + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto array ssh2_publickey_list(resource pkey) +List currently installed publickey entries */ +PHP_FUNCTION(ssh2_publickey_list) +{ + zval *zpkey_data; + php_ssh2_pkey_subsys_data *data; + unsigned long num_keys, i; + libssh2_publickey_list *keys; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zpkey_data) == FAILURE) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(data, php_ssh2_pkey_subsys_data*, &zpkey_data, -1, PHP_SSH2_PKEY_SUBSYS_RES_NAME, le_ssh2_pkey_subsys); + + if (libssh2_publickey_list_fetch(data->pkey, &num_keys, &keys)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to list keys on remote server"); + RETURN_FALSE; + } + + array_init(return_value); + for(i = 0; i < num_keys; i++) { + zval *key, *attrs; + unsigned long j; + + MAKE_STD_ZVAL(key); + array_init(key); + + add_assoc_stringl(key, "name", keys[i].name, keys[i].name_len, 1); + add_assoc_stringl(key, "blob", keys[i].blob, keys[i].blob_len, 1); + + MAKE_STD_ZVAL(attrs); + array_init(attrs); + for(j = 0; j < keys[i].num_attrs; j++) { + zval *attr; + + MAKE_STD_ZVAL(attr); + ZVAL_STRINGL(attr, keys[i].attrs[j].value, keys[i].attrs[j].value_len, 1); + zend_hash_add(Z_ARRVAL_P(attrs), keys[i].attrs[j].name, keys[i].attrs[j].name_len + 1, (void**)&attr, sizeof(zval*), NULL); + } + add_assoc_zval(key, "attrs", attrs); + + add_next_index_zval(return_value, key); + } + + libssh2_publickey_list_free(data->pkey, keys); +} +/* }}} */ +#endif /* PHP_SSH2_PUBLICKEY_SUBSYSTEM */ + +/* *********************** + * Module Housekeeping * + *********************** */ + +static void php_ssh2_session_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) +{ + LIBSSH2_SESSION *session = (LIBSSH2_SESSION*)rsrc->ptr; + php_ssh2_session_data **data = (php_ssh2_session_data**)libssh2_session_abstract(session); + + libssh2_session_disconnect(session, "PECL/ssh2 (http://pecl.php.net/packages/ssh2)"); + + if (*data) { + if ((*data)->ignore_cb) { + zval_ptr_dtor(&(*data)->ignore_cb); + } + if ((*data)->debug_cb) { + zval_ptr_dtor(&(*data)->debug_cb); + } + if ((*data)->macerror_cb) { + zval_ptr_dtor(&(*data)->macerror_cb); + } + if ((*data)->disconnect_cb) { + zval_ptr_dtor(&(*data)->disconnect_cb); + } + + closesocket((*data)->socket); + + efree(*data); + *data = NULL; + } + + libssh2_session_free(session); +} + +#ifdef PHP_SSH2_REMOTE_FORWARDING +static void php_ssh2_listener_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) +{ + php_ssh2_listener_data *data = (php_ssh2_listener_data*)rsrc->ptr; + LIBSSH2_LISTENER *listener = data->listener; + + libssh2_channel_forward_cancel(listener); + zend_list_delete(data->session_rsrcid); + efree(data); +} +#endif /* PHP_SSH2_REMOTE_FORWARDING */ + +#ifdef PHP_SSH2_PUBLICKEY_SUBSYSTEM +static void php_ssh2_pkey_subsys_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) +{ + php_ssh2_pkey_subsys_data *data = (php_ssh2_pkey_subsys_data*)rsrc->ptr; + LIBSSH2_PUBLICKEY *pkey = data->pkey; + + libssh2_publickey_shutdown(pkey); + zend_list_delete(data->session_rsrcid); + efree(data); +} +#endif /* PHP_SSH2_PUBLICKEY_SUBSYSTEM */ + +/* {{{ PHP_MINIT_FUNCTION + */ +PHP_MINIT_FUNCTION(ssh2) +{ + le_ssh2_session = zend_register_list_destructors_ex(php_ssh2_session_dtor, NULL, PHP_SSH2_SESSION_RES_NAME, module_number); +#ifdef PHP_SSH2_REMOTE_FORWARDING + le_ssh2_listener = zend_register_list_destructors_ex(php_ssh2_listener_dtor, NULL, PHP_SSH2_LISTENER_RES_NAME, module_number); +#endif /* PHP_SSH2_REMOTE_FORWARDING */ + le_ssh2_sftp = zend_register_list_destructors_ex(php_ssh2_sftp_dtor, NULL, PHP_SSH2_SFTP_RES_NAME, module_number); +#ifdef PHP_SSH2_PUBLICKEY_SUBSYSTEM + le_ssh2_pkey_subsys = zend_register_list_destructors_ex(php_ssh2_pkey_subsys_dtor, NULL, PHP_SSH2_PKEY_SUBSYS_RES_NAME, module_number); +#endif /* PHP_SSH2_PUBLICKEY_SUBSYSTEM */ + + REGISTER_LONG_CONSTANT("SSH2_FINGERPRINT_MD5", PHP_SSH2_FINGERPRINT_MD5, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SSH2_FINGERPRINT_SHA1", PHP_SSH2_FINGERPRINT_SHA1, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SSH2_FINGERPRINT_HEX", PHP_SSH2_FINGERPRINT_HEX, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SSH2_FINGERPRINT_RAW", PHP_SSH2_FINGERPRINT_RAW, CONST_CS | CONST_PERSISTENT); + + REGISTER_LONG_CONSTANT("SSH2_TERM_UNIT_CHARS", PHP_SSH2_TERM_UNIT_CHARS, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SSH2_TERM_UNIT_PIXELS", PHP_SSH2_TERM_UNIT_PIXELS, CONST_CS | CONST_PERSISTENT); + + REGISTER_STRING_CONSTANT("SSH2_DEFAULT_TERMINAL", PHP_SSH2_DEFAULT_TERMINAL, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SSH2_DEFAULT_TERM_WIDTH", PHP_SSH2_DEFAULT_TERM_WIDTH, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SSH2_DEFAULT_TERM_HEIGHT", PHP_SSH2_DEFAULT_TERM_HEIGHT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SSH2_DEFAULT_TERM_UNIT", PHP_SSH2_DEFAULT_TERM_UNIT, CONST_CS | CONST_PERSISTENT); + + REGISTER_LONG_CONSTANT("SSH2_STREAM_STDIO", 0, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SSH2_STREAM_STDERR", SSH_EXTENDED_DATA_STDERR, CONST_CS | CONST_PERSISTENT); + +#ifdef PHP_SSH2_POLL + /* events/revents */ + REGISTER_LONG_CONSTANT("SSH2_POLLIN", LIBSSH2_POLLFD_POLLIN, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SSH2_POLLEXT", LIBSSH2_POLLFD_POLLEXT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SSH2_POLLOUT", LIBSSH2_POLLFD_POLLOUT, CONST_CS | CONST_PERSISTENT); + + /* revents only */ + REGISTER_LONG_CONSTANT("SSH2_POLLERR", LIBSSH2_POLLFD_POLLERR, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SSH2_POLLHUP", LIBSSH2_POLLFD_POLLHUP, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SSH2_POLLNVAL", LIBSSH2_POLLFD_POLLNVAL, CONST_CS | CONST_PERSISTENT); +#if (defined(LIBSSH2_APINO) && (LIBSSH2_APINO > 200503221619)) || (defined(LIBSSH2_VERSION_NUM) && LIBSSH2_VERSION_NUM >= 0x001000) + REGISTER_LONG_CONSTANT("SSH2_POLL_SESSION_CLOSED", LIBSSH2_POLLFD_SESSION_CLOSED, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SSH2_POLL_CHANNEL_CLOSED", LIBSSH2_POLLFD_CHANNEL_CLOSED, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SSH2_POLL_LISTENER_CLOSED", LIBSSH2_POLLFD_LISTENER_CLOSED, CONST_CS | CONST_PERSISTENT); +#endif /* >= LIBSSH2-0.10 */ +#endif /* POLL */ + + return (php_register_url_stream_wrapper("ssh2.shell", &php_ssh2_stream_wrapper_shell TSRMLS_CC) == SUCCESS && + php_register_url_stream_wrapper("ssh2.exec", &php_ssh2_stream_wrapper_exec TSRMLS_CC) == SUCCESS && + php_register_url_stream_wrapper("ssh2.tunnel", &php_ssh2_stream_wrapper_tunnel TSRMLS_CC) == SUCCESS && + php_register_url_stream_wrapper("ssh2.scp", &php_ssh2_stream_wrapper_scp TSRMLS_CC) == SUCCESS && + php_register_url_stream_wrapper("ssh2.sftp", &php_ssh2_sftp_wrapper TSRMLS_CC) == SUCCESS) ? SUCCESS : FAILURE; +} +/* }}} */ + +/* {{{ PHP_MSHUTDOWN_FUNCTION + */ +PHP_MSHUTDOWN_FUNCTION(ssh2) +{ + return (php_unregister_url_stream_wrapper("ssh2.shell" TSRMLS_CC) == SUCCESS && + php_unregister_url_stream_wrapper("ssh2.exec" TSRMLS_CC) == SUCCESS && + php_unregister_url_stream_wrapper("ssh2.tunnel" TSRMLS_CC) == SUCCESS && + php_unregister_url_stream_wrapper("ssh2.scp" TSRMLS_CC) == SUCCESS && + php_unregister_url_stream_wrapper("ssh2.sftp" TSRMLS_CC) == SUCCESS) ? SUCCESS : FAILURE; +} +/* }}} */ + +/* {{{ PHP_MINFO_FUNCTION + */ +PHP_MINFO_FUNCTION(ssh2) +{ + php_info_print_table_start(); + php_info_print_table_header(2, "SSH2 support", "enabled"); + php_info_print_table_row(2, "extension version", PHP_SSH2_VERSION); + php_info_print_table_row(2, "libssh2 version", LIBSSH2_VERSION); + php_info_print_table_row(2, "banner", LIBSSH2_SSH_BANNER); +#ifdef PHP_SSH2_REMOTE_FORWARDING + php_info_print_table_row(2, "remote forwarding", "enabled"); +#endif +#ifdef PHP_SSH2_HOSTBASED_AUTH + php_info_print_table_row(2, "hostbased auth", "enabled"); +#endif +#ifdef PHP_SSH2_POLL + php_info_print_table_row(2, "polling support", "enabled"); +#endif +#ifdef PHP_SSH2_PUBLICKEY_SUBSYSTEM + php_info_print_table_row(2, "publickey subsystem", "enabled"); +#endif + php_info_print_table_end(); +} +/* }}} */ + +/* {{{ ssh2_functions[] + */ +zend_function_entry ssh2_functions[] = { + PHP_FE(ssh2_connect, NULL) + PHP_FE(ssh2_methods_negotiated, NULL) + PHP_FE(ssh2_fingerprint, NULL) + + PHP_FE(ssh2_auth_none, NULL) + PHP_FE(ssh2_auth_password, NULL) + PHP_FE(ssh2_auth_pubkey_file, NULL) +#ifdef PHP_SSH2_HOSTBASED_AUTH + PHP_FE(ssh2_auth_hostbased_file, NULL) +#endif /* PHP_SSH2_HOSTBASED_AUTH */ + +#ifdef PHP_SSH2_REMOTE_FORWARDING + PHP_FE(ssh2_forward_listen, NULL) + PHP_FE(ssh2_forward_accept, NULL) +#endif /* PHP_SSH2_REMOTE_FORWARDING */ + + /* Stream Stuff */ + PHP_FE(ssh2_shell, NULL) + PHP_FE(ssh2_exec, NULL) + PHP_FE(ssh2_tunnel, NULL) + PHP_FE(ssh2_scp_recv, NULL) + PHP_FE(ssh2_scp_send, NULL) + PHP_FE(ssh2_fetch_stream, NULL) +#ifdef PHP_SSH2_POLL + PHP_FE(ssh2_poll, php_ssh2_first_arg_force_ref) +#endif + + /* SFTP Stuff */ + PHP_FE(ssh2_sftp, NULL) + + /* SFTP Wrapper Ops */ + PHP_FE(ssh2_sftp_rename, NULL) + PHP_FE(ssh2_sftp_unlink, NULL) + PHP_FE(ssh2_sftp_mkdir, NULL) + PHP_FE(ssh2_sftp_rmdir, NULL) + PHP_FE(ssh2_sftp_stat, NULL) + PHP_FE(ssh2_sftp_lstat, NULL) + PHP_FE(ssh2_sftp_symlink, NULL) + PHP_FE(ssh2_sftp_readlink, NULL) + PHP_FE(ssh2_sftp_realpath, NULL) + + /* Publickey subsystem */ +#ifdef PHP_SSH2_PUBLICKEY_SUBSYSTEM + PHP_FE(ssh2_publickey_init, NULL) + PHP_FE(ssh2_publickey_add, NULL) + PHP_FE(ssh2_publickey_remove, NULL) + PHP_FE(ssh2_publickey_list, NULL) +#endif + + {NULL, NULL, NULL} +}; +/* }}} */ + +/* {{{ ssh2_module_entry + */ +zend_module_entry ssh2_module_entry = { +#if ZEND_MODULE_API_NO >= 20010901 + STANDARD_MODULE_HEADER, +#endif + "ssh2", + ssh2_functions, + PHP_MINIT(ssh2), + PHP_MSHUTDOWN(ssh2), + NULL, /* RINIT */ + NULL, /* RSHUTDOWN */ + PHP_MINFO(ssh2), +#if ZEND_MODULE_API_NO >= 20010901 + PHP_SSH2_VERSION, +#endif + STANDARD_MODULE_PROPERTIES +}; +/* }}} */ + +#ifdef COMPILE_DL_SSH2 +ZEND_GET_MODULE(ssh2) +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff -Nru php-ssh2-0.11.2/ssh2-0.11.3/ssh2_fopen_wrappers.c php-ssh2-0.11.3/ssh2-0.11.3/ssh2_fopen_wrappers.c --- php-ssh2-0.11.2/ssh2-0.11.3/ssh2_fopen_wrappers.c 1970-01-01 00:00:00.000000000 +0000 +++ php-ssh2-0.11.3/ssh2-0.11.3/ssh2_fopen_wrappers.c 2011-09-27 20:05:05.000000000 +0000 @@ -0,0 +1,1273 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Sara Golemon | + +----------------------------------------------------------------------+ + + $Id: ssh2_fopen_wrappers.c 306892 2010-12-30 23:59:55Z pajoye $ +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ssh2.h" + +/* ********************** + * channel_stream_ops * + ********************** */ + +static size_t php_ssh2_channel_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) +{ + php_ssh2_channel_data *abstract = (php_ssh2_channel_data*)stream->abstract; + + libssh2_channel_set_blocking(abstract->channel, abstract->is_blocking); + return libssh2_channel_write_ex(abstract->channel, abstract->streamid, buf, count); +} + +static size_t php_ssh2_channel_stream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) +{ + php_ssh2_channel_data *abstract = (php_ssh2_channel_data*)stream->abstract; + ssize_t readstate; + + stream->eof = libssh2_channel_eof(abstract->channel); + libssh2_channel_set_blocking(abstract->channel, abstract->is_blocking); + + readstate = libssh2_channel_read_ex(abstract->channel, abstract->streamid, buf, count); + return (readstate < 0 ? 0 : readstate); +} + +static int php_ssh2_channel_stream_close(php_stream *stream, int close_handle TSRMLS_DC) +{ + php_ssh2_channel_data *abstract = (php_ssh2_channel_data*)stream->abstract; + + if (!abstract->refcount || (--(*(abstract->refcount)) == 0)) { + /* Last one out, turn off the lights */ + if (abstract->refcount) { + efree(abstract->refcount); + } + libssh2_channel_eof(abstract->channel); + libssh2_channel_free(abstract->channel); + zend_list_delete(abstract->session_rsrc); + } + efree(abstract); + + return 0; +} + +static int php_ssh2_channel_stream_flush(php_stream *stream TSRMLS_DC) +{ + php_ssh2_channel_data *abstract = (php_ssh2_channel_data*)stream->abstract; + + return libssh2_channel_flush_ex(abstract->channel, abstract->streamid); +} + +static int php_ssh2_channel_stream_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC) +{ + php_ssh2_channel_data *abstract = (php_ssh2_channel_data*)stream->abstract; + int ret; + + switch (option) { + case PHP_STREAM_OPTION_BLOCKING: + ret = abstract->is_blocking; + abstract->is_blocking = value; + return ret; + break; +#if PHP_MAJOR_VERSION >= 5 + case PHP_STREAM_OPTION_CHECK_LIVENESS: + return stream->eof = libssh2_channel_eof(abstract->channel); + break; +#endif + } + + return -1; +} + +php_stream_ops php_ssh2_channel_stream_ops = { + php_ssh2_channel_stream_write, + php_ssh2_channel_stream_read, + php_ssh2_channel_stream_close, + php_ssh2_channel_stream_flush, + PHP_SSH2_CHANNEL_STREAM_NAME, + NULL, /* seek */ + NULL, /* cast */ + NULL, /* stat */ + php_ssh2_channel_stream_set_option, +}; + +/* ********************* + * Magic Path Helper * + ********************* */ + +/* {{{ php_ssh2_fopen_wraper_parse_path + * Parse an ssh2.*:// path + */ +php_url *php_ssh2_fopen_wraper_parse_path( char *path, char *type, php_stream_context *context, + LIBSSH2_SESSION **psession, int *presource_id, + LIBSSH2_SFTP **psftp, int *psftp_rsrcid + TSRMLS_DC) +{ + php_ssh2_sftp_data *sftp_data = NULL; + LIBSSH2_SESSION *session; + php_url *resource; + zval *methods = NULL, *callbacks = NULL, zsession, **tmpzval; + long resource_id; + char *s, *username = NULL, *password = NULL, *pubkey_file = NULL, *privkey_file = NULL; + int username_len = 0, password_len = 0; + + resource = php_url_parse(path); + if (!resource) { + return NULL; + } + + if (strncmp(resource->scheme, "ssh2.", sizeof("ssh2.") - 1)) { + /* Not an ssh wrapper */ + php_url_free(resource); + return NULL; + } + + if (strcmp(resource->scheme + sizeof("ssh2.") - 1, type)) { + /* Wrong ssh2. wrapper type */ + php_url_free(resource); + return NULL; + } + + if (!resource->host) { + return NULL; + } + + /* Look for a resource ID to reuse a session */ + s = resource->host; + if (strncmp(resource->host, "Resource id #", sizeof("Resource id #") - 1) == 0) { + s = resource->host + sizeof("Resource id #") - 1; + } + if (is_numeric_string(s, strlen(s), &resource_id, NULL, 0) == IS_LONG) { + php_ssh2_sftp_data *sftp_data; + + if (psftp) { + sftp_data = (php_ssh2_sftp_data*)zend_fetch_resource(NULL TSRMLS_CC, resource_id, PHP_SSH2_SFTP_RES_NAME, NULL, 1, le_ssh2_sftp); + if (sftp_data) { + /* Want the sftp layer */ + zend_list_addref(resource_id); + *psftp_rsrcid = resource_id; + *psftp = sftp_data->sftp; + *presource_id = sftp_data->session_rsrcid; + *psession = sftp_data->session; + return resource; + } + } + session = (LIBSSH2_SESSION *)zend_fetch_resource(NULL TSRMLS_CC, resource_id, PHP_SSH2_SESSION_RES_NAME, NULL, 1, le_ssh2_session); + if (session) { + if (psftp) { + /* We need an sftp layer too */ + LIBSSH2_SFTP *sftp = libssh2_sftp_init(session); + + if (!sftp) { + php_url_free(resource); + return NULL; + } + sftp_data = emalloc(sizeof(php_ssh2_sftp_data)); + sftp_data->sftp = sftp; + sftp_data->session = session; + sftp_data->session_rsrcid = resource_id; + zend_list_addref(resource_id); + *psftp_rsrcid = ZEND_REGISTER_RESOURCE(NULL, sftp_data, le_ssh2_sftp); + *psftp = sftp; + *presource_id = resource_id; + *psession = session; + return resource; + } + zend_list_addref(resource_id); + *presource_id = resource_id; + *psession = session; + return resource; + } + } + + /* Fallback on finding it in the context */ + if (resource->host[0] == 0 && context && psftp && + php_stream_context_get_option(context, "ssh2", "sftp", &tmpzval) == SUCCESS && + Z_TYPE_PP(tmpzval) == IS_RESOURCE) { + php_ssh2_sftp_data *sftp_data; + sftp_data = (php_ssh2_sftp_data *)zend_fetch_resource(tmpzval TSRMLS_CC, -1, PHP_SSH2_SFTP_RES_NAME, NULL, 1, le_ssh2_sftp); + if (sftp_data) { + zend_list_addref(Z_LVAL_PP(tmpzval)); + *psftp_rsrcid = Z_LVAL_PP(tmpzval); + *psftp = sftp_data->sftp; + *presource_id = sftp_data->session_rsrcid; + *psession = sftp_data->session; + return resource; + } + } + if (resource->host[0] == 0 && context && + php_stream_context_get_option(context, "ssh2", "session", &tmpzval) == SUCCESS && + Z_TYPE_PP(tmpzval) == IS_RESOURCE) { + session = (LIBSSH2_SESSION *)zend_fetch_resource(tmpzval TSRMLS_CC, -1, PHP_SSH2_SESSION_RES_NAME, NULL, 1, le_ssh2_session); + if (session) { + if (psftp) { + /* We need an SFTP layer too! */ + LIBSSH2_SFTP *sftp = libssh2_sftp_init(session); + php_ssh2_sftp_data *sftp_data; + + if (!sftp) { + php_url_free(resource); + return NULL; + } + sftp_data = emalloc(sizeof(php_ssh2_sftp_data)); + sftp_data->sftp = sftp; + sftp_data->session = session; + sftp_data->session_rsrcid = Z_LVAL_PP(tmpzval); + zend_list_addref(Z_LVAL_PP(tmpzval)); + *psftp_rsrcid = ZEND_REGISTER_RESOURCE(NULL, sftp_data, le_ssh2_sftp); + *psftp = sftp; + *presource_id = Z_LVAL_PP(tmpzval); + *psession = session; + return resource; + } + zend_list_addref(Z_LVAL_PP(tmpzval)); + *psession = session; + *presource_id = Z_LVAL_PP(tmpzval); + return resource; + } + } + + /* Make our own connection then */ + if (!resource->port) { + resource->port = 22; + } + + if (context && + php_stream_context_get_option(context, "ssh2", "methods", &tmpzval) == SUCCESS && + Z_TYPE_PP(tmpzval) == IS_ARRAY) { + methods = *tmpzval; + } + + if (context && + php_stream_context_get_option(context, "ssh2", "callbacks", &tmpzval) == SUCCESS && + Z_TYPE_PP(tmpzval) == IS_ARRAY) { + callbacks = *tmpzval; + } + + if (context && + php_stream_context_get_option(context, "ssh2", "username", &tmpzval) == SUCCESS && + Z_TYPE_PP(tmpzval) == IS_STRING) { + username = Z_STRVAL_PP(tmpzval); + username_len = Z_STRLEN_PP(tmpzval); + } + + if (context && + php_stream_context_get_option(context, "ssh2", "password", &tmpzval) == SUCCESS && + Z_TYPE_PP(tmpzval) == IS_STRING) { + password = Z_STRVAL_PP(tmpzval); + password_len = Z_STRLEN_PP(tmpzval); + } + + if (context && + php_stream_context_get_option(context, "ssh2", "pubkey_file", &tmpzval) == SUCCESS && + Z_TYPE_PP(tmpzval) == IS_STRING) { + pubkey_file = Z_STRVAL_PP(tmpzval); + } + + if (context && + php_stream_context_get_option(context, "ssh2", "privkey_file", &tmpzval) == SUCCESS && + Z_TYPE_PP(tmpzval) == IS_STRING) { + privkey_file = Z_STRVAL_PP(tmpzval); + } + + if (resource->user) { + int len = strlen(resource->user); + + if (len) { + username = resource->user; + username_len = len; + } + } + + if (resource->pass) { + int len = strlen(resource->pass); + + if (len) { + password = resource->pass; + password_len = len; + } + } + + if (!username) { + /* username is a minimum */ + php_url_free(resource); + return NULL; + } + + session = php_ssh2_session_connect(resource->host, resource->port, methods, callbacks TSRMLS_CC); + if (!session) { + /* Unable to connect! */ + php_url_free(resource); + return NULL; + } + + /* Authenticate */ + if (pubkey_file && privkey_file) { + if (SSH2_OPENBASEDIR_CHECKPATH(pubkey_file) || SSH2_OPENBASEDIR_CHECKPATH(privkey_file)) { + php_url_free(resource); + return NULL; + } + + /* Attempt pubkey authentication */ + if (!libssh2_userauth_publickey_fromfile(session, username, pubkey_file, privkey_file, password)) { + goto session_authed; + } + } + + if (password) { + /* Attempt password authentication */ + if (libssh2_userauth_password_ex(session, username, username_len, password, password_len, NULL) == 0) { + goto session_authed; + } + } + + /* Auth failure */ + php_url_free(resource); + zend_list_delete(Z_LVAL(zsession)); + return NULL; + +session_authed: + ZEND_REGISTER_RESOURCE(&zsession, session, le_ssh2_session); + + if (psftp) { + LIBSSH2_SFTP *sftp; + zval zsftp; + + sftp = libssh2_sftp_init(session); + if (!sftp) { + php_url_free(resource); + zend_list_delete(Z_LVAL(zsession)); + return NULL; + } + + sftp_data = emalloc(sizeof(php_ssh2_sftp_data)); + sftp_data->session = session; + sftp_data->sftp = sftp; + sftp_data->session_rsrcid = Z_LVAL(zsession); + + ZEND_REGISTER_RESOURCE(&zsftp, sftp_data, le_ssh2_sftp); + *psftp_rsrcid = Z_LVAL(zsftp); + *psftp = sftp; + } + + *presource_id = Z_LVAL(zsession); + *psession = session; + + return resource; +} +/* }}} */ + +/* ***************** + * Shell Wrapper * + ***************** */ + +/* {{{ php_ssh2_shell_open + * Make a stream from a session + */ +static php_stream *php_ssh2_shell_open(LIBSSH2_SESSION *session, int resource_id, char *term, int term_len, zval *environment, long width, long height, long type TSRMLS_DC) +{ + LIBSSH2_CHANNEL *channel; + php_ssh2_channel_data *channel_data; + php_stream *stream; + + channel = libssh2_channel_open_session(session); + if (!channel) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to request a channel from remote host"); + return NULL; + } + + if (environment) { + char *key; + int key_type, key_len; + long idx; + + for(zend_hash_internal_pointer_reset(HASH_OF(environment)); + (key_type = zend_hash_get_current_key_ex(HASH_OF(environment), &key, &key_len, &idx, 0, NULL)) != HASH_KEY_NON_EXISTANT; + zend_hash_move_forward(HASH_OF(environment))) { + if (key_type == HASH_KEY_IS_STRING) { + zval **value; + + if (zend_hash_get_current_data(HASH_OF(environment), (void**)&value) == SUCCESS) { + zval copyval = **value; + + zval_copy_ctor(©val); + convert_to_string(©val); + if (libssh2_channel_setenv_ex(channel, key, key_len, Z_STRVAL(copyval), Z_STRLEN(copyval))) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed setting %s=%s on remote end", key, Z_STRVAL(copyval)); + } + zval_dtor(©val); + } + } else { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Skipping numeric index in environment array"); + } + } + } + + if (type == PHP_SSH2_TERM_UNIT_CHARS) { + if (libssh2_channel_request_pty_ex(channel, term, term_len, NULL, 0, width, height, 0, 0)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating %s pty at %ldx%ld characters", term, width, height); + libssh2_channel_free(channel); + return NULL; + } + } else { + if (libssh2_channel_request_pty_ex(channel, term, term_len, NULL, 0, 0, 0, width, height)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating %s pty at %ldx%ld pixels", term, width, height); + libssh2_channel_free(channel); + return NULL; + } + } + + if (libssh2_channel_shell(channel)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to request shell from remote host"); + libssh2_channel_free(channel); + return NULL; + } + + /* Turn it into a stream */ + channel_data = emalloc(sizeof(php_ssh2_channel_data)); + channel_data->channel = channel; + channel_data->streamid = 0; + channel_data->is_blocking = 0; + channel_data->session_rsrc = resource_id; + channel_data->refcount = NULL; + + stream = php_stream_alloc(&php_ssh2_channel_stream_ops, channel_data, 0, "r+"); + + return stream; +} +/* }}} */ + +/* {{{ php_ssh2_fopen_wrapper_shell + * ssh2.shell:// fopen wrapper + */ +static php_stream *php_ssh2_fopen_wrapper_shell(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) +{ + LIBSSH2_SESSION *session = NULL; + php_stream *stream; + zval **tmpzval, *environment = NULL; + char *terminal = PHP_SSH2_DEFAULT_TERMINAL; + long width = PHP_SSH2_DEFAULT_TERM_WIDTH; + long height = PHP_SSH2_DEFAULT_TERM_HEIGHT; + long type = PHP_SSH2_DEFAULT_TERM_UNIT; + int resource_id = 0, terminal_len = sizeof(PHP_SSH2_DEFAULT_TERMINAL) - 1; + php_url *resource; + char *s, *e; + + resource = php_ssh2_fopen_wraper_parse_path(path, "shell", context, &session, &resource_id, NULL, NULL TSRMLS_CC); + if (!resource || !session) { + return NULL; + } + + if (context && + php_stream_context_get_option(context, "ssh2", "env", &tmpzval) == SUCCESS && + tmpzval && *tmpzval && Z_TYPE_PP(tmpzval) == IS_ARRAY) { + environment = *tmpzval; + } + + if (context && + php_stream_context_get_option(context, "ssh2", "term", &tmpzval) == SUCCESS && + tmpzval && *tmpzval && Z_TYPE_PP(tmpzval) == IS_STRING) { + terminal = Z_STRVAL_PP(tmpzval); + terminal_len = Z_STRLEN_PP(tmpzval); + } + + if (context && + php_stream_context_get_option(context, "ssh2", "term_width", &tmpzval) == SUCCESS && + tmpzval && *tmpzval) { + zval *copyval; + ALLOC_INIT_ZVAL(copyval); + *copyval = **tmpzval; + convert_to_long(copyval); + width = Z_LVAL_P(copyval); + zval_ptr_dtor(©val); + } + + if (context && + php_stream_context_get_option(context, "ssh2", "term_height", &tmpzval) == SUCCESS && + tmpzval && *tmpzval) { + zval *copyval; + ALLOC_INIT_ZVAL(copyval); + *copyval = **tmpzval; + convert_to_long(copyval); + height = Z_LVAL_P(copyval); + zval_ptr_dtor(©val); + } + + if (context && + php_stream_context_get_option(context, "ssh2", "term_units", &tmpzval) == SUCCESS && + tmpzval && *tmpzval) { + zval *copyval; + ALLOC_INIT_ZVAL(copyval); + *copyval = **tmpzval; + convert_to_long(copyval); + type = Z_LVAL_P(copyval); + zval_ptr_dtor(©val); + } + + s = resource->path ? resource->path : NULL; + e = s ? s + strlen(s) : NULL; + + if (s && s[0] == '/') { + /* Terminal type encoded into URL overrides context terminal type */ + char *p; + + s++; + p = strchr(s, '/'); + if (p) { + if (p - s) { + terminal = s; + terminal_len = p - terminal; + s += terminal_len + 1; + } else { + /* "null" terminal given, skip it */ + s++; + } + } else { + int len; + + if ((len = strlen(path + 1))) { + terminal = s; + terminal_len = len; + s += len; + } + } + } + + /* TODO: Accept resolution and environment vars as URL style parameters + * ssh2.shell://hostorresource/terminal/99x99c?envvar=envval&envvar=envval.... + */ + stream = php_ssh2_shell_open(session, resource_id, terminal, terminal_len, environment, width, height, type TSRMLS_CC); + if (!stream) { + zend_list_delete(resource_id); + } + php_url_free(resource); + + return stream; +} +/* }}} */ + +static php_stream_wrapper_ops php_ssh2_shell_stream_wops = { + php_ssh2_fopen_wrapper_shell, + NULL, /* stream_close */ + NULL, /* stat */ + NULL, /* stat_url */ + NULL, /* opendir */ + "ssh2.shell" +}; + +php_stream_wrapper php_ssh2_stream_wrapper_shell = { + &php_ssh2_shell_stream_wops, + NULL, + 0 +}; + +/* {{{ proto stream ssh2_shell(resource session[, string term_type[, array env[, int width, int height[, int width_height_type]]]]) + * Open a shell at the remote end and allocate a channel for it + */ +PHP_FUNCTION(ssh2_shell) +{ + LIBSSH2_SESSION *session; + php_stream *stream; + zval *zsession; + zval *environment = NULL; + char *term = PHP_SSH2_DEFAULT_TERMINAL; + int term_len = sizeof(PHP_SSH2_DEFAULT_TERMINAL) - 1; + long width = PHP_SSH2_DEFAULT_TERM_WIDTH; + long height = PHP_SSH2_DEFAULT_TERM_HEIGHT; + long type = PHP_SSH2_DEFAULT_TERM_UNIT; + int argc = ZEND_NUM_ARGS(); + + if (argc == 5) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "width specified without height paramter"); + RETURN_FALSE; + } + + if (zend_parse_parameters(argc TSRMLS_CC, "r|sa!lll", &zsession, &term, &term_len, &environment, &width, &height, &type) == FAILURE) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); + + stream = php_ssh2_shell_open(session, Z_LVAL_P(zsession), term, term_len, environment, width, height, type TSRMLS_CC); + if (!stream) { + RETURN_FALSE; + } + + /* Ensure that channels are freed BEFORE the sessions they belong to */ + zend_list_addref(Z_LVAL_P(zsession)); + + php_stream_to_zval(stream, return_value); +} +/* }}} */ + +/* **************** + * Exec Wrapper * + **************** */ + +/* {{{ php_ssh2_exec_command + * Make a stream from a session + */ +static php_stream *php_ssh2_exec_command(LIBSSH2_SESSION *session, int resource_id, char *command, char *term, int term_len, zval *environment, long width, long height, long type TSRMLS_DC) +{ + LIBSSH2_CHANNEL *channel; + php_ssh2_channel_data *channel_data; + php_stream *stream; + + channel = libssh2_channel_open_session(session); + if (!channel) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to request a channel from remote host"); + return NULL; + } + + if (environment) { + char *key; + int key_type, key_len; + long idx; + + for(zend_hash_internal_pointer_reset(HASH_OF(environment)); + (key_type = zend_hash_get_current_key_ex(HASH_OF(environment), &key, &key_len, &idx, 0, NULL)) != HASH_KEY_NON_EXISTANT; + zend_hash_move_forward(HASH_OF(environment))) { + if (key_type == HASH_KEY_IS_STRING) { + zval **value; + + if (zend_hash_get_current_data(HASH_OF(environment), (void**)&value) == SUCCESS) { + zval copyval = **value; + + zval_copy_ctor(©val); + convert_to_string(©val); + if (libssh2_channel_setenv_ex(channel, key, key_len, Z_STRVAL(copyval), Z_STRLEN(copyval))) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed setting %s=%s on remote end", key, Z_STRVAL(copyval)); + } + zval_dtor(©val); + } + } else { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Skipping numeric index in environment array"); + } + } + } + + if (term) { + if (type == PHP_SSH2_TERM_UNIT_CHARS) { + if (libssh2_channel_request_pty_ex(channel, term, term_len, NULL, 0, width, height, 0, 0)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating %s pty at %ldx%ld characters", term, width, height); + libssh2_channel_free(channel); + return NULL; + } + } else { + if (libssh2_channel_request_pty_ex(channel, term, term_len, NULL, 0, 0, 0, width, height)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating %s pty at %ldx%ld pixels", term, width, height); + libssh2_channel_free(channel); + return NULL; + } + } + } + + if (libssh2_channel_exec(channel, command)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to request command execution on remote host"); + libssh2_channel_free(channel); + return NULL; + } + + /* Turn it into a stream */ + channel_data = emalloc(sizeof(php_ssh2_channel_data)); + channel_data->channel = channel; + channel_data->streamid = 0; + channel_data->is_blocking = 0; + channel_data->session_rsrc = resource_id; + channel_data->refcount = NULL; + + stream = php_stream_alloc(&php_ssh2_channel_stream_ops, channel_data, 0, "r+"); + + return stream; +} +/* }}} */ + +/* {{{ php_ssh2_fopen_wrapper_exec + * ssh2.exec:// fopen wrapper + */ +static php_stream *php_ssh2_fopen_wrapper_exec(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) +{ + LIBSSH2_SESSION *session = NULL; + php_stream *stream; + zval **tmpzval, *environment = NULL; + int resource_id = 0; + php_url *resource; + char *terminal = NULL; + int terminal_len = 0; + long width = PHP_SSH2_DEFAULT_TERM_WIDTH; + long height = PHP_SSH2_DEFAULT_TERM_HEIGHT; + long type = PHP_SSH2_DEFAULT_TERM_UNIT; + + resource = php_ssh2_fopen_wraper_parse_path(path, "exec", context, &session, &resource_id, NULL, NULL TSRMLS_CC); + if (!resource || !session) { + return NULL; + } + if (!resource->path) { + php_url_free(resource); + zend_list_delete(resource_id); + return NULL; + } + + if (context && + php_stream_context_get_option(context, "ssh2", "env", &tmpzval) == SUCCESS && + tmpzval && *tmpzval && Z_TYPE_PP(tmpzval) == IS_ARRAY) { + environment = *tmpzval; + } + + if (context && + php_stream_context_get_option(context, "ssh2", "term", &tmpzval) == SUCCESS && + tmpzval && *tmpzval && Z_TYPE_PP(tmpzval) == IS_STRING) { + terminal = Z_STRVAL_PP(tmpzval); + terminal_len = Z_STRLEN_PP(tmpzval); + } + + if (context && + php_stream_context_get_option(context, "ssh2", "term_width", &tmpzval) == SUCCESS && + tmpzval && *tmpzval) { + zval *copyval; + ALLOC_INIT_ZVAL(copyval); + *copyval = **tmpzval; + convert_to_long(copyval); + width = Z_LVAL_P(copyval); + zval_ptr_dtor(©val); + } + + if (context && + php_stream_context_get_option(context, "ssh2", "term_height", &tmpzval) == SUCCESS && + tmpzval && *tmpzval) { + zval *copyval; + ALLOC_INIT_ZVAL(copyval); + *copyval = **tmpzval; + convert_to_long(copyval); + height = Z_LVAL_P(copyval); + zval_ptr_dtor(©val); + } + + if (context && + php_stream_context_get_option(context, "ssh2", "term_units", &tmpzval) == SUCCESS && + tmpzval && *tmpzval) { + zval *copyval; + ALLOC_INIT_ZVAL(copyval); + *copyval = **tmpzval; + convert_to_long(copyval); + type = Z_LVAL_P(copyval); + zval_ptr_dtor(©val); + } + + stream = php_ssh2_exec_command(session, resource_id, resource->path + 1, terminal, terminal_len, environment, width, height, type TSRMLS_CC); + if (!stream) { + zend_list_delete(resource_id); + } + php_url_free(resource); + + return stream; +} +/* }}} */ + +static php_stream_wrapper_ops php_ssh2_exec_stream_wops = { + php_ssh2_fopen_wrapper_exec, + NULL, /* stream_close */ + NULL, /* stat */ + NULL, /* stat_url */ + NULL, /* opendir */ + "ssh2.exec" +}; + +php_stream_wrapper php_ssh2_stream_wrapper_exec = { + &php_ssh2_exec_stream_wops, + NULL, + 0 +}; + +/* {{{ proto stream ssh2_exec(resource session, string command[, string pty[, array env[, int width[, int heightp[, int width_height_type]]]]]) + * Execute a command at the remote end and allocate a channel for it + * + * This function has a dirty little secret.... pty and env can be in either order.... shhhh... don't tell anyone + */ +PHP_FUNCTION(ssh2_exec) +{ + LIBSSH2_SESSION *session; + php_stream *stream; + zval *zsession; + zval *environment = NULL; + zval *zpty = NULL; + char *command; + int command_len; + long width = PHP_SSH2_DEFAULT_TERM_WIDTH; + long height = PHP_SSH2_DEFAULT_TERM_HEIGHT; + long type = PHP_SSH2_DEFAULT_TERM_UNIT; + char *term = NULL; + int term_len = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|z!z!lll", &zsession, &command, &command_len, &zpty, &environment, &width, &height, &type) == FAILURE) { + RETURN_FALSE; + } + + if (zpty && Z_TYPE_P(zpty) == IS_ARRAY) { + /* Swap pty and environment -- old call style */ + zval *tmp = zpty; + zpty = environment; + environment = tmp; + } + + if (environment && Z_TYPE_P(environment) != IS_ARRAY) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "ssh2_exec() expects arg 4 to be of type array"); + RETURN_FALSE; + } + + if (zpty) { + convert_to_string(zpty); + term = Z_STRVAL_P(zpty); + term_len = Z_STRLEN_P(zpty); + } + + ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); + + stream = php_ssh2_exec_command(session, Z_LVAL_P(zsession), command, term, term_len, environment, width, height, type TSRMLS_CC); + if (!stream) { + RETURN_FALSE; + } + + /* Ensure that channels are freed BEFORE the sessions they belong to */ + zend_list_addref(Z_LVAL_P(zsession)); + + php_stream_to_zval(stream, return_value); +} +/* }}} */ + +/* *************** + * SCP Wrapper * + *************** */ + +/* {{{ php_ssh2_scp_xfer + * Make a stream from a session + */ +static php_stream *php_ssh2_scp_xfer(LIBSSH2_SESSION *session, int resource_id, char *filename TSRMLS_DC) +{ + LIBSSH2_CHANNEL *channel; + php_ssh2_channel_data *channel_data; + php_stream *stream; + + channel = libssh2_scp_recv(session, filename, NULL); + if (!channel) { + char *error = ""; + libssh2_session_last_error(session, &error, NULL, 0); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to request a channel from remote host: %s", error); + return NULL; + } + + /* Turn it into a stream */ + channel_data = emalloc(sizeof(php_ssh2_channel_data)); + channel_data->channel = channel; + channel_data->streamid = 0; + channel_data->is_blocking = 0; + channel_data->session_rsrc = resource_id; + channel_data->refcount = NULL; + + stream = php_stream_alloc(&php_ssh2_channel_stream_ops, channel_data, 0, "r"); + + return stream; +} +/* }}} */ + +/* {{{ php_ssh2_fopen_wrapper_scp + * ssh2.scp:// fopen wrapper (Read mode only, if you want to know why write mode isn't supported as a stream, take a look at the SCP protocol) + */ +static php_stream *php_ssh2_fopen_wrapper_scp(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) +{ + LIBSSH2_SESSION *session = NULL; + php_stream *stream; + int resource_id = 0; + php_url *resource; + + if (strchr(mode, '+') || strchr(mode, 'a') || strchr(mode, 'w')) { + return NULL; + } + + resource = php_ssh2_fopen_wraper_parse_path(path, "scp", context, &session, &resource_id, NULL, NULL TSRMLS_CC); + if (!resource || !session) { + return NULL; + } + if (!resource->path) { + php_url_free(resource); + zend_list_delete(resource_id); + return NULL; + } + + stream = php_ssh2_scp_xfer(session, resource_id, resource->path TSRMLS_CC); + if (!stream) { + zend_list_delete(resource_id); + } + php_url_free(resource); + + return stream; +} +/* }}} */ + +static php_stream_wrapper_ops php_ssh2_scp_stream_wops = { + php_ssh2_fopen_wrapper_scp, + NULL, /* stream_close */ + NULL, /* stat */ + NULL, /* stat_url */ + NULL, /* opendir */ + "ssh2.scp" +}; + +php_stream_wrapper php_ssh2_stream_wrapper_scp = { + &php_ssh2_scp_stream_wops, + NULL, + 0 +}; + +/* {{{ proto bool ssh2_scp_recv(resource session, string remote_file, string local_file) + * Request a file via SCP + */ +PHP_FUNCTION(ssh2_scp_recv) +{ + LIBSSH2_SESSION *session; + LIBSSH2_CHANNEL *remote_file; + struct stat sb; + php_stream *local_file; + zval *zsession; + char *remote_filename, *local_filename; + int remote_filename_len, local_filename_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rss", &zsession, &remote_filename, &remote_filename_len, + &local_filename, &local_filename_len) == FAILURE) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); + + remote_file = libssh2_scp_recv(session, remote_filename, &sb); + if (!remote_file) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to receive remote file"); + RETURN_FALSE; + } + libssh2_channel_set_blocking(remote_file, 1); + + local_file = php_stream_open_wrapper(local_filename, "wb", ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL); + if (!local_file) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to write to local file"); + libssh2_channel_free(remote_file); + RETURN_FALSE; + } + + while (sb.st_size) { + char buffer[8192]; + int bytes_read; + + bytes_read = libssh2_channel_read(remote_file, buffer, sb.st_size > 8192 ? 8192 : sb.st_size); + if (bytes_read < 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error reading from remote file"); + libssh2_channel_free(remote_file); + php_stream_close(local_file); + RETURN_FALSE; + } + php_stream_write(local_file, buffer, bytes_read); + sb.st_size -= bytes_read; + } + + libssh2_channel_free(remote_file); + php_stream_close(local_file); + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto stream ssh2_scp_send(resource session, string local_file, string remote_file[, int create_mode = 0644]) + * Send a file via SCP + */ +PHP_FUNCTION(ssh2_scp_send) +{ + LIBSSH2_SESSION *session; + LIBSSH2_CHANNEL *remote_file; + php_stream *local_file; + zval *zsession; + char *local_filename, *remote_filename; + int local_filename_len, remote_filename_len; + long create_mode = 0644; + php_stream_statbuf ssb; + int argc = ZEND_NUM_ARGS(); + + if (zend_parse_parameters(argc TSRMLS_CC, "rss|l", &zsession, &local_filename, &local_filename_len, + &remote_filename, &remote_filename_len, &create_mode) == FAILURE) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); + + local_file = php_stream_open_wrapper(local_filename, "rb", ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL); + if (!local_file) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to read source file"); + RETURN_FALSE; + } + + if (php_stream_stat(local_file, &ssb)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed statting local file"); + php_stream_close(local_file); + RETURN_FALSE; + } + + if (argc < 4) { + create_mode = ssb.sb.st_mode & 0777; + } + + remote_file = libssh2_scp_send_ex(session, remote_filename, create_mode, ssb.sb.st_size, ssb.sb.st_atime, ssb.sb.st_mtime); + if (!remote_file) { + int last_error = 0; + char *error_msg = NULL; + + last_error = libssh2_session_last_error(session, &error_msg, NULL, 0); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failure creating remote file: %s", error_msg); + php_stream_close(local_file); + RETURN_FALSE; + } + libssh2_channel_set_blocking(remote_file, 1); + + while (ssb.sb.st_size) { + char buffer[8192]; + size_t toread = MIN(8192, ssb.sb.st_size); + size_t bytesread = php_stream_read(local_file, buffer, toread); + + if (bytesread <= 0 || bytesread > toread) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed copying file 2"); + php_stream_close(local_file); + libssh2_channel_free(remote_file); + RETURN_FALSE; + } + + if (bytesread != libssh2_channel_write(remote_file, buffer, bytesread)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed copying file"); + php_stream_close(local_file); + libssh2_channel_free(remote_file); + RETURN_FALSE; + } + ssb.sb.st_size -= bytesread; + } + + php_stream_close(local_file); + libssh2_channel_free(remote_file); + RETURN_TRUE; +} +/* }}} */ + +/* *************************** + * Direct TCP/IP Transport * + *************************** */ + +/* {{{ php_ssh2_direct_tcpip + * Make a stream from a session + */ +static php_stream *php_ssh2_direct_tcpip(LIBSSH2_SESSION *session, int resource_id, char *host, int port TSRMLS_DC) +{ + LIBSSH2_CHANNEL *channel; + php_ssh2_channel_data *channel_data; + php_stream *stream; + + channel = libssh2_channel_direct_tcpip(session, host, port); + if (!channel) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to request a channel from remote host"); + return NULL; + } + + /* Turn it into a stream */ + channel_data = emalloc(sizeof(php_ssh2_channel_data)); + channel_data->channel = channel; + channel_data->streamid = 0; + channel_data->is_blocking = 0; + channel_data->session_rsrc = resource_id; + channel_data->refcount = NULL; + + stream = php_stream_alloc(&php_ssh2_channel_stream_ops, channel_data, 0, "r+"); + + return stream; +} +/* }}} */ + +/* {{{ php_ssh2_fopen_wrapper_tunnel + * ssh2.tunnel:// fopen wrapper + */ +static php_stream *php_ssh2_fopen_wrapper_tunnel(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) +{ + LIBSSH2_SESSION *session = NULL; + php_stream *stream = NULL; + php_url *resource; + char *host = NULL; + int port = 0; + int resource_id = 0; + + resource = php_ssh2_fopen_wraper_parse_path(path, "tunnel", context, &session, &resource_id, NULL, NULL TSRMLS_CC); + if (!resource || !session) { + return NULL; + } + + if (resource->path && resource->path[0] == '/') { + char *colon; + + host = resource->path + 1; + if (*host == '[') { + /* IPv6 Encapsulated Format */ + host++; + colon = strstr(host, "]:"); + if (colon) { + *colon = 0; + colon += 2; + } + } else { + colon = strchr(host, ':'); + if (colon) { + *(colon++) = 0; + } + } + if (colon) { + port = atoi(colon); + } + } + + if ((port <= 0) || (port > 65535) || !host || (strlen(host) == 0)) { + /* Invalid connection criteria */ + php_url_free(resource); + zend_list_delete(resource_id); + return NULL; + } + + stream = php_ssh2_direct_tcpip(session, resource_id, host, port TSRMLS_CC); + if (!stream) { + zend_list_delete(resource_id); + } + php_url_free(resource); + + return stream; +} +/* }}} */ + +static php_stream_wrapper_ops php_ssh2_tunnel_stream_wops = { + php_ssh2_fopen_wrapper_tunnel, + NULL, /* stream_close */ + NULL, /* stat */ + NULL, /* stat_url */ + NULL, /* opendir */ + "ssh2.tunnel" +}; + +php_stream_wrapper php_ssh2_stream_wrapper_tunnel = { + &php_ssh2_tunnel_stream_wops, + NULL, + 0 +}; + +/* {{{ proto stream ssh2_tunnel(resource session, string host, int port) + * Tunnel to remote TCP/IP host/port + */ +PHP_FUNCTION(ssh2_tunnel) +{ + LIBSSH2_SESSION *session; + php_stream *stream; + zval *zsession; + char *host; + int host_len; + long port; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsl", &zsession, &host, &host_len, &port) == FAILURE) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); + + stream = php_ssh2_direct_tcpip(session, Z_LVAL_P(zsession), host, port TSRMLS_CC); + if (!stream) { + RETURN_FALSE; + } + + /* Ensure that channels are freed BEFORE the sessions they belong to */ + zend_list_addref(Z_LVAL_P(zsession)); + + php_stream_to_zval(stream, return_value); +} +/* }}} */ + +/* ****************** + * Generic Helper * + ****************** */ + +/* {{{ proto stream ssh2_fetch_stream(stream channel, int streamid) + * Fetch an extended data stream + */ +PHP_FUNCTION(ssh2_fetch_stream) +{ + php_ssh2_channel_data *data, *stream_data; + php_stream *parent, *stream; + zval *zparent; + long streamid; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &zparent, &streamid) == FAILURE) { + RETURN_FALSE; + } + + if (streamid < 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid stream ID requested"); + RETURN_FALSE; + } + + php_stream_from_zval(parent, &zparent); + + if (parent->ops != &php_ssh2_channel_stream_ops) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Provided stream is not of type " PHP_SSH2_CHANNEL_STREAM_NAME); + RETURN_FALSE; + } + + data = (php_ssh2_channel_data*)parent->abstract; + + if (!data->refcount) { + data->refcount = emalloc(sizeof(unsigned char)); + *(data->refcount) = 1; + } + + if (*(data->refcount) == 255) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Too many streams associated to a single channel"); + RETURN_FALSE; + } + + (*(data->refcount))++; + + stream_data = emalloc(sizeof(php_ssh2_channel_data)); + memcpy(stream_data, data, sizeof(php_ssh2_channel_data)); + stream_data->streamid = streamid; + + stream = php_stream_alloc(&php_ssh2_channel_stream_ops, stream_data, 0, "r+"); + if (!stream) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error opening substream"); + efree(stream_data); + *(data->refcount)--; + RETURN_FALSE; + } + + php_stream_to_zval(stream, return_value); +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff -Nru php-ssh2-0.11.2/ssh2-0.11.3/ssh2_sftp.c php-ssh2-0.11.3/ssh2-0.11.3/ssh2_sftp.c --- php-ssh2-0.11.2/ssh2-0.11.3/ssh2_sftp.c 1970-01-01 00:00:00.000000000 +0000 +++ php-ssh2-0.11.3/ssh2-0.11.3/ssh2_sftp.c 2011-09-27 20:05:05.000000000 +0000 @@ -0,0 +1,856 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Sara Golemon | + +----------------------------------------------------------------------+ + + $Id: ssh2_sftp.c 317118 2011-09-21 19:05:21Z bjori $ +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ssh2.h" +#include "ext/standard/php_string.h" + +/* ************************* + * Resource Housekeeping * + ************************* */ + +void php_ssh2_sftp_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) +{ + php_ssh2_sftp_data *data = (php_ssh2_sftp_data*)rsrc->ptr; + + if (!data) { + return; + } + + libssh2_sftp_shutdown(data->sftp); + + zend_list_delete(data->session_rsrcid); + + efree(data); +} + +/* ***************** + * SFTP File Ops * + ***************** */ + +inline unsigned long php_ssh2_parse_fopen_modes(char *openmode) { + unsigned long flags = 0; + + if (strchr(openmode, 'a')) { + flags |= LIBSSH2_FXF_APPEND; + } + + if (strchr(openmode, 'w')) { + flags |= LIBSSH2_FXF_WRITE | LIBSSH2_FXF_TRUNC | LIBSSH2_FXF_CREAT; + } + + if (strchr(openmode, 'r')) { + flags |= LIBSSH2_FXF_READ; + } + + if (strchr(openmode, '+')) { + flags |= LIBSSH2_FXF_READ | LIBSSH2_FXF_WRITE; + } + + if (strchr(openmode, 'x')) { + flags |= LIBSSH2_FXF_WRITE | LIBSSH2_FXF_TRUNC | LIBSSH2_FXF_EXCL | LIBSSH2_FXF_CREAT; + } + + return flags; +} + +inline int php_ssh2_sftp_attr2ssb(php_stream_statbuf *ssb, LIBSSH2_SFTP_ATTRIBUTES *attrs) +{ + memset(ssb, 0, sizeof(php_stream_statbuf)); + if (attrs->flags & LIBSSH2_SFTP_ATTR_SIZE) { + ssb->sb.st_size = attrs->filesize; + } + + if (attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) { + ssb->sb.st_uid = attrs->uid; + ssb->sb.st_gid = attrs->gid; + } + if (attrs->flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) { + ssb->sb.st_mode = attrs->permissions; + } + if (attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) { + ssb->sb.st_atime = attrs->atime; + ssb->sb.st_mtime = attrs->mtime; + } + + return 0; +} + +typedef struct _php_ssh2_sftp_handle_data { + LIBSSH2_SFTP_HANDLE *handle; + + long sftp_rsrcid; +} php_ssh2_sftp_handle_data; + +/* {{{ php_ssh2_sftp_stream_write + */ +static size_t php_ssh2_sftp_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) +{ + php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data*)stream->abstract; + ssize_t bytes_written; + + bytes_written = libssh2_sftp_write(data->handle, buf, count); + + return (size_t)(bytes_written<0 ? 0 : bytes_written); +} +/* }}} */ + +/* {{{ php_ssh2_sftp_stream_read + */ +static size_t php_ssh2_sftp_stream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) +{ + php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data*)stream->abstract; + ssize_t bytes_read; + + bytes_read = libssh2_sftp_read(data->handle, buf, count); + + stream->eof = (bytes_read <= 0 && bytes_read != LIBSSH2_ERROR_EAGAIN); + + return (size_t)(bytes_read<0 ? 0 : bytes_read); +} +/* }}} */ + +/* {{{ php_ssh2_sftp_stream_close + */ +static int php_ssh2_sftp_stream_close(php_stream *stream, int close_handle TSRMLS_DC) +{ + php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data*)stream->abstract; + + libssh2_sftp_close(data->handle); + zend_list_delete(data->sftp_rsrcid); + efree(data); + + return 0; +} +/* }}} */ + +/* {{{ php_ssh2_sftp_stream_seek + */ +static int php_ssh2_sftp_stream_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC) +{ + php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data*)stream->abstract; + + switch (whence) { + case SEEK_END: + { + LIBSSH2_SFTP_ATTRIBUTES attrs; + + if (libssh2_sftp_fstat(data->handle, &attrs)) { + return -1; + } + if ((attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) == 0) { + return -1; + } + offset += attrs.filesize; + } + case SEEK_CUR: + { + off_t current_offset = libssh2_sftp_tell(data->handle); + + if (current_offset < 0) { + return -1; + } + + offset += current_offset; + } + } + + libssh2_sftp_seek(data->handle, offset); + + if (newoffset) { + *newoffset = offset; + } + + return 0; +} +/* }}} */ + +/* {{{ php_ssh2_sftp_stream_fstat + */ +static int php_ssh2_sftp_stream_fstat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) +{ + php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data*)stream->abstract; + LIBSSH2_SFTP_ATTRIBUTES attrs; + + if (libssh2_sftp_fstat(data->handle, &attrs)) { + return -1; + } + + return php_ssh2_sftp_attr2ssb(ssb, &attrs); +} +/* }}} */ + +static php_stream_ops php_ssh2_sftp_stream_ops = { + php_ssh2_sftp_stream_write, + php_ssh2_sftp_stream_read, + php_ssh2_sftp_stream_close, + NULL, /* flush */ + PHP_SSH2_SFTP_STREAM_NAME, + php_ssh2_sftp_stream_seek, + NULL, /* cast */ + php_ssh2_sftp_stream_fstat, + NULL, /* set_option */ +}; + +/* {{{ php_ssh2_sftp_stream_opener + */ +static php_stream *php_ssh2_sftp_stream_opener( php_stream_wrapper *wrapper, char *filename, char *mode, + int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) +{ + php_ssh2_sftp_handle_data *data; + LIBSSH2_SESSION *session = NULL; + LIBSSH2_SFTP *sftp = NULL; + LIBSSH2_SFTP_HANDLE *handle; + php_stream *stream; + int resource_id = 0, sftp_rsrcid = 0; + php_url *resource; + unsigned long flags; + long perms = 0644; + + resource = php_ssh2_fopen_wraper_parse_path(filename, "sftp", context, &session, &resource_id, &sftp, &sftp_rsrcid TSRMLS_CC); + if (!resource || !session || !sftp) { + return NULL; + } + + flags = php_ssh2_parse_fopen_modes(mode); + + handle = libssh2_sftp_open(sftp, resource->path, flags, perms); + if (!handle) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to open %s on remote host", filename); + php_url_free(resource); + zend_list_delete(sftp_rsrcid); + return NULL; + } + + data = emalloc(sizeof(php_ssh2_sftp_handle_data)); + data->handle = handle; + data->sftp_rsrcid = sftp_rsrcid; + + stream = php_stream_alloc(&php_ssh2_sftp_stream_ops, data, 0, mode); + if (!stream) { + libssh2_sftp_close(handle); + zend_list_delete(sftp_rsrcid); + efree(data); + } + php_url_free(resource); + + return stream; +} +/* }}} */ + +/* ********************** + * SFTP Directory Ops * + ********************** */ + +/* {{{ php_ssh2_sftp_dirstream_read + */ +static size_t php_ssh2_sftp_dirstream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) +{ + php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data*)stream->abstract; + php_stream_dirent *ent = (php_stream_dirent*)buf; + size_t bytesread = libssh2_sftp_readdir(data->handle, ent->d_name, sizeof(ent->d_name) - 1, NULL); + char *basename = NULL; + size_t basename_len = 0; + + if (bytesread <= 0) { + return 0; + } + ent->d_name[bytesread] = 0; + +#ifdef ZEND_ENGINE_2 + php_basename(ent->d_name, bytesread, NULL, 0, &basename, &basename_len TSRMLS_CC); +#else +/* CURSE YOU BC BREAKS! */ + basename = php_basename(ent->d_name, bytesread, NULL, 0); + if (basename) { + basename_len = strlen(basename); + } +#endif + if (!basename) { + return 0; + } + + if (!basename_len) { + efree(basename); + return 0; + } + bytesread = MIN(sizeof(ent->d_name) - 1, basename_len); + memcpy(ent->d_name, basename, bytesread); + ent->d_name[bytesread] = 0; + efree(basename); + + return sizeof(php_stream_dirent); +} +/* }}} */ + +/* {{{ php_ssh2_sftp_dirstream_close + */ +static int php_ssh2_sftp_dirstream_close(php_stream *stream, int close_handle TSRMLS_DC) +{ + php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data*)stream->abstract; + + libssh2_sftp_close(data->handle); + zend_list_delete(data->sftp_rsrcid); + efree(data); + + return 0; +} +/* }}} */ + +static php_stream_ops php_ssh2_sftp_dirstream_ops = { + NULL, /* write */ + php_ssh2_sftp_dirstream_read, + php_ssh2_sftp_dirstream_close, + NULL, /* flush */ + PHP_SSH2_SFTP_DIRSTREAM_NAME, + NULL, /* seek */ + NULL, /* cast */ + NULL, /* fstat */ + NULL, /* set_option */ +}; + +/* {{{ php_ssh2_sftp_dirstream_opener + */ +static php_stream *php_ssh2_sftp_dirstream_opener( php_stream_wrapper *wrapper, char *filename, char *mode, + int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) +{ + php_ssh2_sftp_handle_data *data; + LIBSSH2_SESSION *session = NULL; + LIBSSH2_SFTP *sftp = NULL; + LIBSSH2_SFTP_HANDLE *handle; + php_stream *stream; + int resource_id = 0, sftp_rsrcid = 0; + php_url *resource; + + resource = php_ssh2_fopen_wraper_parse_path(filename, "sftp", context, &session, &resource_id, &sftp, &sftp_rsrcid TSRMLS_CC); + if (!resource || !session || !sftp) { + return NULL; + } + + handle = libssh2_sftp_opendir(sftp, resource->path); + if (!handle) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to open %s on remote host", filename); + php_url_free(resource); + zend_list_delete(sftp_rsrcid); + return NULL; + } + + data = emalloc(sizeof(php_ssh2_sftp_handle_data)); + data->handle = handle; + data->sftp_rsrcid = sftp_rsrcid; + + stream = php_stream_alloc(&php_ssh2_sftp_dirstream_ops, data, 0, mode); + if (!stream) { + libssh2_sftp_close(handle); + zend_list_delete(sftp_rsrcid); + efree(data); + } + php_url_free(resource); + + return stream; +} +/* }}} */ + +/* **************** + * SFTP Wrapper * + **************** */ + +#ifdef ZEND_ENGINE_2 +/* {{{ php_ssh2_sftp_urlstat + */ +static int php_ssh2_sftp_urlstat(php_stream_wrapper *wrapper, char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC) +{ + LIBSSH2_SFTP_ATTRIBUTES attrs; + LIBSSH2_SESSION *session = NULL; + LIBSSH2_SFTP *sftp = NULL; + int resource_id = 0, sftp_rsrcid = 0; + php_url *resource; + + resource = php_ssh2_fopen_wraper_parse_path(url, "sftp", context, &session, &resource_id, &sftp, &sftp_rsrcid TSRMLS_CC); + if (!resource || !session || !sftp || !resource->path) { + return -1; + } + + if (libssh2_sftp_stat_ex(sftp, resource->path, strlen(resource->path), + (flags & PHP_STREAM_URL_STAT_LINK) ? LIBSSH2_SFTP_LSTAT : LIBSSH2_SFTP_STAT, &attrs)) { + php_url_free(resource); + zend_list_delete(sftp_rsrcid); + return -1; + } + + php_url_free(resource); + + /* parse_path addrefs the resource, but we're not holding on to it so we have to delref it before we leave */ + zend_list_delete(sftp_rsrcid); + + return php_ssh2_sftp_attr2ssb(ssb, &attrs); +} +/* }}} */ + +/* {{{ php_ssh2_sftp_unlink + */ +static int php_ssh2_sftp_unlink(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC) +{ + LIBSSH2_SESSION *session = NULL; + LIBSSH2_SFTP *sftp = NULL; + int resource_id = 0, sftp_rsrcid = 0; + php_url *resource; + int result; + + resource = php_ssh2_fopen_wraper_parse_path(url, "sftp", context, &session, &resource_id, &sftp, &sftp_rsrcid TSRMLS_CC); + if (!resource || !session || !sftp || !resource->path) { + if (resource) { + php_url_free(resource); + } + return 0; + } + + result = libssh2_sftp_unlink(sftp, resource->path); + php_url_free(resource); + + zend_list_delete(sftp_rsrcid); + + /* libssh2 uses 0 for success and the streams API uses 0 for failure, so invert */ + return (result == 0) ? -1 : 0; +} +/* }}} */ + +/* {{{ php_ssh2_sftp_rename + */ +static int php_ssh2_sftp_rename(php_stream_wrapper *wrapper, char *url_from, char *url_to, int options, php_stream_context *context TSRMLS_DC) +{ + LIBSSH2_SESSION *session = NULL; + LIBSSH2_SFTP *sftp = NULL; + int resource_id = 0, sftp_rsrcid = 0; + php_url *resource, *resource_to; + int result; + + if (strncmp(url_from, "ssh2.sftp://", sizeof("ssh2.sftp://") - 1) || + strncmp(url_to, "ssh2.sftp://", sizeof("ssh2.sftp://") - 1)) { + return 0; + } + + resource_to = php_url_parse(url_to); + if (!resource_to || !resource_to->path) { + if (resource_to) { + php_url_free(resource_to); + } + return 0; + } + + resource = php_ssh2_fopen_wraper_parse_path(url_from, "sftp", context, &session, &resource_id, &sftp, &sftp_rsrcid TSRMLS_CC); + if (!resource || !session || !sftp || !resource->path) { + if (resource) { + php_url_free(resource); + } + php_url_free(resource_to); + return 0; + } + + result = libssh2_sftp_rename(sftp, resource->path, resource_to->path); + php_url_free(resource); + php_url_free(resource_to); + + zend_list_delete(sftp_rsrcid); + + /* libssh2 uses 0 for success and the streams API uses 0 for failure, so invert */ + return (result == 0) ? -1 : 0; +} +/* }}} */ + +/* {{{ php_ssh2_sftp_mkdir + */ +static int php_ssh2_sftp_mkdir(php_stream_wrapper *wrapper, char *url, int mode, int options, php_stream_context *context TSRMLS_DC) +{ + LIBSSH2_SESSION *session = NULL; + LIBSSH2_SFTP *sftp = NULL; + int resource_id = 0, sftp_rsrcid = 0; + php_url *resource; + int result; + + resource = php_ssh2_fopen_wraper_parse_path(url, "sftp", context, &session, &resource_id, &sftp, &sftp_rsrcid TSRMLS_CC); + if (!resource || !session || !sftp || !resource->path) { + if (resource) { + php_url_free(resource); + } + return 0; + } + + if (options & PHP_STREAM_MKDIR_RECURSIVE) { + /* Just attempt to make every directory, some will fail, but we only care about the last success/failure */ + char *p = resource->path; + while ((p = strchr(p + 1, '/'))) { + libssh2_sftp_mkdir_ex(sftp, resource->path, p - resource->path, mode); + } + } + + result = libssh2_sftp_mkdir(sftp, resource->path, mode); + php_url_free(resource); + + zend_list_delete(sftp_rsrcid); + + /* libssh2 uses 0 for success and the streams API uses 0 for failure, so invert */ + return (result == 0) ? -1 : 0; +} +/* }}} */ + +/* {{{ php_ssh2_sftp_rmdir + */ +static int php_ssh2_sftp_rmdir(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC) +{ + LIBSSH2_SESSION *session = NULL; + LIBSSH2_SFTP *sftp = NULL; + int resource_id = 0, sftp_rsrcid = 0; + php_url *resource; + int result; + + resource = php_ssh2_fopen_wraper_parse_path(url, "sftp", context, &session, &resource_id, &sftp, &sftp_rsrcid TSRMLS_CC); + if (!resource || !session || !sftp || !resource->path) { + if (resource) { + php_url_free(resource); + } + return 0; + } + + result = libssh2_sftp_rmdir(sftp, resource->path); + php_url_free(resource); + + zend_list_delete(sftp_rsrcid); + + /* libssh2 uses 0 for success and the streams API uses 0 for failure, so invert */ + return (result == 0) ? -1 : 0; +} +/* }}} */ +#endif + +static php_stream_wrapper_ops php_ssh2_sftp_wrapper_ops = { + php_ssh2_sftp_stream_opener, + NULL, /* close */ + NULL, /* stat */ +#ifdef ZEND_ENGINE_2 + php_ssh2_sftp_urlstat, +#else + NULL, /* url_stat isn't actually functional prior to PHP5 */ +#endif + php_ssh2_sftp_dirstream_opener, + PHP_SSH2_SFTP_WRAPPER_NAME, +#ifdef ZEND_ENGINE_2 + php_ssh2_sftp_unlink, + php_ssh2_sftp_rename, + php_ssh2_sftp_mkdir, + php_ssh2_sftp_rmdir, +#endif +}; + +php_stream_wrapper php_ssh2_sftp_wrapper = { + &php_ssh2_sftp_wrapper_ops, + NULL, + 1, + 0, + NULL, +}; + +/* ***************** + * Userspace API * + ***************** */ + + +/* {{{ proto resource ssh2_sftp(resource session) + * Request the SFTP subsystem from an already connected SSH2 server + */ +PHP_FUNCTION(ssh2_sftp) +{ + LIBSSH2_SESSION *session; + LIBSSH2_SFTP *sftp; + php_ssh2_sftp_data *data; + zval *zsession; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zsession) == FAILURE) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); + + sftp = libssh2_sftp_init(session); + if (!sftp) { + char *sess_err = "Unknown"; + + libssh2_session_last_error(session, &sess_err, NULL, 0); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to startup SFTP subsystem: %s", sess_err); + RETURN_FALSE; + } + + data = emalloc(sizeof(php_ssh2_sftp_data)); + data->session = session; + data->sftp = sftp; + data->session_rsrcid = Z_LVAL_P(zsession); + zend_list_addref(Z_LVAL_P(zsession)); + + ZEND_REGISTER_RESOURCE(return_value, data, le_ssh2_sftp); +} +/* }}} */ + +/* Much of the stuff below can be done via wrapper ops as of PHP5, but is included here for PHP 4.3 users */ + +/* {{{ proto bool ssh2_sftp_rename(resource sftp, string from, string to) + */ +PHP_FUNCTION(ssh2_sftp_rename) +{ + php_ssh2_sftp_data *data; + zval *zsftp; + char *src, *dst; + int src_len, dst_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rss", &zsftp, &src, &src_len, &dst, &dst_len) == FAILURE) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(data, php_ssh2_sftp_data*, &zsftp, -1, PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp); + + RETURN_BOOL(!libssh2_sftp_rename_ex(data->sftp, src, src_len, dst, dst_len, + LIBSSH2_SFTP_RENAME_OVERWRITE | LIBSSH2_SFTP_RENAME_ATOMIC | LIBSSH2_SFTP_RENAME_NATIVE)); +} +/* }}} */ + +/* {{{ proto bool ssh2_sftp_unlink(resource sftp, string filename) + */ +PHP_FUNCTION(ssh2_sftp_unlink) +{ + php_ssh2_sftp_data *data; + zval *zsftp; + char *filename; + int filename_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &zsftp, &filename, &filename_len) == FAILURE) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(data, php_ssh2_sftp_data*, &zsftp, -1, PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp); + + RETURN_BOOL(!libssh2_sftp_unlink_ex(data->sftp, filename, filename_len)); +} +/* }}} */ + +/* {{{ proto bool ssh2_sftp_mkdir(resource sftp, string filename[, int mode[, int recursive]]) + */ +PHP_FUNCTION(ssh2_sftp_mkdir) +{ + php_ssh2_sftp_data *data; + zval *zsftp; + char *filename; + int filename_len; + long mode = 0777; + zend_bool recursive = 0; + char *p; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|lb", &zsftp, &filename, &filename_len, &mode, &recursive) == FAILURE) { + RETURN_FALSE; + } + + if (filename_len < 1) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(data, php_ssh2_sftp_data*, &zsftp, -1, PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp); + + if (recursive) { + /* Just attempt to make every directory, some will fail, but we only care about the last success/failure */ + p = filename; + while ((p = strchr(p + 1, '/'))) { + if ((p - filename) + 1 == filename_len) { + break; + } + libssh2_sftp_mkdir_ex(data->sftp, filename, p - filename, mode); + } + } + + + RETURN_BOOL(!libssh2_sftp_mkdir_ex(data->sftp, filename, filename_len, mode)); +} +/* }}} */ + +/* {{{ proto bool ssh2_sftp_rmdir(resource sftp, string filename) + */ +PHP_FUNCTION(ssh2_sftp_rmdir) +{ + php_ssh2_sftp_data *data; + zval *zsftp; + char *filename; + int filename_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &zsftp, &filename, &filename_len) == FAILURE) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(data, php_ssh2_sftp_data*, &zsftp, -1, PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp); + + RETURN_BOOL(!libssh2_sftp_rmdir_ex(data->sftp, filename, filename_len)); +} +/* }}} */ + +/* {{{ php_ssh2_sftp_stat_func + * In PHP4.3 this is the only way to request stat into, in PHP >= 5 you can use the fopen wrapper approach + * Both methods will return identical structures + * (well, the other one will include other values set to 0 but they don't count) + */ +static void php_ssh2_sftp_stat_func(INTERNAL_FUNCTION_PARAMETERS, int stat_type) +{ + php_ssh2_sftp_data *data; + LIBSSH2_SFTP_ATTRIBUTES attrs; + zval *zsftp; + char *path; + int path_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &zsftp, &path, &path_len) == FAILURE) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(data, php_ssh2_sftp_data*, &zsftp, -1, PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp); + + if (libssh2_sftp_stat_ex(data->sftp, path, path_len, stat_type, &attrs)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to stat remote file"); + RETURN_FALSE; + } + + array_init(return_value); + + if (attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) { + add_index_long(return_value, 7, attrs.filesize); + add_assoc_long(return_value, "size", attrs.filesize); + } + if (attrs.flags & LIBSSH2_SFTP_ATTR_UIDGID) { + add_index_long(return_value, 4, attrs.uid); + add_assoc_long(return_value, "uid", attrs.uid); + + add_index_long(return_value, 5, attrs.gid); + add_assoc_long(return_value, "gid", attrs.gid); + } + if (attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) { + add_index_long(return_value, 2, attrs.permissions); + add_assoc_long(return_value, "mode", attrs.permissions); + } + if (attrs.flags & LIBSSH2_SFTP_ATTR_ACMODTIME) { + add_index_long(return_value, 8, attrs.atime); + add_assoc_long(return_value, "atime", attrs.atime); + + add_index_long(return_value, 9, attrs.mtime); + add_assoc_long(return_value, "mtime", attrs.mtime); + } +} +/* }}} */ + +/* {{{ proto array ssh2_sftp_stat(resource sftp, string path) + */ +PHP_FUNCTION(ssh2_sftp_stat) +{ + php_ssh2_sftp_stat_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, LIBSSH2_SFTP_STAT); +} +/* }}} */ + +/* {{{ proto array ssh2_sftp_lstat(resource sftp, string path) + */ +PHP_FUNCTION(ssh2_sftp_lstat) +{ + php_ssh2_sftp_stat_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, LIBSSH2_SFTP_LSTAT); +} +/* }}} */ + +/* {{{ proto bool ssh2_sftp_symlink(resource sftp, string target, string link) + */ +PHP_FUNCTION(ssh2_sftp_symlink) +{ + php_ssh2_sftp_data *data; + zval *zsftp; + char *targ, *link; + int targ_len, link_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rss", &zsftp, &targ, &targ_len, &link, &link_len) == FAILURE) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(data, php_ssh2_sftp_data*, &zsftp, -1, PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp); + + RETURN_BOOL(!libssh2_sftp_symlink_ex(data->sftp, targ, targ_len, link, link_len, LIBSSH2_SFTP_SYMLINK)); +} +/* }}} */ + +/* {{{ proto string ssh2_sftp_readlink(resource sftp, string link) + */ +PHP_FUNCTION(ssh2_sftp_readlink) +{ + php_ssh2_sftp_data *data; + zval *zsftp; + char *link; + int targ_len = 0, link_len; + char targ[8192]; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &zsftp, &link, &link_len) == FAILURE) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(data, php_ssh2_sftp_data*, &zsftp, -1, PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp); + + if ((targ_len = libssh2_sftp_symlink_ex(data->sftp, link, link_len, targ, 8192, LIBSSH2_SFTP_READLINK)) < 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to read link '%s'", link); + RETURN_FALSE; + } + + RETURN_STRINGL(targ, targ_len, 1); +} +/* }}} */ + +/* {{{ proto string ssh2_sftp_realpath(resource sftp, string filename) + */ +PHP_FUNCTION(ssh2_sftp_realpath) +{ + php_ssh2_sftp_data *data; + zval *zsftp; + char *link; + int targ_len = 0, link_len; + char targ[8192]; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &zsftp, &link, &link_len) == FAILURE) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(data, php_ssh2_sftp_data*, &zsftp, -1, PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp); + + if ((targ_len = libssh2_sftp_symlink_ex(data->sftp, link, link_len, targ, 8192, LIBSSH2_SFTP_REALPATH)) < 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to resolve realpath for '%s'", link); + RETURN_FALSE; + } + + RETURN_STRINGL(targ, targ_len, 1); +} +/* }}} */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * indent-tabs-mode: t + * End: + */ +