package.xml 0000664 0001750 0001750 00000013233 12037051456 012516 0 ustar casper casper
ssh2
pecl.php.net
Bindings for the libssh2 library
Provides bindings to the functions of libssh2 which implements the SSH2 protocol.
libssh2 is available from http:/libssh2.org/
Casper Langemeijer
langemeijer
langemeijer@php.net
yes
Pierre Joye
pajoye
pierre@php.net
yes
Mike Sullivan
mikesul
mikesul@php.net
no
Sara Golemon
pollita
pollita@php.net
no
2012-10-15
0.12
0.12
beta
beta
PHP License
- Bumped libssh2 version requirement to 1.2 (aug 2009)
- Added ssh2_auth_agent() - SSH agent authentication support (with libssh >= 1.2.3)
- Added ssh2_sftp_chmod() (fixed bug #59880)
- Added support for stream_set_timeout() to channel streams (fixed bug #56377) (with libssh >= 1.2.9)
- Added keyboard-interactive to ssh2_auth_password() (fixed bugs #61760 and #54916)
- Add license file as requested in bug #59980
- Allow for tilde (~) in public and private keyfile paths
- Be more verbose about any errors within ssh2_scp_send
- Fixed bug #56425 - Problem with ssh2_scp_send
- Fixed bug #59794 - ssh2.sftp protocol wrapper works incorrectly for paths which contain a '#'
- Fixed bug #63192 - Check if connection is authenticated.
- Fixed bug #58576 - Handle error values from reads and writes to a channel.
4.0.0
6.0.0
1.4.0
ssh2
2011-09-22
0.11.3
0.11.0
- 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)
2009-11-28
0.11.1
0.11.0
beta
beta
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
- Fix build with PHP 5.3
- Fixed incorrect return values for rename/unlink/mkdir/rmdir with sftp functions/streams
- Fixed various memory leaks and segfaults
2009-11-28
0.11.1-dev
0.11.0
beta
beta
PHP License
- Support for latest libssh2 release
- Fix build with PHP 5.3
- Fixed incorrect return values for rename/unlink/mkdir/rmdir with sftp functions/streams
- Fixed various memory leaks and segfaults
2008-12-08
0.11.0
0.11.0
beta
beta
PHP License
- Support for latest libssh2 release
- Fix build with PHP 5.3
- Fixed incorrect return values for rename/unlink/mkdir/rmdir with sftp functions/streams
- Fixed various memory leaks and segfaults
ssh2-0.12/config.m4 0000664 0001750 0001750 00000003342 12034033664 013343 0 ustar casper casper dnl $Id: config.m4 327906 2012-10-06 13:52:51Z langemeijer $
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)
PHP_CHECK_LIBRARY(ssh2,libssh2_session_hostkey,
[
PHP_ADD_LIBRARY_WITH_PATH(ssh2, $SSH2_DIR/lib, SSH2_SHARED_LIBADD)
AC_DEFINE(HAVE_SSH2LIB,1,[Have libssh2])
],[
AC_MSG_ERROR([libssh2 version >= 1.2 not found])
],[
-L$SSH2_DIR/lib -lm
])
PHP_CHECK_LIBRARY(ssh2,libssh2_agent_init,
[
AC_DEFINE(PHP_SSH2_AGENT_AUTH, 1, [Have libssh2 with ssh-agent support])
],[
AC_MSG_WARN([libssh2 <= 1.2.3, ssh-agent subsystem support not enabled])
],[ ],[
-L$SSH2_DIR/lib -lm -L$SSH2_DIR/lib -lm
])
PHP_CHECK_LIBRARY(ssh2,libssh2_session_set_timeout,
[
AC_DEFINE(PHP_SSH2_SESSION_TIMEOUT, 1, [Have libssh2 with session timeout support])
],[
AC_MSG_WARN([libssh2 < 1.2.9, session timeout 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
ssh2-0.12/ssh2.c 0000664 0001750 0001750 00000130533 12034033664 012662 0 ustar casper casper /*
+----------------------------------------------------------------------+
| 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 327906 2012-10-06 13:52:51Z langemeijer $
*/
#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;
int le_ssh2_listener;
int le_ssh2_sftp;
int le_ssh2_pkey_subsys;
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;
}
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;
}
ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session);
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);
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;
}
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;
}
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);
}
}
/* }}} */
char *password_for_kbd_callback;
static void kbd_callback(const char *name, int name_len,
const char *instruction, int instruction_len,
int num_prompts,
const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts,
LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses,
void **abstract)
{
(void)name;
(void)name_len;
(void)instruction;
(void)instruction_len;
if (num_prompts == 1) {
responses[0].text = estrdup(password_for_kbd_callback);
responses[0].length = strlen(password_for_kbd_callback);
}
(void)prompts;
(void)abstract;
}
/* {{{ 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;
char *userauthlist;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rss", &zsession, &username, &username_len, &password, &password_len) == FAILURE) {
return;
}
SSH2_FETCH_NONAUTHENTICATED_SESSION(session, zsession);
userauthlist = libssh2_userauth_list(session, username, username_len);
password_for_kbd_callback = password;
if (strstr(userauthlist, "keyboard-interactive") != NULL) {
if (libssh2_userauth_keyboard_interactive(session, username, &kbd_callback) == 0) {
RETURN_TRUE;
}
}
/* 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;
char *newpath;
struct passwd *pws;
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;
}
if (SSH2_OPENBASEDIR_CHECKPATH(pubkey) || SSH2_OPENBASEDIR_CHECKPATH(privkey)) {
RETURN_FALSE;
}
SSH2_FETCH_NONAUTHENTICATED_SESSION(session, zsession);
// Explode '~/paths' stopgap fix because libssh2 does not accept tilde for homedir
// This should be ifdef'ed when a fix is available to support older libssh2 versions
pws = getpwuid(geteuid());
if (pubkey_len >= 2 && *pubkey == '~' && *(pubkey+1) == '/') {
newpath = emalloc(strlen(pws->pw_dir) + strlen(pubkey));
strcpy(newpath, pws->pw_dir);
strcat(newpath, pubkey+1);
efree(pubkey);
pubkey = newpath;
}
if (privkey_len >= 2 && *privkey == '~' && *(privkey+1) == '/') {
newpath = emalloc(strlen(pws->pw_dir) + strlen(privkey));
strcpy(newpath, pws->pw_dir);
strcat(newpath, privkey+1);
efree(privkey);
privkey = newpath;
}
/* 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;
}
/* }}} */
/* {{{ 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;
}
if (SSH2_OPENBASEDIR_CHECKPATH(pubkey) || SSH2_OPENBASEDIR_CHECKPATH(privkey)) {
RETURN_FALSE;
}
SSH2_FETCH_NONAUTHENTICATED_SESSION(session, zsession);
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;
}
/* }}} */
/* {{{ 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;
}
SSH2_FETCH_AUTHENTICATED_SESSION(session, zsession);
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;
}
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);
}
/* }}} */
/* {{{ 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;
}
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);
}
/* }}} */
/* ***********************
* 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;
}
SSH2_FETCH_AUTHENTICATED_SESSION(session, zsession);
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;
}
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;
}
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;
}
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);
}
/* }}} */
/* {{{ proto array ssh2_auth_agent(resource session, string username)
Authenticate using the ssh agent */
PHP_FUNCTION(ssh2_auth_agent)
{
#ifdef PHP_SSH2_AGENT_AUTH
zval *zsession;
char *username;
int username_len;
LIBSSH2_SESSION *session;
char *userauthlist;
LIBSSH2_AGENT *agent = NULL;
int rc;
struct libssh2_agent_publickey *identity, *prev_identity = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &zsession, &username, &username_len) == FAILURE) {
return;
}
SSH2_FETCH_NONAUTHENTICATED_SESSION(session, zsession);
/* check what authentication methods are available */
userauthlist = libssh2_userauth_list(session, username, username_len);
if (strstr(userauthlist, "publickey") == NULL) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "\"publickey\" authentication is not supported");
RETURN_FALSE;
}
/* Connect to the ssh-agent */
agent = libssh2_agent_init(session);
if (!agent) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failure initializing ssh-agent support");
RETURN_FALSE;
}
if (libssh2_agent_connect(agent)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failure connecting to ssh-agent");
libssh2_agent_free(agent);
RETURN_FALSE;
}
if (libssh2_agent_list_identities(agent)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failure requesting identities to ssh-agent");
libssh2_agent_disconnect(agent);
libssh2_agent_free(agent);
RETURN_FALSE;
}
while (1) {
rc = libssh2_agent_get_identity(agent, &identity, prev_identity);
if (rc == 1) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Couldn't continue authentication");
libssh2_agent_disconnect(agent);
libssh2_agent_free(agent);
RETURN_FALSE;
}
if (rc < 0) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failure obtaining identity from ssh-agent support");
libssh2_agent_disconnect(agent);
libssh2_agent_free(agent);
RETURN_FALSE;
}
if (!libssh2_agent_userauth(agent, username, identity)) {
libssh2_agent_disconnect(agent);
libssh2_agent_free(agent);
RETURN_TRUE;
}
prev_identity = identity;
}
#else
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Upgrade the libssh2 library (needs 1.2.3 or higher) and reinstall the ssh2 extension for ssh2 agent support");
RETURN_FALSE;
#endif /* PHP_SSH2_AGENT_AUTH */
}
/* }}} */
/* ***********************
* 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);
}
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);
}
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);
}
/* {{{ 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);
le_ssh2_listener = zend_register_list_destructors_ex(php_ssh2_listener_dtor, NULL, PHP_SSH2_LISTENER_RES_NAME, module_number);
le_ssh2_sftp = zend_register_list_destructors_ex(php_ssh2_sftp_dtor, NULL, PHP_SSH2_SFTP_RES_NAME, module_number);
le_ssh2_pkey_subsys = zend_register_list_destructors_ex(php_ssh2_pkey_subsys_dtor, NULL, PHP_SSH2_PKEY_SUBSYS_RES_NAME, module_number);
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);
/* 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);
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);
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);
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)
PHP_FE(ssh2_auth_hostbased_file, NULL)
PHP_FE(ssh2_forward_listen, NULL)
PHP_FE(ssh2_forward_accept, NULL)
/* 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)
PHP_FE(ssh2_poll, php_ssh2_first_arg_force_ref)
/* 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_chmod, 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 */
PHP_FE(ssh2_publickey_init, NULL)
PHP_FE(ssh2_publickey_add, NULL)
PHP_FE(ssh2_publickey_remove, NULL)
PHP_FE(ssh2_publickey_list, NULL)
PHP_FE(ssh2_auth_agent, NULL)
{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
*/
ssh2-0.12/php_ssh2.h 0000664 0001750 0001750 00000016250 12037050060 013525 0 ustar casper casper /*
+----------------------------------------------------------------------+
| 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 328041 2012-10-15 18:04:32Z langemeijer $
*/
#ifndef PHP_SSH2_H
#define PHP_SSH2_H
#include
#include
#include "ext/standard/url.h"
#define PHP_SSH2_VERSION "0.12"
#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;
typedef struct _php_ssh2_listener_data {
LIBSSH2_SESSION *session;
LIBSSH2_LISTENER *listener;
int session_rsrcid;
} php_ssh2_listener_data;
#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;
#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
#define SSH2_FETCH_NONAUTHENTICATED_SESSION(session, zsession) \
ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); \
if (libssh2_userauth_authenticated(session)) { \
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connection already authenticated"); \
RETURN_FALSE; \
}
#define SSH2_FETCH_AUTHENTICATED_SESSION(session, zsession) \
ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); \
if (!libssh2_userauth_authenticated(session)) { \
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connection not authenticated"); \
RETURN_FALSE; \
}
typedef struct _php_ssh2_channel_data {
LIBSSH2_CHANNEL *channel;
/* Distinguish which stream we should read/write from/to */
unsigned int streamid;
char is_blocking;
long timeout;
/* 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_chmod);
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:
*/
ssh2-0.12/ssh2_fopen_wrappers.c 0000664 0001750 0001750 00000115044 12032645777 016010 0 ustar casper casper /*
+----------------------------------------------------------------------+
| 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 327861 2012-10-02 20:18:07Z langemeijer $
*/
#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;
size_t writestate;
LIBSSH2_SESSION *session;
libssh2_channel_set_blocking(abstract->channel, abstract->is_blocking);
session = (LIBSSH2_SESSION *)zend_fetch_resource(NULL TSRMLS_CC, abstract->session_rsrc, PHP_SSH2_SESSION_RES_NAME, NULL, 1, le_ssh2_session);
#ifdef PHP_SSH2_SESSION_TIMEOUT
if (abstract->is_blocking) {
libssh2_session_set_timeout(session, abstract->timeout);
}
#endif
writestate = libssh2_channel_write_ex(abstract->channel, abstract->streamid, buf, count);
#ifdef PHP_SSH2_SESSION_TIMEOUT
if (abstract->is_blocking) {
libssh2_session_set_timeout(session, 0);
}
#endif
if (writestate == LIBSSH2_ERROR_EAGAIN) {
writestate = 0;
}
if (writestate < 0) {
char *error_msg = NULL;
if (libssh2_session_last_error(session, &error_msg, NULL, 0) == writestate) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failure '%s' (%ld)", error_msg, writestate);
}
stream->eof = 1;
writestate = 0;
}
return writestate;
}
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;
LIBSSH2_SESSION *session;
stream->eof = libssh2_channel_eof(abstract->channel);
libssh2_channel_set_blocking(abstract->channel, abstract->is_blocking);
session = (LIBSSH2_SESSION *)zend_fetch_resource(NULL TSRMLS_CC, abstract->session_rsrc, PHP_SSH2_SESSION_RES_NAME, NULL, 1, le_ssh2_session);
#ifdef PHP_SSH2_SESSION_TIMEOUT
if (abstract->is_blocking) {
libssh2_session_set_timeout(session, abstract->timeout);
}
#endif
readstate = libssh2_channel_read_ex(abstract->channel, abstract->streamid, buf, count);
#ifdef PHP_SSH2_SESSION_TIMEOUT
if (abstract->is_blocking) {
libssh2_session_set_timeout(session, 0);
}
#endif
if (readstate == LIBSSH2_ERROR_EAGAIN) {
readstate = 0;
}
if (readstate < 0) {
char *error_msg = NULL;
if (libssh2_session_last_error(session, &error_msg, NULL, 0) == readstate) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failure '%s' (%ld)", error_msg, readstate);
}
stream->eof = 1;
readstate = 0;
}
return 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;
case PHP_STREAM_OPTION_READ_TIMEOUT:
ret = abstract->timeout;
#ifdef PHP_SSH2_SESSION_TIMEOUT
struct timeval tv = *(struct timeval*)ptrparam;
abstract->timeout = tv.tv_sec * 1000 + (tv.tv_usec / 1000);
#else
php_error_docref(NULL TSRMLS_CC, E_WARNING, "No support for ssh2 stream timeout. Please recompile with libssh2 >= 1.2.9");
#endif
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;
}
/*
Find resource->path in the path string, then copy the entire string from the original path.
This includes ?query#fragment in the path string
*/
s = resource->path;
resource->path = estrdup(strstr(path, resource->path));
efree(s);
/* 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->timeout = 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 parameter");
RETURN_FALSE;
}
if (zend_parse_parameters(argc TSRMLS_CC, "r|sa!lll", &zsession, &term, &term_len, &environment, &width, &height, &type) == FAILURE) {
return;
}
SSH2_FETCH_AUTHENTICATED_SESSION(session, zsession);
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->timeout = 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;
}
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);
}
SSH2_FETCH_AUTHENTICATED_SESSION(session, zsession);
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->timeout = 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;
}
SSH2_FETCH_AUTHENTICATED_SESSION(session, zsession);
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;
}
SSH2_FETCH_AUTHENTICATED_SESSION(session, zsession);
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;
}
size_t sent = 0;
size_t justsent = 0;
while (bytesread - sent > 0) {
if ((justsent = libssh2_channel_write(remote_file, (buffer + sent), bytesread - sent)) < 0) {
switch (justsent) {
case LIBSSH2_ERROR_EAGAIN:
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Operation would block");
break;
case LIBSSH2_ERROR_ALLOC:
php_error_docref(NULL TSRMLS_CC,E_WARNING, "An internal memory allocation call failed");
break;
case LIBSSH2_ERROR_SOCKET_SEND:
php_error_docref(NULL TSRMLS_CC,E_WARNING, "Unable to send data on socket");
break;
case LIBSSH2_ERROR_CHANNEL_CLOSED:
php_error_docref(NULL TSRMLS_CC,E_WARNING, "The channel has been closed");
break;
case LIBSSH2_ERROR_CHANNEL_EOF_SENT:
php_error_docref(NULL TSRMLS_CC,E_WARNING, "The channel has been requested to be closed");
break;
}
php_stream_close(local_file);
libssh2_channel_free(remote_file);
RETURN_FALSE;
}
sent = sent + justsent;
}
ssb.sb.st_size -= bytesread;
}
libssh2_channel_flush_ex(remote_file, LIBSSH2_CHANNEL_FLUSH_ALL);
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->timeout = 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;
}
SSH2_FETCH_AUTHENTICATED_SESSION(session, zsession);
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;
}
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
*/
ssh2-0.12/ssh2_sftp.c 0000664 0001750 0001750 00000056354 12032563225 013725 0 ustar casper casper /*
+----------------------------------------------------------------------+
| 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 326206 2012-06-17 20:54:01Z langemeijer $
*/
#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;
}
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;
}
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;
}
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;
}
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;
}
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));
}
/* }}} */
/* {{{ proto bool ssh2_sftp_chmod(resource sftp, string filename, int mode)
*/
PHP_FUNCTION(ssh2_sftp_chmod)
{
php_ssh2_sftp_data *data;
zval *zsftp;
char *filename;
int filename_len;
long mode;
LIBSSH2_SFTP_ATTRIBUTES attrs;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsl", &zsftp, &filename, &filename_len, &mode) == FAILURE) {
return;
}
if (filename_len < 1) {
RETURN_FALSE;
}
ZEND_FETCH_RESOURCE(data, php_ssh2_sftp_data*, &zsftp, -1, PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp);
attrs.permissions = mode;
attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS;
RETURN_BOOL(!libssh2_sftp_stat_ex(data->sftp, filename, filename_len, LIBSSH2_SFTP_SETSTAT, &attrs));
}
/* }}} */
/* {{{ 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;
}
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;
}
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;
}
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;
}
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:
*/
ssh2-0.12/LICENSE 0000664 0001750 0001750 00000006221 11765640034 012645 0 ustar casper casper --------------------------------------------------------------------
The PHP License, version 3.01
Copyright (c) 1999 - 2012 The PHP Group. All rights reserved.
--------------------------------------------------------------------
Redistribution and use in source and binary forms, with or without
modification, is permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
3. The name "PHP" must not be used to endorse or promote products
derived from this software without prior written permission. For
written permission, please contact group@php.net.
4. Products derived from this software may not be called "PHP", nor
may "PHP" appear in their name, without prior written permission
from group@php.net. You may indicate that your software works in
conjunction with PHP by saying "Foo for PHP" instead of calling
it "PHP Foo" or "phpfoo"
5. The PHP Group may publish revised and/or new versions of the
license from time to time. Each version will be given a
distinguishing version number.
Once covered code has been published under a particular version
of the license, you may always continue to use it under the terms
of that version. You may also choose to use such covered code
under the terms of any subsequent version of the license
published by the PHP Group. No one other than the PHP Group has
the right to modify the terms applicable to covered code created
under this License.
6. Redistributions of any form whatsoever must retain the following
acknowledgment:
"This product includes PHP software, freely available from
".
THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND
ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP
DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------
This software consists of voluntary contributions made by many
individuals on behalf of the PHP Group.
The PHP Group can be contacted via Email at group@php.net.
For more information on the PHP Group and the PHP project,
please see .
PHP includes the Zend Engine, freely available at
.