package.xml 0000644 0001750 0001750 00000025542 14561537332 012500 0 ustar derick derick
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
2024-02-10
1.4.1
1.0
stable
stable
PHP License
- End zend_function_entry ssh2_functions list with PHP_FE_END [PR #67] (Gerdriaan Mulder)
- Remove implicit switch-case fallthrough [PR #66] (Gerdriaan Mulder)
- Windows build setup with GitHub Actions [PR #75] (Derick Rethans)
7.0.0
1.4.0
ssh2
2023-04-15
1.4
1.0
- Add ssh2_shell_resize function [PR #55] (jentian)
- Add ssh2_auth_pubkey to allow public and private keys as strings [PR #56] (Andreas Treichel)
2021-03-02
1.3
1.0
- 1.3.1 release only fixed the PHP >= 8.0 requirement in the package.xml. No code changes.
Changes in 1.3:
- Add ssh2_send_eof() [PR #45] (Calvin Buckley)
- PHP stream cast function for SSH channel objects for stream_select() support. [PR #38] (Robert Wolf)
- Fix for PHP 7.4 and 8 [PR #44] (Andy Postnikov and Remi Collet)
- Fixed debug and disconnected callback [PR #37] (Piotr Rogowski)
- Various stability and memory issue fixes [PR #39, #41] (Robert Wolf)
- Segfault fix for bug #79757 [PR #43] (Konrad K)
- Various stability and bug fixes #79631, #63480, #80294, #75523, #80729 [PR #46, #47, #48, #49, #50 and #51] (Christoph M. Becker)
2019-09-17
1.2
1.0
- Fix multiple reflection/documentation disparities [PR #29] (Michael Moravec)
- Fix PHP7 port. Restored commented reference counting. [Dmitry Stogov]
- Updates for php_url structure changes [Daniel Ciochiu]
- Make the PR generic for all PHP7 versions [Jan Ehrhardt]
- Fix compatibility with PHP 7.3 [Jan Ehrhardt]
- Fix config.w32 for compatibility with OpenSSL 1.1.x [Anatol]
- Make static inline for php_ssh2_sftp_attr2ssb [Andy Postnikov]
- Enable 7.1-7.3 on Travis CI [Andy Postnikov]
2017-06-14
1.1
1.0
- Fixed bug #72988 (Libor M.)
- Fixed bug #73198 (Langemeijer)
- Fixed php_url_parse issue (Remi Collet)
- fix Invalid write of size 1 (Remi Collet)
- Fixed bug #73819 (Mitch Hagstrand)
- Fixed bug #73410 (Mitch Hagstrand)
- Travis CI (Mitch Hagstrand)
- Various other fixes on PHP 7 code and code cleanup (Mitch Hagstrand, Libor M., Anatol Belski)
2016-06-12
1.0
1.0
- Release for PHP 7 (Sean DuBois)
- Made win32 builds depend on zlib and openssl extensions (Credomane Evonguard)
- Add blocking call to php_ssh2_direct_tcpip (Credomane Evonguard)
- Added explicit ssh2_disconnect function (Credomane Evonguard)
- Fixed bug #72150 - Fixing segfault when passing env variables (Krakjoe)
2016-06-12
0.13
0.13
- Fixed bug #63660 php_ssh2_fopen_wrapper_parse_path segfaults
- Fixed bug #63855 compilation fails on windows (patch from Erez H)
- Fixed bug #64535 php_ssh2_sftp_dirstream_read segfault on error (Matt Pelmear)
- Add reflection API support (Frédéric Hardy)
- Add exit-status support for ssh2 file wrappers (Vladimir Zidar)
- Fixed bug #58893 Unable to request a channel from remote host (Vladimir Zidar)
- Fix segfault when trying to authenticate in servers that do not support authentication (none) (Felipe Weckx)
2012-10-15
0.12
0.12
- 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.
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-1.4.1/tests/bug63480.phpt 0000644 0001750 0001750 00000000736 14561537332 015120 0 ustar derick derick --TEST--
Bug #63480 (Warning on using the SSH2 Session resource in the uri)
--SKIPIF--
--FILE--
--EXPECT--
yada yada
ssh2-1.4.1/tests/bug79631.phpt 0000644 0001750 0001750 00000000552 14561537332 015121 0 ustar derick derick --TEST--
Bug 79631 (SSH disconnect segfault with SFTP (assertion failed))
--SKIPIF--
--FILE--
--EXPECT--
done
ssh2-1.4.1/tests/ssh2_auth.phpt 0000644 0001750 0001750 00000000433 14561537332 015630 0 ustar derick derick --TEST--
ssh2_auth_FOO() - Attempt to authenticate to a remote host
--SKIPIF--
--FILE--
--FILE--
--FILE--
--FILE--
0);
}
echo "**Negotiation\n";
$mn = ssh2_methods_negotiated($ssh);
var_dump(ssh2t_strset($mn['kex']));
var_dump(ssh2t_strset($mn['hostkey']));
foreach(array('client_to_server', 'server_to_client') as $direction) {
$mnd = $mn[$direction];
var_dump(ssh2t_strset($mnd['crypt']));
var_dump(ssh2t_strset($mnd['comp']));
var_dump(ssh2t_strset($mnd['mac']));
}
--EXPECT--
**Connect
bool(true)
string(12) "SSH2 Session"
**Fingerprint MD5
bool(true)
int(32)
bool(true)
**Fingerprint SHA1
bool(true)
int(40)
bool(true)
**Negotiation
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
ssh2-1.4.1/tests/ssh2_exec.phpt 0000644 0001750 0001750 00000000744 14561537332 015620 0 ustar derick derick --TEST--
ssh2_shell_test() - Tests opening a shell
--SKIPIF--
--FILE--
--FILE--
--FILE--
--FILE--
0) {
while($line = fgets($shell)) {
echo $line;
}
}
$elapsed = time() - $start;
var_dump(($elapsed < $timeout));
--EXPECTF--
bool(true)
resource(%d) of type (stream)
%a
%a
%a
howdy
%sbool(true) ssh2-1.4.1/tests/ssh2_test.inc 0000644 0001750 0001750 00000002222 14561537332 015442 0 ustar derick derick = 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
])
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-1.4.1/config.w32 0000644 0001750 0001750 00000002670 14561537332 013500 0 ustar derick derick // vim:ft=javascript
ARG_WITH("ssh2", "SSH2 support", "no");
if (PHP_SSH2 != "no") {
var ssh2_all_ok = true;
if (!CHECK_LIB("libssh2_a.lib;libssh2.lib", "ssh2", PHP_SSH2)){
WARNING("ssh2 not enabled: libssh2 libraries not found");
ssh2_all_ok = false;
}
if (!CHECK_HEADER_ADD_INCLUDE("libssh2.h", "CFLAGS_SSH2", PHP_PHP_BUILD + "\\include\\libssh2")) {
WARNING("ssh2 not enabled: libssh2 headers not found");
ssh2_all_ok = false;
}
if (typeof SETUP_OPENSSL === "function") {
var ret = SETUP_OPENSSL("ssh2", PHP_PHP_BUILD + "\\lib");
if (ret < 1) {
WARNING("ssh2 support can't be enabled, openssl library not found")
ssh2_all_ok = false;
}
} else {
if (!CHECK_LIB("libeay32.lib", "ssh2", PHP_PHP_BUILD + "\\lib")) {
WARNING("ssh2 support can't be enabled, openssl library not found")
ssh2_all_ok = false;
}
}
if (!CHECK_LIB("zlib_a.lib", "ssh2", PHP_PHP_BUILD + "\\lib")) {
WARNING("ssh2 support can't be enabled, zlib library not found")
ssh2_all_ok = false;
}
if (ssh2_all_ok){
EXTENSION("ssh2", "ssh2.c ssh2_fopen_wrappers.c ssh2_sftp.c");
AC_DEFINE('HAVE_SSH2LIB', 1);
AC_DEFINE('PHP_SSH2_AGENT_AUTH', 1);
AC_DEFINE('PHP_SSH2_SESSION_TIMEOUT', 1);
ADD_EXTENSION_DEP('ssh2', 'zlib')
ADD_EXTENSION_DEP('ssh2', 'openssl')
}
}
ssh2-1.4.1/ssh2.c 0000644 0001750 0001750 00000145704 14561537332 012727 0 ustar derick derick /*
+----------------------------------------------------------------------+
| PHP Version 7 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2016 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.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 |
+----------------------------------------------------------------------+
*/
#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;
/* *************
* Callbacks *
************* */
/* {{{ 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 args[3];
if (!abstract || !*abstract) {
return;
}
data = (php_ssh2_session_data*)*abstract;
if (!data->debug_cb) {
return;
}
ZVAL_STRINGL(&args[0], message, message_len);
ZVAL_STRINGL(&args[1], language, language_len);
ZVAL_LONG(&args[2], always_display);
zval retval;
if (FAILURE == call_user_function(NULL, NULL, data->debug_cb, &retval, 3, args)) {
php_error_docref(NULL, E_WARNING, "Failure calling debug callback");
}
if (!Z_ISUNDEF(retval)) {
zval_ptr_dtor(&retval);
}
}
/* }}} */
/* {{{ php_ssh2_ignore_cb
* Ignore packets
*/
LIBSSH2_IGNORE_FUNC(php_ssh2_ignore_cb)
{
php_ssh2_session_data *data;
zval zretval;
zval args[1];
if (!abstract || !*abstract) {
return;
}
data = (php_ssh2_session_data*)*abstract;
if (!data->ignore_cb) {
return;
}
ZVAL_STRINGL(&args[0], message, message_len);
if (FAILURE == call_user_function(NULL, NULL, data->ignore_cb, &zretval, 1, args)) {
php_error_docref(NULL, E_WARNING, "Failure calling ignore callback");
}
if (Z_TYPE_P(&zretval) != IS_UNDEF) {
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;
zval args[1];
int retval = -1;
if (!abstract || !*abstract) {
return -1;
}
data = (php_ssh2_session_data*)*abstract;
if (!data->macerror_cb) {
return -1;
}
ZVAL_STRINGL(&args[0], packet, packet_len);
if (FAILURE == call_user_function(NULL, NULL, data->macerror_cb, &zretval, 1, args)) {
php_error_docref(NULL, E_WARNING, "Failure calling macerror callback");
} else {
retval = zval_is_true(&zretval) ? 0 : -1;
}
if (Z_TYPE_P(&zretval) != IS_UNDEF) {
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 args[3];
if (!abstract || !*abstract) {
return;
}
data = (php_ssh2_session_data*)*abstract;
if (!data->disconnect_cb) {
return;
}
ZVAL_LONG(&args[0], reason);
ZVAL_STRINGL(&args[1], message, message_len);
ZVAL_STRINGL(&args[2], language, language_len);
zval retval;
if (FAILURE == call_user_function(NULL, NULL, data->disconnect_cb, &retval, 3, args)) {
php_error_docref(NULL, E_WARNING, "Failure calling disconnect callback");
}
if (!Z_ISUNDEF(retval)) {
zval_ptr_dtor(&retval);
}
}
/* }}} */
/* *****************
* 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)
{
zval *handler, *copyval;
void *internal_handler;
zend_string *callback_zstring;
callback_zstring = zend_string_init(callback, callback_len, 0);
if ((handler = zend_hash_find(ht, callback_zstring)) == NULL) {
zend_string_release(callback_zstring);
return 0;
}
zend_string_release(callback_zstring);
if (!zend_is_callable(handler, 0, NULL)) {
return -1;
}
copyval = emalloc(sizeof(zval));
ZVAL_COPY(copyval, handler);
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(copyval);
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;
zend_string *method_zstring;
method_zstring = zend_string_init(method, method_len, 0);
if ((value = zend_hash_find(ht, method_zstring)) == NULL) {
zend_string_release(method_zstring);
return 0;
}
zend_string_release(method_zstring);
if ((Z_TYPE_P(value) != IS_STRING)) {
return -1;
}
return libssh2_session_method_pref(session, method_type, Z_STRVAL_P(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)
{
LIBSSH2_SESSION *session;
int socket;
php_ssh2_session_data *data;
struct timeval tv;
zend_string *hash_lookup_zstring;
tv.tv_sec = FG(default_socket_timeout);
tv.tv_usec = 0;
socket = php_network_connect_socket_to_host(host, port, SOCK_STREAM, 0, &tv, NULL, NULL, NULL, 0, STREAM_SOCKOP_NONE);
if (socket <= 0) {
php_error_docref(NULL, E_WARNING, "Unable to connect to %s on port %d", host, port);
return NULL;
}
data = ecalloc(1, sizeof(php_ssh2_session_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, 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, 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, E_WARNING, "Failed overriding HOSTKEY method");
}
hash_lookup_zstring = zend_string_init("client_to_server", sizeof("client_to_server") - 1, 0);
if ((container = zend_hash_find(HASH_OF(methods), hash_lookup_zstring)) != NULL && Z_TYPE_P(container) == IS_ARRAY) {
if (php_ssh2_set_method(session, HASH_OF(container), "crypt", sizeof("crypt") - 1, LIBSSH2_METHOD_CRYPT_CS)) {
php_error_docref(NULL, 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, 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, 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, E_WARNING, "Failed overriding client to server LANG method");
}
}
zend_string_release(hash_lookup_zstring);
hash_lookup_zstring = zend_string_init("server_to_client", sizeof("server_to_client") - 1, 0);
if ((container = zend_hash_find(HASH_OF(methods), hash_lookup_zstring)) != NULL && Z_TYPE_P(container) == IS_ARRAY) {
if (php_ssh2_set_method(session, HASH_OF(container), "crypt", sizeof("crypt") - 1, LIBSSH2_METHOD_CRYPT_SC)) {
php_error_docref(NULL, 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, 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, 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, E_WARNING, "Failed overriding server to client LANG method");
}
}
zend_string_release(hash_lookup_zstring);
}
/* 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)) {
php_error_docref(NULL, E_WARNING, "Failed setting IGNORE callback");
}
if (php_ssh2_set_callback(session, HASH_OF(callbacks), "debug", sizeof("debug") - 1, LIBSSH2_CALLBACK_DEBUG, data)) {
php_error_docref(NULL, E_WARNING, "Failed setting DEBUG callback");
}
if (php_ssh2_set_callback(session, HASH_OF(callbacks), "macerror", sizeof("macerror") - 1, LIBSSH2_CALLBACK_MACERROR, data)) {
php_error_docref(NULL, E_WARNING, "Failed setting MACERROR callback");
}
if (php_ssh2_set_callback(session, HASH_OF(callbacks), "disconnect", sizeof("disconnect") - 1, LIBSSH2_CALLBACK_DISCONNECT, data)) {
php_error_docref(NULL, 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, 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;
zend_long port = PHP_SSH2_DEFAULT_PORT;
size_t host_len;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|la!a!", &host, &host_len, &port, &methods, &callbacks) == FAILURE) {
return;
}
session = php_ssh2_session_connect(host, port, methods, callbacks);
if (!session) {
php_error_docref(NULL, E_WARNING, "Unable to connect to %s", host);
RETURN_FALSE;
}
RETURN_RES(zend_register_resource(session, le_ssh2_session));
}
/* }}} */
/* {{{ proto resource ssh2_disconnect(resource session)
* close a connection to a remote SSH server and return a true on success, false on error.
*/
PHP_FUNCTION(ssh2_disconnect)
{
zval *zsession;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zsession) == FAILURE) {
RETURN_FALSE;
}
zend_list_close(Z_RES_P(zsession));
RETURN_TRUE;
}
/* }}} */
/* {{{ 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(), "r", &zsession) == FAILURE) {
return;
}
if ((session = (LIBSSH2_SESSION *)zend_fetch_resource(Z_RES_P(zsession), PHP_SSH2_SESSION_RES_NAME, le_ssh2_session)) == NULL) {
RETURN_FALSE;
}
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);
add_assoc_string(return_value, "hostkey", hostkey);
array_init(&endpoint);
add_assoc_string(&endpoint, "crypt", crypt_cs);
add_assoc_string(&endpoint, "mac", mac_cs);
add_assoc_string(&endpoint, "comp", comp_cs);
add_assoc_string(&endpoint, "lang", lang_cs);
add_assoc_zval(return_value, "client_to_server", &endpoint);
array_init(&endpoint);
add_assoc_string(&endpoint, "crypt", crypt_sc);
add_assoc_string(&endpoint, "mac", mac_sc);
add_assoc_string(&endpoint, "comp", comp_sc);
add_assoc_string(&endpoint, "lang", lang_sc);
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;
zend_long flags = 0;
int i, fingerprint_len;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|l", &zsession, &flags) == FAILURE) {
return;
}
fingerprint_len = (flags & PHP_SSH2_FINGERPRINT_SHA1) ? SHA_DIGEST_LENGTH : MD5_DIGEST_LENGTH;
if ((session = (LIBSSH2_SESSION *)zend_fetch_resource(Z_RES_P(zsession), PHP_SSH2_SESSION_RES_NAME, le_ssh2_session)) == NULL) {
RETURN_FALSE;
}
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, E_WARNING, "Unable to retrieve fingerprint from specified session");
RETURN_FALSE;
}
for(i = 0; i < fingerprint_len; i++) {
if (fingerprint[i] != '\0') {
goto fingerprint_good;
}
}
php_error_docref(NULL, E_WARNING, "No fingerprint available using specified hash");
RETURN_NULL();
fingerprint_good:
if (flags & PHP_SSH2_FINGERPRINT_RAW) {
RETURN_STRINGL(fingerprint, fingerprint_len);
} 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]);
}
ZVAL_STRINGL(return_value, hexchars, 2 * fingerprint_len);
efree(hexchars);
}
}
/* }}} */
/* {{{ 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;
size_t username_len;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &zsession, &username, &username_len) == FAILURE) {
return;
}
if ((session = (LIBSSH2_SESSION *)zend_fetch_resource(Z_RES_P(zsession), PHP_SSH2_SESSION_RES_NAME, le_ssh2_session)) == NULL) {
RETURN_FALSE;
}
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);
}
s = p + 1;
}
if (strlen(s)) {
add_next_index_string(return_value, s);
}
}
/* }}} */
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;
zend_string *username, *password;
char *userauthlist;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "rSS", &zsession, &username, &password) == FAILURE) {
return;
}
SSH2_FETCH_NONAUTHENTICATED_SESSION(session, zsession);
userauthlist = libssh2_userauth_list(session, username->val, username->len);
if (userauthlist != NULL) {
password_for_kbd_callback = password->val;
if (strstr(userauthlist, "keyboard-interactive") != NULL) {
if (libssh2_userauth_keyboard_interactive(session, username->val, &kbd_callback) == 0) {
RETURN_TRUE;
}
}
/* TODO: Support password change callback */
if (libssh2_userauth_password_ex(session, username->val, username->len, password->val, password->len, NULL)) {
php_error_docref(NULL, E_WARNING, "Authentication failed for %s using password", username->val);
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;
zend_string *username, *pubkey, *privkey, *passphrase;
#ifndef PHP_WIN32
zend_string *newpath;
struct passwd *pws;
#endif
if (zend_parse_parameters(ZEND_NUM_ARGS(), "rSSS|S", &zsession, &username, &pubkey, &privkey, &passphrase) == FAILURE) {
return;
}
if (php_check_open_basedir(ZSTR_VAL(pubkey)) || php_check_open_basedir(ZSTR_VAL(privkey))) {
RETURN_FALSE;
}
SSH2_FETCH_NONAUTHENTICATED_SESSION(session, zsession);
#ifndef PHP_WIN32
/* 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 (ZSTR_LEN(pubkey) >= 2 && *(ZSTR_VAL(pubkey)) == '~' && *(ZSTR_VAL(pubkey)+1) == '/') {
newpath = zend_string_alloc(strlen(pws->pw_dir) + ZSTR_LEN(pubkey), 0);
strcpy(ZSTR_VAL(newpath), pws->pw_dir);
strcat(ZSTR_VAL(newpath), ZSTR_VAL(pubkey)+1);
zend_string_release(pubkey);
pubkey = newpath;
}
if (ZSTR_LEN(privkey) >= 2 && *(ZSTR_VAL(privkey)) == '~' && *(ZSTR_VAL(privkey)+1) == '/') {
newpath = zend_string_alloc(strlen(pws->pw_dir) + ZSTR_LEN(privkey), 0);
strcpy(ZSTR_VAL(newpath), pws->pw_dir);
strcat(ZSTR_VAL(newpath), ZSTR_VAL(privkey)+1);
zend_string_release(privkey);
privkey = newpath;
}
#endif
/* TODO: Support passphrase callback */
if (libssh2_userauth_publickey_fromfile_ex(session, ZSTR_VAL(username), ZSTR_LEN(username), ZSTR_VAL(pubkey), ZSTR_VAL(privkey), ZSTR_VAL(passphrase))) {
char *buf;
int len;
libssh2_session_last_error(session, &buf, &len, 0);
php_error_docref(NULL, E_WARNING, "Authentication failed for %s using public key: %s", ZSTR_VAL(username), buf);
RETURN_FALSE;
}
RETURN_TRUE;
}
/* }}} */
/* {{{ proto bool ssh2_auth_pubkey(resource session, string username, string pubkey, string privkey[, string passphrase])
* Authenticate using a public key
*/
PHP_FUNCTION(ssh2_auth_pubkey)
{
LIBSSH2_SESSION *session;
zval *zsession;
zend_string *username, *pubkey, *privkey, *passphrase;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "rSSS|S", &zsession, &username, &pubkey, &privkey, &passphrase) == FAILURE) {
return;
}
SSH2_FETCH_NONAUTHENTICATED_SESSION(session, zsession);
if (libssh2_userauth_publickey_frommemory(session, ZSTR_VAL(username), ZSTR_LEN(username), ZSTR_VAL(pubkey), ZSTR_LEN(pubkey), ZSTR_VAL(privkey), ZSTR_LEN(privkey), ZSTR_VAL(passphrase))) {
char *buf;
int len;
libssh2_session_last_error(session, &buf, &len, 0);
php_error_docref(NULL, E_WARNING, "Authentication failed for %s using public key: %s", ZSTR_VAL(username), buf);
RETURN_FALSE;
}
RETURN_TRUE;
}
/* }}} */
/* {{{ proto bool ssh2_auth_hostbased_file(resource session, string username, string 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;
size_t username_len, hostname_len, pubkey_len, privkey_len, passphrase_len, local_username_len;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "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 (php_check_open_basedir(pubkey) || php_check_open_basedir(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, 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;
zend_long port;
char *host = NULL;
size_t host_len;
zend_long max_connections = PHP_SSH2_LISTEN_MAX_QUEUED;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "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, E_WARNING, "Failure listening on remote port");
RETURN_FALSE;
}
data = emalloc(sizeof(php_ssh2_listener_data));
data->session = session;
data->session_rsrc = Z_RES_P(zsession);
Z_ADDREF_P(zsession);
data->listener = listener;
RETURN_RES(zend_register_resource(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(), "r", &zlistener) == FAILURE) {
return;
}
if ((data = (php_ssh2_listener_data *)zend_fetch_resource(Z_RES_P(zlistener), PHP_SSH2_LISTENER_RES_NAME, le_ssh2_listener)) == NULL) {
RETURN_FALSE;
}
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_rsrc;
channel_data->refcount = NULL;
stream = php_stream_alloc(&php_ssh2_channel_stream_ops, channel_data, 0, "r+");
if (!stream) {
php_error_docref(NULL, E_WARNING, "Failure allocating stream");
efree(channel_data);
libssh2_channel_free(channel);
RETURN_FALSE;
}
#if PHP_VERSION_ID < 70300
GC_REFCOUNT(channel_data->session_rsrc)++;
#else
GC_ADDREF(channel_data->session_rsrc);
#endif
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, **pollmap;
zend_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();
zend_string *hash_key_zstring;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "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));
(subarray = zend_hash_get_current_data(Z_ARRVAL_P(zdesc))) != NULL;
zend_hash_move_forward(Z_ARRVAL_P(zdesc))) {
zval *tmpzval;
int res_type = 0;
void *res;
if (Z_TYPE_P(subarray) != IS_ARRAY) {
php_error_docref(NULL, E_WARNING, "Invalid element in poll array, not a sub array");
numfds--;
continue;
}
hash_key_zstring = zend_string_init("events", sizeof("events") - 1, 0);
if ((tmpzval = zend_hash_find(Z_ARRVAL_P(subarray), hash_key_zstring)) == NULL || Z_TYPE_P(tmpzval) != IS_LONG) {
php_error_docref(NULL, E_WARNING, "Invalid data in subarray, no events element, or not a bitmask");
numfds--;
zend_string_release(hash_key_zstring);
continue;
}
zend_string_release(hash_key_zstring);
pollfds[i].events = Z_LVAL_P(tmpzval);
hash_key_zstring = zend_string_init("resource", sizeof("resource") - 1, 0);
if ((tmpzval = zend_hash_find(Z_ARRVAL_P(subarray), hash_key_zstring)) == NULL || Z_TYPE_P(tmpzval) != IS_REFERENCE
|| (tmpzval = Z_REFVAL_P(tmpzval)) == NULL || Z_TYPE_P(tmpzval) != IS_RESOURCE) {
php_error_docref(NULL, E_WARNING, "Invalid data in subarray, no resource element, or not of type resource");
numfds--;
zend_string_release(hash_key_zstring);
continue;
}
zend_string_release(hash_key_zstring);
res_type = Z_RES_P(tmpzval)->type;
res = zend_fetch_resource_ex(tmpzval, "Poll Resource", 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, E_WARNING, "Invalid resource type in subarray: %s", zend_rsrc_list_get_rsrc_type(Z_RES_P(tmpzval)));
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_REFCOUNT_P(subarray) > 1) {
/* Make a new copy of the subarray zval* */
zval new_subarray;
ZVAL_DUP(&new_subarray, subarray);
/* Decrement refcount prior to swapping in new allocation */
Z_DELREF_P(subarray);
*pollmap[i] = new_subarray;
}
hash_key_zstring = zend_string_init("revents", sizeof("revents") - 1, 0);
zend_hash_del(Z_ARRVAL_P(subarray), hash_key_zstring);
zend_string_release(hash_key_zstring);
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(), "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, 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_rsrc = Z_RES_P(zsession);
Z_ADDREF_P(zsession);
data->pkey = pkey;
RETURN_RES(zend_register_resource(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;
size_t 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(), "rss|ba", &zpkey_data, &algo, &algo_len, &blob, &blob_len, &overwrite, &zattrs) == FAILURE) {
return;
}
if ((data = (php_ssh2_pkey_subsys_data *)zend_fetch_resource(Z_RES_P(zpkey_data), PHP_SSH2_PKEY_SUBSYS_RES_NAME, le_ssh2_pkey_subsys)) == NULL) {
RETURN_FALSE;
}
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);
(attr_val = zend_hash_get_current_data_ex(Z_ARRVAL_P(zattrs), &pos)) != NULL;
zend_hash_move_forward_ex(Z_ARRVAL_P(zattrs), &pos)) {
zend_string *key;
int type;
zend_ulong idx;
zval copyval = *attr_val;
type = zend_hash_get_current_key_ex(Z_ARRVAL_P(zattrs), &key, &idx, &pos);
if (type == HASH_KEY_NON_EXISTENT) {
/* All but impossible */
break;
}
if (type == HASH_KEY_IS_LONG) {
/* Malformed, ignore */
php_error_docref(NULL, E_WARNING, "Malformed attirbute array, contains numeric index");
num_attrs--;
continue;
}
if (!key || (key->len == 1 && key->val[0] == '*')) {
/* Empty key, ignore */
php_error_docref(NULL, E_WARNING, "Empty attribute key");
num_attrs--;
continue;
}
zval_copy_ctor(©val);
// TODO Sean-Der
//Z_UNSET_ISREF_P(©val);
//Z_SET_REFCOUNT_P(©val, 1);
convert_to_string(©val);
if (key->val[0] == '*') {
attrs[current_attr].mandatory = 1;
attrs[current_attr].name = key->val + 1;
attrs[current_attr].name_len = key->len - 2;
} else {
attrs[current_attr].mandatory = 0;
attrs[current_attr].name = key->val;
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, (unsigned char *) algo, algo_len, (unsigned char *) blob, blob_len, overwrite, num_attrs, attrs)) {
php_error_docref(NULL, 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 */
// TODO Sean-Der
//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;
size_t algo_len, blob_len;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "rss", &zpkey_data, &algo, &algo_len, &blob, &blob_len) == FAILURE) {
return;
}
if ((data = (php_ssh2_pkey_subsys_data *)zend_fetch_resource(Z_RES_P(zpkey_data), PHP_SSH2_PKEY_SUBSYS_RES_NAME, le_ssh2_pkey_subsys)) == NULL) {
RETURN_FALSE;
}
if (libssh2_publickey_remove_ex(data->pkey, (unsigned char *) algo, algo_len, (unsigned char *) blob, blob_len)) {
php_error_docref(NULL, 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;
zend_string *hash_key_zstring;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zpkey_data) == FAILURE) {
return;
}
if ((data = (php_ssh2_pkey_subsys_data *)zend_fetch_resource(Z_RES_P(zpkey_data), PHP_SSH2_PKEY_SUBSYS_RES_NAME, le_ssh2_pkey_subsys)) == NULL) {
RETURN_FALSE;
}
if (libssh2_publickey_list_fetch(data->pkey, &num_keys, &keys)) {
php_error_docref(NULL, 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;
array_init(&key);
add_assoc_stringl(&key, "name", (char *) keys[i].name, keys[i].name_len);
add_assoc_stringl(&key, "blob", (char *) keys[i].blob, keys[i].blob_len);
array_init(&attrs);
for(j = 0; j < keys[i].num_attrs; j++) {
zval attr;
ZVAL_STRINGL(&attr, keys[i].attrs[j].value, keys[i].attrs[j].value_len);
hash_key_zstring = zend_string_init(keys[i].attrs[j].name, keys[i].attrs[j].name_len, 0);
zend_hash_add(Z_ARRVAL_P(&attrs), hash_key_zstring, &attr);
zend_string_release(hash_key_zstring);
}
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;
size_t 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(), "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 (userauthlist != NULL && strstr(userauthlist, "publickey") == NULL) {
php_error_docref(NULL, 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, E_WARNING, "Failure initializing ssh-agent support");
RETURN_FALSE;
}
if (libssh2_agent_connect(agent)) {
php_error_docref(NULL, E_WARNING, "Failure connecting to ssh-agent");
libssh2_agent_free(agent);
RETURN_FALSE;
}
if (libssh2_agent_list_identities(agent)) {
php_error_docref(NULL, 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, E_WARNING, "Couldn't continue authentication");
libssh2_agent_disconnect(agent);
libssh2_agent_free(agent);
RETURN_FALSE;
}
if (rc < 0) {
php_error_docref(NULL, 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, 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_resource *rsrc)
{
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_resource *rsrc)
{
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_rsrc);
efree(data);
}
static void php_ssh2_pkey_subsys_dtor(zend_resource *rsrc)
{
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_rsrc);
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) == SUCCESS &&
php_register_url_stream_wrapper("ssh2.exec", &php_ssh2_stream_wrapper_exec) == SUCCESS &&
php_register_url_stream_wrapper("ssh2.tunnel", &php_ssh2_stream_wrapper_tunnel) == SUCCESS &&
php_register_url_stream_wrapper("ssh2.scp", &php_ssh2_stream_wrapper_scp) == SUCCESS &&
php_register_url_stream_wrapper("ssh2.sftp", &php_ssh2_sftp_wrapper) == SUCCESS) ? SUCCESS : FAILURE;
}
/* }}} */
/* {{{ PHP_MSHUTDOWN_FUNCTION
*/
PHP_MSHUTDOWN_FUNCTION(ssh2)
{
return (php_unregister_url_stream_wrapper("ssh2.shell") == SUCCESS &&
php_unregister_url_stream_wrapper("ssh2.exec") == SUCCESS &&
php_unregister_url_stream_wrapper("ssh2.tunnel") == SUCCESS &&
php_unregister_url_stream_wrapper("ssh2.scp") == SUCCESS &&
php_unregister_url_stream_wrapper("ssh2.sftp") == 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();
}
/* }}} */
/* {{{ ZEND_BEGIN_ARG_INFO
*/
ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_connect, 0, 0, 1)
ZEND_ARG_INFO(0, host)
ZEND_ARG_INFO(0, port)
ZEND_ARG_INFO(0, methods)
ZEND_ARG_INFO(0, callbacks)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_ssh2_disconnect, 1)
ZEND_ARG_INFO(0, session)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_ssh2_methods_negotiated, 1)
ZEND_ARG_INFO(0, session)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_fingerprint, 0, 0, 1)
ZEND_ARG_INFO(0, session)
ZEND_ARG_INFO(0, flags)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_ssh2_auth_none, 2)
ZEND_ARG_INFO(0, session)
ZEND_ARG_INFO(0, username)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_ssh2_auth_password, 3)
ZEND_ARG_INFO(0, session)
ZEND_ARG_INFO(0, username)
ZEND_ARG_INFO(0, password)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_auth_pubkey_file, 0, 0, 4)
ZEND_ARG_INFO(0, session)
ZEND_ARG_INFO(0, username)
ZEND_ARG_INFO(0, pubkeyfile)
ZEND_ARG_INFO(0, privkeyfile)
ZEND_ARG_INFO(0, passphrase)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_auth_pubkey, 0, 0, 4)
ZEND_ARG_INFO(0, session)
ZEND_ARG_INFO(0, username)
ZEND_ARG_INFO(0, pubkey)
ZEND_ARG_INFO(0, privkey)
ZEND_ARG_INFO(0, passphrase)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_auth_hostbased_file, 0, 0, 5)
ZEND_ARG_INFO(0, session)
ZEND_ARG_INFO(0, username)
ZEND_ARG_INFO(0, hostname)
ZEND_ARG_INFO(0, pubkeyfile)
ZEND_ARG_INFO(0, privkeyfile)
ZEND_ARG_INFO(0, passphrase)
ZEND_ARG_INFO(0, local_username)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_forward_listen, 0, 0, 2)
ZEND_ARG_INFO(0, session)
ZEND_ARG_INFO(0, port)
ZEND_ARG_INFO(0, host)
ZEND_ARG_INFO(0, max_connections)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_forward_accept, 0, 0, 1)
ZEND_ARG_INFO(0, listener)
ZEND_ARG_INFO(1, host)
ZEND_ARG_INFO(0, port)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_shell, 0, 0, 1)
ZEND_ARG_INFO(0, session)
ZEND_ARG_INFO(0, termtype)
ZEND_ARG_INFO(0, env)
ZEND_ARG_INFO(0, width)
ZEND_ARG_INFO(0, height)
ZEND_ARG_INFO(0, width_height_type)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_shell_resize, 0, 0, 3)
ZEND_ARG_INFO(0, session)
ZEND_ARG_INFO(0, width)
ZEND_ARG_INFO(0, height)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_exec, 0, 0, 2)
ZEND_ARG_INFO(0, session)
ZEND_ARG_INFO(0, command)
ZEND_ARG_INFO(0, pty)
ZEND_ARG_INFO(0, env)
ZEND_ARG_INFO(0, width)
ZEND_ARG_INFO(0, height)
ZEND_ARG_INFO(0, width_height_type)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_ssh2_tunnel, 3)
ZEND_ARG_INFO(0, session)
ZEND_ARG_INFO(0, host)
ZEND_ARG_INFO(0, port)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_ssh2_scp_recv, 3)
ZEND_ARG_INFO(0, session)
ZEND_ARG_INFO(0, remote_file)
ZEND_ARG_INFO(0, local_file)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_scp_send, 0, 0, 3)
ZEND_ARG_INFO(0, session)
ZEND_ARG_INFO(0, local_file)
ZEND_ARG_INFO(0, remote_file)
ZEND_ARG_INFO(0, create_mode)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_ssh2_fetch_stream, 2)
ZEND_ARG_INFO(0, channel)
ZEND_ARG_INFO(0, streamid)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_poll, 0, 0, 1)
ZEND_ARG_INFO(1, polldes)
ZEND_ARG_INFO(0, timeout)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_send_eof, 0, 0, 1)
ZEND_ARG_INFO(0, channel)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_ssh2_sftp, 1)
ZEND_ARG_INFO(0, session)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_ssh2_sftp_rename, 3)
ZEND_ARG_INFO(0, sftp)
ZEND_ARG_INFO(0, from)
ZEND_ARG_INFO(0, to)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_ssh2_sftp_unlink, 2)
ZEND_ARG_INFO(0, sftp)
ZEND_ARG_INFO(0, filename)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_sftp_mkdir, 0, 0, 2)
ZEND_ARG_INFO(0, sftp)
ZEND_ARG_INFO(0, dirname)
ZEND_ARG_INFO(0, mode)
ZEND_ARG_INFO(0, recursive)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_ssh2_sftp_rmdir, 2)
ZEND_ARG_INFO(0, sftp)
ZEND_ARG_INFO(0, dirname)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_ssh2_sftp_chmod, 3)
ZEND_ARG_INFO(0, sftp)
ZEND_ARG_INFO(0, filename)
ZEND_ARG_INFO(0, mode)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_ssh2_sftp_stat, 2)
ZEND_ARG_INFO(0, sftp)
ZEND_ARG_INFO(0, path)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_ssh2_sftp_lstat, 2)
ZEND_ARG_INFO(0, sftp)
ZEND_ARG_INFO(0, path)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_ssh2_sftp_symlink, 3)
ZEND_ARG_INFO(0, sftp)
ZEND_ARG_INFO(0, target)
ZEND_ARG_INFO(0, link)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_ssh2_sftp_readlink, 2)
ZEND_ARG_INFO(0, sftp)
ZEND_ARG_INFO(0, link)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_ssh2_sftp_realpath, 2)
ZEND_ARG_INFO(0, sftp)
ZEND_ARG_INFO(0, filename)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_ssh2_publickey_init, 1)
ZEND_ARG_INFO(0, session)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_publickey_add, 0, 0, 3)
ZEND_ARG_INFO(0, pkey)
ZEND_ARG_INFO(0, algoname)
ZEND_ARG_INFO(0, blob)
ZEND_ARG_INFO(0, overwrite)
ZEND_ARG_INFO(0, attributes)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_ssh2_publickey_remove, 3)
ZEND_ARG_INFO(0, pkey)
ZEND_ARG_INFO(0, algoname)
ZEND_ARG_INFO(0, blob)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_ssh2_publickey_list, 1)
ZEND_ARG_INFO(0, pkey)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_ssh2_auth_agent, 2)
ZEND_ARG_INFO(0, session)
ZEND_ARG_INFO(0, username)
ZEND_END_ARG_INFO()
/* }}} */
/* {{{ ssh2_functions[]
*/
zend_function_entry ssh2_functions[] = {
PHP_FE(ssh2_connect, arginfo_ssh2_connect)
PHP_FE(ssh2_disconnect, arginfo_ssh2_disconnect)
PHP_FE(ssh2_methods_negotiated, arginfo_ssh2_methods_negotiated)
PHP_FE(ssh2_fingerprint, arginfo_ssh2_fingerprint)
PHP_FE(ssh2_auth_none, arginfo_ssh2_auth_none)
PHP_FE(ssh2_auth_password, arginfo_ssh2_auth_password)
PHP_FE(ssh2_auth_pubkey_file, arginfo_ssh2_auth_pubkey_file)
PHP_FE(ssh2_auth_pubkey, arginfo_ssh2_auth_pubkey)
PHP_FE(ssh2_auth_hostbased_file, arginfo_ssh2_auth_hostbased_file)
PHP_FE(ssh2_forward_listen, arginfo_ssh2_forward_listen)
PHP_FE(ssh2_forward_accept, arginfo_ssh2_forward_accept)
/* Stream Stuff */
PHP_FE(ssh2_shell, arginfo_ssh2_shell)
PHP_FE(ssh2_exec, arginfo_ssh2_exec)
PHP_FE(ssh2_tunnel, arginfo_ssh2_tunnel)
PHP_FE(ssh2_scp_recv, arginfo_ssh2_scp_recv)
PHP_FE(ssh2_scp_send, arginfo_ssh2_scp_send)
PHP_FE(ssh2_fetch_stream, arginfo_ssh2_fetch_stream)
PHP_FE(ssh2_poll, arginfo_ssh2_poll)
PHP_FE(ssh2_send_eof, arginfo_ssh2_send_eof)
PHP_FE(ssh2_shell_resize, arginfo_ssh2_shell_resize)
/* SFTP Stuff */
PHP_FE(ssh2_sftp, arginfo_ssh2_sftp)
/* SFTP Wrapper Ops */
PHP_FE(ssh2_sftp_rename, arginfo_ssh2_sftp_rename)
PHP_FE(ssh2_sftp_unlink, arginfo_ssh2_sftp_unlink)
PHP_FE(ssh2_sftp_mkdir, arginfo_ssh2_sftp_mkdir)
PHP_FE(ssh2_sftp_rmdir, arginfo_ssh2_sftp_rmdir)
PHP_FE(ssh2_sftp_chmod, arginfo_ssh2_sftp_chmod)
PHP_FE(ssh2_sftp_stat, arginfo_ssh2_sftp_stat)
PHP_FE(ssh2_sftp_lstat, arginfo_ssh2_sftp_lstat)
PHP_FE(ssh2_sftp_symlink, arginfo_ssh2_sftp_symlink)
PHP_FE(ssh2_sftp_readlink, arginfo_ssh2_sftp_readlink)
PHP_FE(ssh2_sftp_realpath, arginfo_ssh2_sftp_realpath)
/* Publickey subsystem */
PHP_FE(ssh2_publickey_init, arginfo_ssh2_publickey_init)
PHP_FE(ssh2_publickey_add, arginfo_ssh2_publickey_add)
PHP_FE(ssh2_publickey_remove, arginfo_ssh2_publickey_remove)
PHP_FE(ssh2_publickey_list, arginfo_ssh2_publickey_list)
PHP_FE(ssh2_auth_agent, arginfo_ssh2_auth_agent)
PHP_FE_END
};
/* }}} */
/* {{{ ssh2_module_entry
*/
zend_module_entry ssh2_module_entry = {
STANDARD_MODULE_HEADER,
"ssh2",
ssh2_functions,
PHP_MINIT(ssh2),
PHP_MSHUTDOWN(ssh2),
NULL, /* RINIT */
NULL, /* RSHUTDOWN */
PHP_MINFO(ssh2),
PHP_SSH2_VERSION,
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-1.4.1/php_ssh2.h 0000644 0001750 0001750 00000013375 14561537332 013601 0 ustar derick derick /*
+----------------------------------------------------------------------+
| PHP Version 7 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2016 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.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 |
+----------------------------------------------------------------------+
*/
#ifndef PHP_SSH2_H
#define PHP_SSH2_H
#include
#include
#include "ext/standard/url.h"
#include "main/php_network.h"
#define PHP_SSH2_VERSION "1.4.1"
#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;
php_socket_t socket;
} php_ssh2_session_data;
typedef struct _php_ssh2_sftp_data {
LIBSSH2_SESSION *session;
LIBSSH2_SFTP *sftp;
zend_resource *session_rsrc;
} php_ssh2_sftp_data;
typedef struct _php_ssh2_listener_data {
LIBSSH2_SESSION *session;
LIBSSH2_LISTENER *listener;
zend_resource *session_rsrc;
} php_ssh2_listener_data;
#include "libssh2_publickey.h"
typedef struct _php_ssh2_pkey_subsys_data {
LIBSSH2_SESSION *session;
LIBSSH2_PUBLICKEY *pkey;
zend_resource *session_rsrc;
} php_ssh2_pkey_subsys_data;
#define SSH2_FETCH_NONAUTHENTICATED_SESSION(session, zsession) \
if ((session = (LIBSSH2_SESSION *)zend_fetch_resource(Z_RES_P(zsession), PHP_SSH2_SESSION_RES_NAME, le_ssh2_session)) == NULL) { \
RETURN_FALSE; \
} \
if (libssh2_userauth_authenticated(session)) { \
php_error_docref(NULL, E_WARNING, "Connection already authenticated"); \
RETURN_FALSE; \
}
#define SSH2_FETCH_AUTHENTICATED_SESSION(session, zsession) \
if ((session = (LIBSSH2_SESSION *)zend_fetch_resource(Z_RES_P(zsession), PHP_SSH2_SESSION_RES_NAME, le_ssh2_session)) == NULL) { \
RETURN_FALSE; \
} \
if (!libssh2_userauth_authenticated(session)) { \
php_error_docref(NULL, 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 */
zend_resource *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);
PHP_FUNCTION(ssh2_send_eof);
PHP_FUNCTION(ssh2_shell_resize);
/* 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);
void php_ssh2_sftp_dtor(zend_resource *rsrc);
php_url *php_ssh2_fopen_wraper_parse_path(const char *path, char *type, php_stream_context *context,
LIBSSH2_SESSION **psession, zend_resource **presource,
LIBSSH2_SFTP **psftp, zend_resource **psftp_rsrc);
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;
#if PHP_VERSION_ID < 70300
#define SSH2_URL_STR(a) (a)
#define SSH2_URL_LEN(a) strlen(a)
#else
#define SSH2_URL_STR(a) ZSTR_VAL(a)
#define SSH2_URL_LEN(a) ZSTR_LEN(a)
#endif
#endif /* PHP_SSH2_H */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* indent-tabs-mode: t
* End:
*/
ssh2-1.4.1/ssh2_fopen_wrappers.c 0000644 0001750 0001750 00000122267 14561537332 016040 0 ustar derick derick /*
+----------------------------------------------------------------------+
| PHP Version 7 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2016 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.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 |
+----------------------------------------------------------------------+
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "php_ssh2.h"
void *php_ssh2_zval_from_resource_handle(int handle) {
zval *val;
zend_resource *zr;
ZEND_HASH_FOREACH_VAL(&EG(regular_list), val) {
zr = Z_RES_P(val);
if (zr->handle == handle) {
return val;
}
} ZEND_HASH_FOREACH_END();
return NULL;
}
/* **********************
* channel_stream_ops *
********************** */
#if PHP_VERSION_ID < 70400
static size_t php_ssh2_channel_stream_write(php_stream *stream, const char *buf, size_t count)
#else
static ssize_t php_ssh2_channel_stream_write(php_stream *stream, const char *buf, size_t count)
#endif
{
php_ssh2_channel_data *abstract = (php_ssh2_channel_data*)stream->abstract;
ssize_t writestate;
LIBSSH2_SESSION *session;
libssh2_channel_set_blocking(abstract->channel, abstract->is_blocking);
session = (LIBSSH2_SESSION *)zend_fetch_resource(abstract->session_rsrc, PHP_SSH2_SESSION_RES_NAME, 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) {
#if PHP_VERSION_ID < 70400
writestate = 0;
#endif
} else if (writestate < 0) {
char *error_msg = NULL;
if (libssh2_session_last_error(session, &error_msg, NULL, 0) == writestate) {
php_error_docref(NULL, E_WARNING, "Failure '%s' (%ld)", error_msg, writestate);
}
stream->eof = 1;
#if PHP_VERSION_ID < 70400
writestate = 0;
#endif
}
return writestate;
}
#if PHP_VERSION_ID < 70400
static size_t php_ssh2_channel_stream_read(php_stream *stream, char *buf, size_t count)
#else
static ssize_t php_ssh2_channel_stream_read(php_stream *stream, char *buf, size_t count)
#endif
{
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(abstract->session_rsrc, PHP_SSH2_SESSION_RES_NAME, 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) {
#if PHP_VERSION_ID < 70400
readstate = 0;
#endif
} else if (readstate < 0) {
char *error_msg = NULL;
if (libssh2_session_last_error(session, &error_msg, NULL, 0) == readstate) {
php_error_docref(NULL, 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)
{
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)
{
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_cast(php_stream *stream, int castas, void **ret)
{
php_ssh2_channel_data *abstract = (php_ssh2_channel_data*)stream->abstract;
LIBSSH2_SESSION *session;
php_ssh2_session_data **session_data;
session = (LIBSSH2_SESSION *)zend_fetch_resource(abstract->session_rsrc, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session);
session_data = (php_ssh2_session_data **)libssh2_session_abstract(session);
switch (castas) {
case PHP_STREAM_AS_FD:
case PHP_STREAM_AS_FD_FOR_SELECT:
case PHP_STREAM_AS_SOCKETD:
if (ret) {
*(php_socket_t *)ret = (*session_data)->socket;
}
return SUCCESS;
default:
return FAILURE;
}
}
static int php_ssh2_channel_stream_set_option(php_stream *stream, int option, int value, void *ptrparam)
{
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_META_DATA_API:
add_assoc_long((zval*)ptrparam, "exit_status", libssh2_channel_get_exit_status(abstract->channel));
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, E_WARNING, "No support for ssh2 stream timeout. Please recompile with libssh2 >= 1.2.9");
#endif
return ret;
break;
case PHP_STREAM_OPTION_CHECK_LIVENESS:
return stream->eof = libssh2_channel_eof(abstract->channel);
break;
}
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 */
php_ssh2_channel_stream_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(const char *path, char *type, php_stream_context *context,
LIBSSH2_SESSION **psession, zend_resource **presource,
LIBSSH2_SFTP **psftp, zend_resource **psftp_rsrc)
{
php_ssh2_sftp_data *sftp_data = NULL;
LIBSSH2_SESSION *session;
php_url *resource;
zval *methods = NULL, *callbacks = NULL, zsession, *tmpzval;
zend_long resource_id;
char *h, *username = NULL, *password = NULL, *pubkey_file = NULL, *privkey_file = NULL;
int username_len = 0, password_len = 0;
h = strstr(path, "Resource id #");
if (h) {
/* Starting with 5.6.28, 7.0.13 need to be clean, else php_url_parse will fail */
char *tmp = estrdup(path);
strncpy(tmp + (h-path), h + sizeof("Resource id #")-1, strlen(tmp)-sizeof("Resource id #"));
resource = php_url_parse(tmp);
efree(tmp);
} else {
resource = php_url_parse(path);
}
if (!resource || !resource->path) {
return NULL;
}
if (strncmp(SSH2_URL_STR(resource->scheme), "ssh2.", sizeof("ssh2.") - 1)) {
/* Not an ssh wrapper */
php_url_free(resource);
return NULL;
}
if (strcmp(SSH2_URL_STR(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
*/
// TODO copy seems uneeded
#if PHP_VERSION_ID < 70300
{
char * s;
s = resource->path;
resource->path = estrdup(strstr(path, resource->path));
efree(s);
}
#else
{
zend_string *tmp;
tmp = resource->path;
resource->path = zend_string_init(ZSTR_VAL(resource->path), ZSTR_LEN(resource->path), 0);
zend_string_release(tmp);
}
#endif
/* Look for a resource ID to reuse a session */
if (is_numeric_string(SSH2_URL_STR(resource->host), SSH2_URL_LEN(resource->host), &resource_id, NULL, 0) == IS_LONG) {
php_ssh2_sftp_data *sftp_data;
zval *zresource;
if ((zresource = php_ssh2_zval_from_resource_handle(resource_id)) == NULL) {
php_url_free(resource);
return NULL;
}
if (psftp) {
/* suppress potential warning by passing NULL as resource_type_name */
sftp_data = (php_ssh2_sftp_data *)zend_fetch_resource(Z_RES_P(zresource), NULL, le_ssh2_sftp);
if (sftp_data) {
/* Want the sftp layer */
Z_ADDREF_P(zresource);
*psftp_rsrc = Z_RES_P(zresource);
*psftp = sftp_data->sftp;
*presource = sftp_data->session_rsrc;
*psession = sftp_data->session;
return resource;
}
}
session = (LIBSSH2_SESSION *)zend_fetch_resource(Z_RES_P(zresource), PHP_SSH2_SESSION_RES_NAME, 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_rsrc = Z_RES_P(zresource);
Z_ADDREF_P(zresource);
*psftp_rsrc = zend_register_resource(sftp_data, le_ssh2_sftp);
*psftp = sftp;
*presource = Z_RES_P(zresource);
*psession = session;
return resource;
}
Z_ADDREF_P(zresource);
*presource = Z_RES_P(zresource);
*psession = session;
return resource;
}
}
/* Fallback on finding it in the context */
if (SSH2_URL_STR(resource->host)[0] == 0 && context && psftp &&
(tmpzval = php_stream_context_get_option(context, "ssh2", "sftp")) != NULL &&
Z_TYPE_P(tmpzval) == IS_RESOURCE) {
php_ssh2_sftp_data *sftp_data;
sftp_data = (php_ssh2_sftp_data *)zend_fetch_resource(Z_RES_P(tmpzval), PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp);
if (sftp_data) {
Z_ADDREF_P(tmpzval);
*psftp_rsrc = Z_RES_P(tmpzval);
*psftp = sftp_data->sftp;
*presource = sftp_data->session_rsrc;
*psession = sftp_data->session;
return resource;
}
}
if (SSH2_URL_STR(resource->host)[0] == 0 && context &&
(tmpzval = php_stream_context_get_option(context, "ssh2", "session")) != NULL &&
Z_TYPE_P(tmpzval) == IS_RESOURCE) {
session = (LIBSSH2_SESSION *)zend_fetch_resource(Z_RES_P(tmpzval), PHP_SSH2_SESSION_RES_NAME, 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_rsrc = Z_RES_P(tmpzval);
Z_ADDREF_P(tmpzval);
*psftp_rsrc = zend_register_resource(sftp_data, le_ssh2_sftp);
*psftp = sftp;
*presource = Z_RES_P(tmpzval);
*psession = session;
return resource;
}
Z_ADDREF_P(tmpzval);
*psession = session;
*presource = Z_RES_P(tmpzval);
return resource;
}
}
/* Make our own connection then */
if (!resource->port) {
resource->port = 22;
}
if (context &&
(tmpzval = php_stream_context_get_option(context, "ssh2", "methods")) != NULL &&
Z_TYPE_P(tmpzval) == IS_ARRAY) {
methods = tmpzval;
}
if (context &&
(tmpzval = php_stream_context_get_option(context, "ssh2", "callbacks")) != NULL &&
Z_TYPE_P(tmpzval) == IS_ARRAY) {
callbacks = tmpzval;
}
if (context &&
(tmpzval = php_stream_context_get_option(context, "ssh2", "username")) != NULL &&
Z_TYPE_P(tmpzval) == IS_STRING) {
username = Z_STRVAL_P(tmpzval);
username_len = Z_STRLEN_P(tmpzval);
}
if (context &&
(tmpzval = php_stream_context_get_option(context, "ssh2", "password")) != NULL &&
Z_TYPE_P(tmpzval) == IS_STRING) {
password = Z_STRVAL_P(tmpzval);
password_len = Z_STRLEN_P(tmpzval);
}
if (context &&
(tmpzval = php_stream_context_get_option(context, "ssh2", "pubkey_file")) != NULL &&
Z_TYPE_P(tmpzval) == IS_STRING) {
pubkey_file = Z_STRVAL_P(tmpzval);
}
if (context &&
(tmpzval = php_stream_context_get_option(context, "ssh2", "privkey_file")) != NULL &&
Z_TYPE_P(tmpzval) == IS_STRING) {
privkey_file = Z_STRVAL_P(tmpzval);
}
if (resource->user) {
int len = SSH2_URL_LEN(resource->user);
if (len) {
username = SSH2_URL_STR(resource->user);
username_len = len;
}
}
if (resource->pass) {
int len = SSH2_URL_LEN(resource->pass);
if (len) {
password = SSH2_URL_STR(resource->pass);
password_len = len;
}
}
if (!username) {
/* username is a minimum */
php_url_free(resource);
return NULL;
}
session = php_ssh2_session_connect(SSH2_URL_STR(resource->host), resource->port, methods, callbacks);
if (!session) {
/* Unable to connect! */
php_url_free(resource);
return NULL;
}
/* Authenticate */
if (pubkey_file && privkey_file) {
if (php_check_open_basedir(pubkey_file) || php_check_open_basedir(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);
if (Z_RES(zsession)) {
zend_list_delete(Z_RES(zsession));
}
return NULL;
session_authed:
ZVAL_RES(&zsession, zend_register_resource(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_RES(zsession));
return NULL;
}
sftp_data = emalloc(sizeof(php_ssh2_sftp_data));
sftp_data->session = session;
sftp_data->sftp = sftp;
sftp_data->session_rsrc = Z_RES(zsession);
//TODO Sean-Der
//ZEND_REGISTER_RESOURCE(sftp_data, le_ssh2_sftp);
*psftp_rsrc = Z_RES(zsftp);
*psftp = sftp;
}
*presource = Z_RES(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, zend_resource *resource, char *term, int term_len, zval *environment, long width, long height, long type)
{
LIBSSH2_CHANNEL *channel;
php_ssh2_channel_data *channel_data;
php_stream *stream;
libssh2_session_set_blocking(session, 1);
channel = libssh2_channel_open_session(session);
if (!channel) {
php_error_docref(NULL, E_WARNING, "Unable to request a channel from remote host");
return NULL;
}
if (environment) {
zend_string *key;
int key_type;
zend_ulong idx;
for(zend_hash_internal_pointer_reset(HASH_OF(environment));
(key_type = zend_hash_get_current_key(HASH_OF(environment), &key, &idx)) != HASH_KEY_NON_EXISTENT;
zend_hash_move_forward(HASH_OF(environment))) {
if (key_type == HASH_KEY_IS_STRING) {
zval *value;
if ((value = zend_hash_get_current_data(HASH_OF(environment))) != NULL) {
zval copyval = *value;
zval_copy_ctor(©val);
convert_to_string(©val);
if (libssh2_channel_setenv_ex(channel, key->val, key->len, Z_STRVAL(copyval), Z_STRLEN(copyval))) {
php_error_docref(NULL, E_WARNING, "Failed setting %s=%s on remote end", ZSTR_VAL(key), Z_STRVAL(copyval));
}
zval_dtor(©val);
}
} else {
php_error_docref(NULL, 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, 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, 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, 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;
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, const char *path, const char *mode, int options, zend_string **opened_path, php_stream_context *context STREAMS_DC)
{
LIBSSH2_SESSION *session = NULL;
php_stream *stream;
zval *tmpzval, *environment = NULL;
char *terminal = PHP_SSH2_DEFAULT_TERMINAL;
zend_long width = PHP_SSH2_DEFAULT_TERM_WIDTH;
zend_long height = PHP_SSH2_DEFAULT_TERM_HEIGHT;
zend_long type = PHP_SSH2_DEFAULT_TERM_UNIT;
zend_resource *rsrc = NULL;
int terminal_len = sizeof(PHP_SSH2_DEFAULT_TERMINAL) - 1;
php_url *resource;
char *s;
resource = php_ssh2_fopen_wraper_parse_path(path, "shell", context, &session, &rsrc, NULL, NULL);
if (!resource || !session) {
return NULL;
}
if (context &&
(tmpzval = php_stream_context_get_option(context, "ssh2", "env")) != NULL && Z_TYPE_P(tmpzval) == IS_ARRAY) {
environment = tmpzval;
}
if (context &&
(tmpzval = php_stream_context_get_option(context, "ssh2", "term")) != NULL && Z_TYPE_P(tmpzval) == IS_STRING) {
terminal = Z_STRVAL_P(tmpzval);
terminal_len = Z_STRLEN_P(tmpzval);
}
if (context &&
(tmpzval = php_stream_context_get_option(context, "ssh2", "term_width")) != NULL) {
zval copyval;
copyval = *tmpzval;
convert_to_long(©val);
width = Z_LVAL_P(©val);
zval_ptr_dtor(©val);
}
if (context &&
(tmpzval = php_stream_context_get_option(context, "ssh2", "term_height")) != NULL) {
zval copyval;
copyval = *tmpzval;
convert_to_long(©val);
height = Z_LVAL_P(©val);
zval_ptr_dtor(©val);
}
if (context &&
(tmpzval = php_stream_context_get_option(context, "ssh2", "term_units")) != NULL) {
zval copyval;
copyval = *tmpzval;
convert_to_long(©val);
type = Z_LVAL_P(©val);
zval_ptr_dtor(©val);
}
s = resource->path ? SSH2_URL_STR(resource->path) : 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, rsrc, terminal, terminal_len, environment, width, height, type);
if (!stream) {
zend_list_delete(rsrc);
}
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;
size_t term_len = sizeof(PHP_SSH2_DEFAULT_TERMINAL) - 1;
zend_long width = PHP_SSH2_DEFAULT_TERM_WIDTH;
zend_long height = PHP_SSH2_DEFAULT_TERM_HEIGHT;
zend_long type = PHP_SSH2_DEFAULT_TERM_UNIT;
int argc = ZEND_NUM_ARGS();
if (argc == 5) {
php_error_docref(NULL, E_ERROR, "width specified without height parameter");
RETURN_FALSE;
}
if (zend_parse_parameters(argc, "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_RES_P(zsession), term, term_len, environment, width, height, type);
if (!stream) {
RETURN_FALSE;
}
/* Ensure that channels are freed BEFORE the sessions they belong to */
Z_ADDREF_P(zsession);
php_stream_to_zval(stream, return_value);
}
/* }}} */
PHP_FUNCTION(ssh2_shell_resize)
{
zend_long width;
zend_long height;
zend_long width_px = 0;
zend_long height_px = 0;
zval *zparent;
php_stream *parent;
php_ssh2_channel_data *data;
int argc = ZEND_NUM_ARGS();
if (zend_parse_parameters(argc, "rll|ll", &zparent, &width, &height, &width_px, &height_px) == FAILURE) {
return;
}
php_stream_from_zval(parent, zparent);
if (parent->ops != &php_ssh2_channel_stream_ops) {
php_error_docref(NULL, E_WARNING, "Provided stream is not of type " PHP_SSH2_CHANNEL_STREAM_NAME);
RETURN_FALSE;
}
data = (php_ssh2_channel_data*)parent->abstract;
libssh2_channel_request_pty_size_ex(data->channel, width, height, width_px, height_px);
RETURN_TRUE;
}
/* ****************
* Exec Wrapper *
**************** */
/* {{{ php_ssh2_exec_command
* Make a stream from a session
*/
static php_stream *php_ssh2_exec_command(LIBSSH2_SESSION *session, zend_resource *rsrc, char *command, char *term, int term_len, zval *environment, long width, long height, long type)
{
LIBSSH2_CHANNEL *channel;
php_ssh2_channel_data *channel_data;
php_stream *stream;
libssh2_session_set_blocking(session, 1);
channel = libssh2_channel_open_session(session);
if (!channel) {
php_error_docref(NULL, E_WARNING, "Unable to request a channel from remote host");
return NULL;
}
if (environment) {
zend_string *key = NULL;
int key_type;
zend_ulong idx = 0;
HashPosition pos;
for(zend_hash_internal_pointer_reset_ex(HASH_OF(environment), &pos);
(key_type = zend_hash_get_current_key_ex(HASH_OF(environment), &key, &idx, &pos)) != HASH_KEY_NON_EXISTENT;
zend_hash_move_forward_ex(HASH_OF(environment), &pos)) {
if (key_type == HASH_KEY_IS_STRING) {
zval *value;
if ((value = zend_hash_get_current_data(HASH_OF(environment))) != NULL) {
zval copyval = *value;
zval_copy_ctor(©val);
convert_to_string(©val);
if (libssh2_channel_setenv_ex(channel, key->val, key->len, Z_STRVAL(copyval), Z_STRLEN(copyval))) {
php_error_docref(NULL, E_WARNING, "Failed setting %s=%s on remote end", ZSTR_VAL(key), Z_STRVAL(copyval));
}
zval_dtor(©val);
}
} else {
php_error_docref(NULL, 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, 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, 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, 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 = rsrc;
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, const char *path, const char *mode, int options, zend_string **opened_path, php_stream_context *context STREAMS_DC)
{
LIBSSH2_SESSION *session = NULL;
php_stream *stream;
zval *tmpzval, *environment = NULL;
zend_resource *rsrc = NULL;
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, &rsrc, NULL, NULL);
if (!resource || !session) {
return NULL;
}
if (!resource->path) {
php_url_free(resource);
zend_list_delete(rsrc);
return NULL;
}
if (context &&
(tmpzval = php_stream_context_get_option(context, "ssh2", "env")) != NULL && Z_TYPE_P(tmpzval) == IS_ARRAY) {
environment = tmpzval;
}
if (context &&
(tmpzval = php_stream_context_get_option(context, "ssh2", "term")) != NULL && Z_TYPE_P(tmpzval) == IS_STRING) {
terminal = Z_STRVAL_P(tmpzval);
terminal_len = Z_STRLEN_P(tmpzval);
}
if (context &&
(tmpzval = php_stream_context_get_option(context, "ssh2", "term_width")) != NULL) {
zval copyval;
copyval = *tmpzval;
convert_to_long(©val);
width = Z_LVAL_P(©val);
zval_ptr_dtor(©val);
}
if (context &&
(tmpzval = php_stream_context_get_option(context, "ssh2", "term_height")) != NULL) {
zval copyval;
copyval = *tmpzval;
convert_to_long(©val);
height = Z_LVAL_P(©val);
zval_ptr_dtor(©val);
}
if (context &&
(tmpzval = php_stream_context_get_option(context, "ssh2", "term_units")) != NULL) {
zval *copyval;
copyval = tmpzval;
convert_to_long(copyval);
type = Z_LVAL_P(copyval);
zval_ptr_dtor(copyval);
}
stream = php_ssh2_exec_command(session, rsrc, SSH2_URL_STR(resource->path) + 1, terminal, terminal_len, environment, width, height, type);
if (!stream) {
zend_list_delete(rsrc);
}
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 height[, 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;
size_t command_len;
zend_long width = PHP_SSH2_DEFAULT_TERM_WIDTH;
zend_long height = PHP_SSH2_DEFAULT_TERM_HEIGHT;
zend_long type = PHP_SSH2_DEFAULT_TERM_UNIT;
char *term = NULL;
int term_len = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "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, 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_RES_P(zsession), command, term, term_len, environment, width, height, type);
if (!stream) {
RETURN_FALSE;
}
/* Ensure that channels are freed BEFORE the sessions they belong to */
Z_ADDREF_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, zend_resource *rsrc, char *filename)
{
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, 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 = rsrc;
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, const char *path, const char *mode, int options, zend_string **opened_path, php_stream_context *context STREAMS_DC)
{
LIBSSH2_SESSION *session = NULL;
php_stream *stream;
zend_resource *rsrc = NULL;
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, &rsrc, NULL, NULL);
if (!resource || !session) {
return NULL;
}
if (!resource->path) {
php_url_free(resource);
zend_list_delete(rsrc);
return NULL;
}
stream = php_ssh2_scp_xfer(session, rsrc, SSH2_URL_STR(resource->path));
if (!stream) {
zend_list_delete(rsrc);
}
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;
size_t remote_filename_len, local_filename_len;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "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, 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", REPORT_ERRORS, NULL);
if (!local_file) {
php_error_docref(NULL, 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, 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;
size_t local_filename_len, remote_filename_len;
zend_long create_mode = 0644;
php_stream_statbuf ssb;
int argc = ZEND_NUM_ARGS();
if (zend_parse_parameters(argc, "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", REPORT_ERRORS, NULL);
if (!local_file) {
php_error_docref(NULL, E_WARNING, "Unable to read source file");
RETURN_FALSE;
}
if (php_stream_stat(local_file, &ssb)) {
php_error_docref(NULL, 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, E_WARNING, "Failure creating remote file: %s (%d)", error_msg, last_error);
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);
size_t sent = 0;
size_t justsent = 0;
if (bytesread <= 0 || bytesread > toread) {
php_error_docref(NULL, E_WARNING, "Failed copying file 2");
php_stream_close(local_file);
libssh2_channel_free(remote_file);
RETURN_FALSE;
}
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, E_WARNING, "Operation would block");
break;
case LIBSSH2_ERROR_ALLOC:
php_error_docref(NULL,E_WARNING, "An internal memory allocation call failed");
break;
case LIBSSH2_ERROR_SOCKET_SEND:
php_error_docref(NULL,E_WARNING, "Unable to send data on socket");
break;
case LIBSSH2_ERROR_CHANNEL_CLOSED:
php_error_docref(NULL,E_WARNING, "The channel has been closed");
break;
case LIBSSH2_ERROR_CHANNEL_EOF_SENT:
php_error_docref(NULL,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, zend_resource *rsrc, char *host, int port)
{
LIBSSH2_CHANNEL *channel;
php_ssh2_channel_data *channel_data;
php_stream *stream;
libssh2_session_set_blocking(session, 1);
channel = libssh2_channel_direct_tcpip(session, host, port);
if (!channel) {
php_error_docref(NULL, 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 = rsrc;
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, const char *path, const char *mode, int options, zend_string **opened_path, php_stream_context *context STREAMS_DC)
{
LIBSSH2_SESSION *session = NULL;
php_stream *stream = NULL;
php_url *resource;
char *host = NULL;
int port = 0;
zend_resource *rsrc;
resource = php_ssh2_fopen_wraper_parse_path(path, "tunnel", context, &session, &rsrc, NULL, NULL);
if (!resource || !session) {
return NULL;
}
if (resource->path && SSH2_URL_STR(resource->path)[0] == '/') {
char *colon;
host = SSH2_URL_STR(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(rsrc);
return NULL;
}
stream = php_ssh2_direct_tcpip(session, rsrc, host, port);
if (!stream) {
zend_list_delete(rsrc);
}
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;
size_t host_len;
zend_long port;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "rsl", &zsession, &host, &host_len, &port) == FAILURE) {
return;
}
SSH2_FETCH_AUTHENTICATED_SESSION(session, zsession);
stream = php_ssh2_direct_tcpip(session, Z_RES_P(zsession), host, port);
if (!stream) {
RETURN_FALSE;
}
/* Ensure that channels are freed BEFORE the sessions they belong to */
Z_ADDREF_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;
zend_long streamid;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl", &zparent, &streamid) == FAILURE) {
return;
}
if (streamid < 0) {
php_error_docref(NULL, 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, 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, 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, E_WARNING, "Error opening substream");
efree(stream_data);
(data->refcount)--;
RETURN_FALSE;
}
php_stream_to_zval(stream, return_value);
}
/* }}} */
/* {{{ proto stream ssh2_send_eof(stream channel)
* Sends EOF to a stream. Primary use is to close stdin of an stdio stream.
*/
PHP_FUNCTION(ssh2_send_eof)
{
php_ssh2_channel_data *data;
php_stream *parent;
zval *zparent;
int ssh2_ret;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zparent) == FAILURE) {
return;
}
php_stream_from_zval(parent, zparent);
if (parent->ops != &php_ssh2_channel_stream_ops) {
php_error_docref(NULL, E_WARNING, "Provided stream is not of type " PHP_SSH2_CHANNEL_STREAM_NAME);
RETURN_FALSE;
}
data = (php_ssh2_channel_data*)parent->abstract;
if (!data) {
php_error_docref(NULL, E_WARNING, "Abstract in stream is null");
RETURN_FALSE;
}
ssh2_ret = libssh2_channel_send_eof(data->channel);
if (ssh2_ret < 0) {
php_error_docref(NULL, E_WARNING, "Couldn't send EOF to channel (Return code %d)", ssh2_ret);
RETURN_FALSE;
}
RETURN_TRUE;
}
/* }}} */
/*
* 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-1.4.1/ssh2_sftp.c 0000644 0001750 0001750 00000056716 14561537332 013767 0 ustar derick derick /*
+----------------------------------------------------------------------+
| PHP Version 7 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2016 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.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 |
+----------------------------------------------------------------------+
*/
#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_resource *rsrc)
{
php_ssh2_sftp_data *data = (php_ssh2_sftp_data*)rsrc->ptr;
if (!data) {
return;
}
if (data->session_rsrc->ptr != NULL) {
libssh2_sftp_shutdown(data->sftp);
}
zend_list_delete(data->session_rsrc);
efree(data);
}
/* *****************
* SFTP File Ops *
***************** */
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;
}
static 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;
zend_resource *sftp_rsrc;
} php_ssh2_sftp_handle_data;
/* {{{ php_ssh2_sftp_stream_write
*/
#if PHP_VERSION_ID < 70400
static size_t php_ssh2_sftp_stream_write(php_stream *stream, const char *buf, size_t count)
#else
static ssize_t php_ssh2_sftp_stream_write(php_stream *stream, const char *buf, size_t count)
#endif
{
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);
#if PHP_VERSION_ID < 70400
return (size_t)(bytes_written<0 ? 0 : bytes_written);
#else
return bytes_written;
#endif
}
/* }}} */
/* {{{ php_ssh2_sftp_stream_read
*/
#if PHP_VERSION_ID < 70400
static size_t php_ssh2_sftp_stream_read(php_stream *stream, char *buf, size_t count)
#else
static ssize_t php_ssh2_sftp_stream_read(php_stream *stream, char *buf, size_t count)
#endif
{
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);
#if PHP_VERSION_ID < 70400
return (size_t)(bytes_read<0 ? 0 : bytes_read);
#else
return bytes_read;
#endif
}
/* }}} */
/* {{{ php_ssh2_sftp_stream_close
*/
static int php_ssh2_sftp_stream_close(php_stream *stream, int close_handle)
{
php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data*)stream->abstract;
libssh2_sftp_close(data->handle);
zend_list_delete(data->sftp_rsrc);
efree(data);
return 0;
}
/* }}} */
/* {{{ php_ssh2_sftp_stream_seek
*/
static int php_ssh2_sftp_stream_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset)
{
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;
break;
}
case SEEK_CUR:
{
zend_off_t current_offset = libssh2_sftp_tell(data->handle);
if (current_offset < 0) {
return -1;
}
offset += current_offset;
break;
}
}
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)
{
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, const char *filename, const char *mode,
int options, zend_string **opened_path, php_stream_context *context STREAMS_DC)
{
php_ssh2_sftp_handle_data *data;
LIBSSH2_SESSION *session = NULL;
LIBSSH2_SFTP *sftp = NULL;
LIBSSH2_SFTP_HANDLE *handle;
php_stream *stream;
zend_resource *rsrc = NULL, *sftp_rsrc = NULL;
php_url *resource;
unsigned long flags;
long perms = 0644;
resource = php_ssh2_fopen_wraper_parse_path(filename, "sftp", context, &session, &rsrc, &sftp, &sftp_rsrc);
if (!resource || !session || !sftp || !sftp_rsrc) {
return NULL;
}
flags = php_ssh2_parse_fopen_modes((char *)mode);
handle = libssh2_sftp_open(sftp, SSH2_URL_STR(resource->path), flags, perms);
if (!handle) {
php_error_docref(NULL, E_WARNING, "Unable to open %s on remote host", filename);
php_url_free(resource);
zend_list_delete(sftp_rsrc);
return NULL;
}
data = emalloc(sizeof(php_ssh2_sftp_handle_data));
data->handle = handle;
data->sftp_rsrc = sftp_rsrc;
stream = php_stream_alloc(&php_ssh2_sftp_stream_ops, data, 0, mode);
if (!stream) {
libssh2_sftp_close(handle);
zend_list_delete(sftp_rsrc);
efree(data);
}
php_url_free(resource);
return stream;
}
/* }}} */
/* **********************
* SFTP Directory Ops *
********************** */
/* {{{ php_ssh2_sftp_dirstream_read
*/
#if PHP_VERSION_ID < 70400
static size_t php_ssh2_sftp_dirstream_read(php_stream *stream, char *buf, size_t count)
#else
static ssize_t php_ssh2_sftp_dirstream_read(php_stream *stream, char *buf, size_t count)
#endif
{
php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data*)stream->abstract;
php_stream_dirent *ent = (php_stream_dirent*)buf;
int bytesread = libssh2_sftp_readdir(data->handle, ent->d_name, sizeof(ent->d_name) - 1, NULL);
zend_string *basename;
if (bytesread <= 0) {
return 0;
}
ent->d_name[bytesread] = 0;
basename = php_basename(ent->d_name, bytesread, NULL, 0);
if (!basename) {
return 0;
}
bytesread = MIN(sizeof(ent->d_name) - 1, basename->len);
memcpy(ent->d_name, basename->val, bytesread);
ent->d_name[bytesread] = 0;
zend_string_release(basename);
return sizeof(php_stream_dirent);
}
/* }}} */
/* {{{ php_ssh2_sftp_dirstream_close
*/
static int php_ssh2_sftp_dirstream_close(php_stream *stream, int close_handle)
{
php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data*)stream->abstract;
libssh2_sftp_close(data->handle);
zend_list_delete(data->sftp_rsrc);
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, const char *filename, const char *mode,
int options, zend_string **opened_path, php_stream_context *context STREAMS_DC)
{
php_ssh2_sftp_handle_data *data;
LIBSSH2_SESSION *session = NULL;
LIBSSH2_SFTP *sftp = NULL;
LIBSSH2_SFTP_HANDLE *handle;
php_stream *stream;
zend_resource *rsrc = NULL, *sftp_rsrc = NULL;
php_url *resource;
resource = php_ssh2_fopen_wraper_parse_path(filename, "sftp", context, &session, &rsrc, &sftp, &sftp_rsrc);
if (!resource || !session || !sftp) {
return NULL;
}
handle = libssh2_sftp_opendir(sftp, SSH2_URL_STR(resource->path));
if (!handle) {
php_error_docref(NULL, E_WARNING, "Unable to open %s on remote host", filename);
php_url_free(resource);
zend_list_delete(sftp_rsrc);
return NULL;
}
data = emalloc(sizeof(php_ssh2_sftp_handle_data));
data->handle = handle;
data->sftp_rsrc = sftp_rsrc;
stream = php_stream_alloc(&php_ssh2_sftp_dirstream_ops, data, 0, mode);
if (!stream) {
libssh2_sftp_close(handle);
zend_list_delete(sftp_rsrc);
efree(data);
}
php_url_free(resource);
return stream;
}
/* }}} */
/* ****************
* SFTP Wrapper *
**************** */
/* {{{ php_ssh2_sftp_urlstat
*/
static int php_ssh2_sftp_urlstat(php_stream_wrapper *wrapper, const char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context)
{
LIBSSH2_SFTP_ATTRIBUTES attrs;
LIBSSH2_SESSION *session = NULL;
LIBSSH2_SFTP *sftp = NULL;
zend_resource *rsrc = NULL, *sftp_rsrc = NULL;
php_url *resource;
resource = php_ssh2_fopen_wraper_parse_path(url, "sftp", context, &session, &rsrc, &sftp, &sftp_rsrc);
if (!resource || !session || !sftp || !resource->path) {
return -1;
}
if (libssh2_sftp_stat_ex(sftp, SSH2_URL_STR(resource->path), SSH2_URL_LEN(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, const char *url, int options, php_stream_context *context)
{
LIBSSH2_SESSION *session = NULL;
LIBSSH2_SFTP *sftp = NULL;
zend_resource *rsrc = NULL, *sftp_rsrc = NULL;
php_url *resource;
int result;
resource = php_ssh2_fopen_wraper_parse_path(url, "sftp", context, &session, &rsrc, &sftp, &sftp_rsrc);
if (!resource || !session || !sftp || !resource->path) {
if (resource) {
php_url_free(resource);
}
return 0;
}
result = libssh2_sftp_unlink(sftp, SSH2_URL_STR(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, const char *url_from, const char *url_to, int options, php_stream_context *context)
{
LIBSSH2_SESSION *session = NULL;
LIBSSH2_SFTP *sftp = NULL;
zend_resource *rsrc = NULL, *sftp_rsrc = NULL;
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, &rsrc, &sftp, &sftp_rsrc);
if (!resource || !session || !sftp || !resource->path) {
if (resource) {
php_url_free(resource);
}
php_url_free(resource_to);
return 0;
}
result = libssh2_sftp_rename(sftp, SSH2_URL_STR(resource->path), SSH2_URL_STR(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, const char *url, int mode, int options, php_stream_context *context)
{
LIBSSH2_SESSION *session = NULL;
LIBSSH2_SFTP *sftp = NULL;
zend_resource *rsrc = NULL, *sftp_rsrc = NULL;
php_url *resource;
int result;
resource = php_ssh2_fopen_wraper_parse_path(url, "sftp", context, &session, &rsrc, &sftp, &sftp_rsrc);
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 = SSH2_URL_STR(resource->path);
while ((p = strchr(p + 1, '/'))) {
libssh2_sftp_mkdir_ex(sftp, SSH2_URL_STR(resource->path), p - SSH2_URL_STR(resource->path), mode);
}
}
result = libssh2_sftp_mkdir(sftp, SSH2_URL_STR(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, const char *url, int options, php_stream_context *context)
{
LIBSSH2_SESSION *session = NULL;
LIBSSH2_SFTP *sftp = NULL;
zend_resource *rsrc = NULL, *sftp_rsrc = NULL;
php_url *resource;
int result;
resource = php_ssh2_fopen_wraper_parse_path(url, "sftp", context, &session, &rsrc, &sftp, &sftp_rsrc);
if (!resource || !session || !sftp || !resource->path) {
if (resource) {
php_url_free(resource);
}
return 0;
}
result = libssh2_sftp_rmdir(sftp, SSH2_URL_STR(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;
}
/* }}} */
static php_stream_wrapper_ops php_ssh2_sftp_wrapper_ops = {
php_ssh2_sftp_stream_opener,
NULL, /* close */
NULL, /* stat */
php_ssh2_sftp_urlstat,
php_ssh2_sftp_dirstream_opener,
PHP_SSH2_SFTP_WRAPPER_NAME,
php_ssh2_sftp_unlink,
php_ssh2_sftp_rename,
php_ssh2_sftp_mkdir,
php_ssh2_sftp_rmdir,
};
php_stream_wrapper php_ssh2_sftp_wrapper = {
&php_ssh2_sftp_wrapper_ops,
NULL,
1,
};
/* *****************
* 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(), "r", &zsession) == FAILURE) {
return;
}
if ((session = (LIBSSH2_SESSION *)zend_fetch_resource(Z_RES_P(zsession), PHP_SSH2_SESSION_RES_NAME, le_ssh2_session)) == NULL) {
RETURN_FALSE;
}
sftp = libssh2_sftp_init(session);
if (!sftp) {
char *sess_err = "Unknown";
libssh2_session_last_error(session, &sess_err, NULL, 0);
php_error_docref(NULL, 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_rsrc = Z_RES_P(zsession);
Z_ADDREF_P(zsession);
RETURN_RES(zend_register_resource(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;
zend_string *src, *dst;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "rSS", &zsftp, &src, &dst) == FAILURE) {
return;
}
if ((data = (php_ssh2_sftp_data *)zend_fetch_resource(Z_RES_P(zsftp), PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp)) == NULL) {
RETURN_FALSE;
}
RETURN_BOOL(!libssh2_sftp_rename_ex(data->sftp, src->val, src->len, dst->val, 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;
zend_string *filename;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "rS", &zsftp, &filename) == FAILURE) {
return;
}
if ((data = (php_ssh2_sftp_data *)zend_fetch_resource(Z_RES_P(zsftp), PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp)) == NULL) {
RETURN_FALSE;
}
RETURN_BOOL(!libssh2_sftp_unlink_ex(data->sftp, filename->val, filename->len));
}
/* }}} */
/* {{{ proto bool ssh2_sftp_mkdir(resource sftp, string dirname[, int mode[, int recursive]])
*/
PHP_FUNCTION(ssh2_sftp_mkdir)
{
php_ssh2_sftp_data *data;
zval *zsftp;
zend_string *dirname;
zend_long mode = 0777;
zend_bool recursive = 0;
char *p;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "rS|lb", &zsftp, &dirname, &mode, &recursive) == FAILURE) {
return;
}
if (!dirname) {
RETURN_FALSE;
}
if ((data = (php_ssh2_sftp_data *)zend_fetch_resource(Z_RES_P(zsftp), PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp)) == NULL) {
RETURN_FALSE;
}
if (recursive) {
/* Just attempt to make every directory, some will fail, but we only care about the last success/failure */
p = dirname->val;
while ((p = strchr(p + 1, '/'))) {
if ((p - dirname->val) + 1 == dirname->len) {
break;
}
libssh2_sftp_mkdir_ex(data->sftp, dirname->val, p - dirname->val, mode);
}
}
RETURN_BOOL(!libssh2_sftp_mkdir_ex(data->sftp, dirname->val, dirname->len, mode));
}
/* }}} */
/* {{{ proto bool ssh2_sftp_rmdir(resource sftp, string dirname)
*/
PHP_FUNCTION(ssh2_sftp_rmdir)
{
php_ssh2_sftp_data *data;
zval *zsftp;
zend_string *dirname;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "rS", &zsftp, &dirname) == FAILURE) {
return;
}
if ((data = (php_ssh2_sftp_data *)zend_fetch_resource(Z_RES_P(zsftp), PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp)) == NULL) {
RETURN_FALSE;
}
RETURN_BOOL(!libssh2_sftp_rmdir_ex(data->sftp, dirname->val, dirname->len));
}
/* }}} */
/* {{{ proto bool ssh2_sftp_chmod(resource sftp, string filename, int mode)
*/
PHP_FUNCTION(ssh2_sftp_chmod)
{
php_ssh2_sftp_data *data;
zval *zsftp;
zend_string *filename;
zend_long mode;
LIBSSH2_SFTP_ATTRIBUTES attrs;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "rSl", &zsftp, &filename, &mode) == FAILURE) {
return;
}
if (ZSTR_LEN(filename) < 1) {
RETURN_FALSE;
}
if ((data = (php_ssh2_sftp_data *)zend_fetch_resource(Z_RES_P(zsftp), PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp)) == NULL) {
RETURN_FALSE;
}
attrs.permissions = mode;
attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS;
RETURN_BOOL(!libssh2_sftp_stat_ex(data->sftp, filename->val, 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;
zend_string *path;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "rS", &zsftp, &path) == FAILURE) {
return;
}
if ((data = (php_ssh2_sftp_data *)zend_fetch_resource(Z_RES_P(zsftp), PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp)) == NULL) {
RETURN_FALSE;
}
if (libssh2_sftp_stat_ex(data->sftp, path->val, path->len, stat_type, &attrs)) {
php_error_docref(NULL, 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;
zend_string *targ, *link;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "rSS", &zsftp, &targ, &link) == FAILURE) {
return;
}
if ((data = (php_ssh2_sftp_data *)zend_fetch_resource(Z_RES_P(zsftp), PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp)) == NULL) {
RETURN_FALSE;
}
RETURN_BOOL(!libssh2_sftp_symlink_ex(data->sftp, targ->val, targ->len, link->val, 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;
zend_string *link;
int targ_len = 0;
char targ[8192];
if (zend_parse_parameters(ZEND_NUM_ARGS(), "rS", &zsftp, &link) == FAILURE) {
return;
}
if ((data = (php_ssh2_sftp_data *)zend_fetch_resource(Z_RES_P(zsftp), PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp)) == NULL) {
RETURN_FALSE;
}
if ((targ_len = libssh2_sftp_symlink_ex(data->sftp, link->val, link->len, targ, 8192, LIBSSH2_SFTP_READLINK)) < 0) {
php_error_docref(NULL, E_WARNING, "Unable to read link '%s'", ZSTR_VAL(link));
RETURN_FALSE;
}
RETURN_STRINGL(targ, targ_len);
}
/* }}} */
/* {{{ proto string ssh2_sftp_realpath(resource sftp, string filename)
*/
PHP_FUNCTION(ssh2_sftp_realpath)
{
php_ssh2_sftp_data *data;
zval *zsftp;
zend_string *link;
int targ_len = 0;
char targ[8192];
if (zend_parse_parameters(ZEND_NUM_ARGS(), "rS", &zsftp, &link) == FAILURE) {
return;
}
if ((data = (php_ssh2_sftp_data *)zend_fetch_resource(Z_RES_P(zsftp), PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp)) == NULL) {
RETURN_FALSE;
}
if (data->session_rsrc->ptr == NULL) {
RETURN_FALSE;
}
if ((targ_len = libssh2_sftp_symlink_ex(data->sftp, link->val, link->len, targ, 8192, LIBSSH2_SFTP_REALPATH)) < 0) {
php_error_docref(NULL, E_WARNING, "Unable to resolve realpath for '%s'", link->val);
RETURN_FALSE;
}
RETURN_STRINGL(targ, targ_len);
}
/* }}} */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* indent-tabs-mode: t
* End:
*/
ssh2-1.4.1/LICENSE 0000644 0001750 0001750 00000006221 14561537332 012677 0 ustar derick derick --------------------------------------------------------------------
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
. ssh2-1.4.1/README.md 0000644 0001750 0001750 00000001100 14561537332 013140 0 ustar derick derick ssh2
====
Bindings for the libssh2 library.
Provides bindings to the functions of [libssh2](http:/libssh2.org/) which implements the SSH2 protocol.
[The ssh2 extension at the PECL Repository website](http://pecl.php.net/package/ssh2)
Documentation
=============
For documentation of the functions that this extension provides can be found here: http://www.php.net/ssh2
Contribute
==========
Your contributions and bugreports are highly appreciated. To contribute, fork and create a pull request. To report a
bug use the [PHP Bug Tracking System](https://bugs.php.net/)