PageRenderTime 172ms CodeModel.GetById 16ms app.highlight 140ms RepoModel.GetById 1ms app.codeStats 0ms

/php-pecl-ssh2-0.11.3/ssh2-0.11.3/ssh2.c

#
C | 1343 lines | 959 code | 201 blank | 183 comment | 190 complexity | d9800f20e0598cbd97f736df5ed4e2ba MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception
   1/*
   2  +----------------------------------------------------------------------+
   3  | PHP Version 4                                                        |
   4  +----------------------------------------------------------------------+
   5  | Copyright (c) 1997-2006 The PHP Group                                |
   6  +----------------------------------------------------------------------+
   7  | This source file is subject to version 2.02 of the PHP license,      |
   8  | that is bundled with this package in the file LICENSE, and is        |
   9  | available at through the world-wide-web at                           |
  10  | http://www.php.net/license/2_02.txt.                                 |
  11  | If you did not receive a copy of the PHP license and are unable to   |
  12  | obtain it through the world-wide-web, please send a note to          |
  13  | license@php.net so we can mail you a copy immediately.               |
  14  +----------------------------------------------------------------------+
  15  | Author: Sara Golemon <pollita@php.net>                               |
  16  +----------------------------------------------------------------------+
  17
  18  $Id: ssh2.c 317115 2011-09-21 17:40:23Z bjori $ 
  19*/
  20
  21#ifdef HAVE_CONFIG_H
  22#include "config.h"
  23#endif
  24
  25#include "php.h"
  26#include "ext/standard/info.h"
  27#include "ext/standard/file.h"
  28#include "php_ssh2.h"
  29#include "main/php_network.h"
  30
  31#if (OPENSSL_VERSION_NUMBER >= 0x00908000L)
  32#include <openssl/applink.c>
  33#endif
  34
  35/* Internal Constants */
  36#ifndef SHA_DIGEST_LENGTH
  37#define SHA_DIGEST_LENGTH	20
  38#endif
  39
  40#ifndef MD5_DIGEST_LENGTH
  41#define MD5_DIGEST_LENGTH	16
  42#endif
  43
  44/* True global resources - no need for thread safety here */
  45int le_ssh2_session;
  46#ifdef PHP_SSH2_REMOTE_FORWARDING
  47int le_ssh2_listener;
  48#endif
  49int le_ssh2_sftp;
  50#ifdef PHP_SSH2_PUBLICKEY_SUBSYSTEM
  51int le_ssh2_pkey_subsys;
  52#endif
  53
  54ZEND_BEGIN_ARG_INFO(php_ssh2_first_arg_force_ref, 0)
  55    ZEND_ARG_PASS_INFO(1)
  56ZEND_END_ARG_INFO()
  57
  58/* *************
  59   * Callbacks *
  60   ************* */
  61
  62#ifdef ZTS
  63#define PHP_SSH2_TSRMLS_FETCH()		TSRMLS_D = *(void****)abstract;
  64#else
  65#define PHP_SSH2_TSRMLS_FETCH()
  66#endif
  67
  68/* {{{ php_ssh2_alloc_cb
  69 * Wrap emalloc()
  70 */
  71static LIBSSH2_ALLOC_FUNC(php_ssh2_alloc_cb)
  72{
  73	return emalloc(count);
  74}
  75/* }}} */
  76
  77/* {{{ php_ssh2_free_cb
  78 * Wrap efree()
  79 */
  80static LIBSSH2_FREE_FUNC(php_ssh2_free_cb)
  81{
  82	efree(ptr);
  83}
  84/* }}} */
  85
  86/* {{{ php_ssh2_realloc_cb
  87 * Wrap erealloc()
  88 */
  89static LIBSSH2_REALLOC_FUNC(php_ssh2_realloc_cb)
  90{
  91	return erealloc(ptr, count);
  92}
  93/* }}} */
  94
  95/* {{{ php_ssh2_debug_cb
  96 * Debug packets
  97 */
  98LIBSSH2_DEBUG_FUNC(php_ssh2_debug_cb)
  99{
 100	php_ssh2_session_data *data;
 101	zval *zdisplay, *zmessage, *zlanguage;
 102	zval **args[3];
 103	SSH2_TSRMLS_FETCH(*abstract);
 104
 105	if (!abstract || !*abstract) {
 106		return;
 107	}
 108	data = (php_ssh2_session_data*)*abstract;
 109	if (!data->debug_cb) {
 110		return;
 111	}
 112
 113	MAKE_STD_ZVAL(zmessage);
 114	ZVAL_STRINGL(zmessage, (char*)message, message_len, 1);
 115	args[0] = &zmessage;
 116
 117	MAKE_STD_ZVAL(zlanguage);
 118	ZVAL_STRINGL(zlanguage, (char*)language, language_len, 1);
 119	args[1] = &zlanguage;
 120
 121	MAKE_STD_ZVAL(zdisplay);
 122	ZVAL_LONG(zdisplay, always_display);
 123	args[2] = &zdisplay;
 124
 125	if (FAILURE == call_user_function_ex(NULL, NULL, data->disconnect_cb, NULL, 3, args, 0, NULL TSRMLS_CC)) {
 126		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failure calling disconnect callback");
 127	}
 128	zval_ptr_dtor(&zdisplay);
 129	zval_ptr_dtor(&zmessage);
 130	zval_ptr_dtor(&zlanguage);
 131}
 132/* }}} */
 133
 134/* {{{ php_ssh2_ignore_cb
 135 * Ignore packets
 136 */
 137LIBSSH2_IGNORE_FUNC(php_ssh2_ignore_cb)
 138{
 139	php_ssh2_session_data *data;
 140	zval *zretval = NULL, *zmessage;
 141	zval **args[1];
 142	SSH2_TSRMLS_FETCH(*abstract);
 143
 144	if (!abstract || !*abstract) {
 145		return;
 146	}
 147	data = (php_ssh2_session_data*)*abstract;
 148	if (!data->ignore_cb) {
 149		return;
 150	}
 151
 152	MAKE_STD_ZVAL(zmessage);
 153	ZVAL_STRINGL(zmessage, (char*)message, message_len, 1);
 154	args[0] = &zmessage;
 155
 156	if (FAILURE == call_user_function_ex(NULL, NULL, data->ignore_cb, &zretval, 1, args, 0, NULL TSRMLS_CC)) {
 157		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failure calling ignore callback");
 158	}
 159	zval_ptr_dtor(&zmessage);
 160	if (zretval) {
 161		zval_ptr_dtor(&zretval);
 162	}
 163}
 164/* }}} */
 165
 166/* {{{ php_ssh2_macerror_cb
 167 * Called when a MAC error occurs, offers the chance to ignore
 168 * WHY ARE YOU IGNORING MAC ERRORS??????
 169 */
 170LIBSSH2_MACERROR_FUNC(php_ssh2_macerror_cb)
 171{
 172	php_ssh2_session_data *data;
 173	zval *zretval = NULL, *zpacket;
 174	zval **args[1];
 175	int retval = -1;
 176	SSH2_TSRMLS_FETCH(*abstract);
 177
 178	if (!abstract || !*abstract) {
 179		return -1;
 180	}
 181	data = (php_ssh2_session_data*)*abstract;
 182	if (!data->macerror_cb) {
 183		return -1;
 184	}
 185
 186	MAKE_STD_ZVAL(zpacket);
 187	ZVAL_STRINGL(zpacket, (char*)packet, packet_len, 1);
 188	args[0] = &zpacket;
 189
 190	if (FAILURE == call_user_function_ex(NULL, NULL, data->macerror_cb, &zretval, 1, args, 0, NULL TSRMLS_CC)) {
 191		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failure calling macerror callback");
 192	} else {
 193		retval = zval_is_true(zretval) ? 0 : -1;
 194	}
 195	zval_ptr_dtor(&zpacket);
 196	if (zretval) {
 197		zval_ptr_dtor(&zretval);
 198	}
 199
 200	return retval;
 201}
 202/* }}} */
 203
 204/* {{{ php_ssh2_disconnect_cb
 205 * Connection closed by foreign host
 206 */
 207LIBSSH2_DISCONNECT_FUNC(php_ssh2_disconnect_cb)
 208{
 209	php_ssh2_session_data *data;
 210	zval *zreason, *zmessage, *zlanguage;
 211	zval **args[3];
 212	SSH2_TSRMLS_FETCH(*abstract);
 213
 214	if (!abstract || !*abstract) {
 215		return;
 216	}
 217	data = (php_ssh2_session_data*)*abstract;
 218	if (!data->disconnect_cb) {
 219		return;
 220	}
 221
 222	MAKE_STD_ZVAL(zreason);
 223	ZVAL_LONG(zreason, reason);
 224	args[0] = &zreason;
 225
 226	MAKE_STD_ZVAL(zmessage);
 227	ZVAL_STRINGL(zmessage, (char*)message, message_len, 1);
 228	args[1] = &zmessage;
 229
 230	MAKE_STD_ZVAL(zlanguage);
 231	ZVAL_STRINGL(zlanguage, (char*)language, language_len, 1);
 232	args[2] = &zlanguage;
 233
 234	if (FAILURE == call_user_function_ex(NULL, NULL, data->disconnect_cb, NULL, 3, args, 0, NULL TSRMLS_CC)) {
 235		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failure calling disconnect callback");
 236	}
 237	zval_ptr_dtor(&zreason);
 238	zval_ptr_dtor(&zmessage);
 239	zval_ptr_dtor(&zlanguage);
 240}
 241/* }}} */
 242
 243/* *****************
 244   * Userspace API *
 245   ***************** */
 246
 247/* {{{ php_ssh2_set_callback
 248 * Try to set a method if it's passed in with the hash table
 249 */
 250static int php_ssh2_set_callback(LIBSSH2_SESSION *session, HashTable *ht, char *callback, int callback_len, int callback_type, php_ssh2_session_data *data TSRMLS_DC)
 251{
 252	zval **handler, *copyval;
 253	void *internal_handler;
 254
 255	if (zend_hash_find(ht, callback, callback_len + 1, (void**)&handler) == FAILURE) {
 256		return 0;
 257	}
 258
 259	if (!handler || !*handler || !zend_is_callable(*handler, 0, NULL ZEND_IS_CALLABLE_TSRMLS_CC)) {
 260		return -1;
 261	}
 262
 263	ALLOC_INIT_ZVAL(copyval);
 264	*copyval = **handler;
 265	zval_copy_ctor(copyval);
 266
 267	switch (callback_type) {
 268		case LIBSSH2_CALLBACK_IGNORE:
 269			internal_handler = php_ssh2_ignore_cb;
 270			if (data->ignore_cb) {
 271				zval_ptr_dtor(&data->ignore_cb);
 272			}
 273			data->ignore_cb = copyval;
 274			break;
 275		case LIBSSH2_CALLBACK_DEBUG:
 276			internal_handler = php_ssh2_debug_cb;
 277			if (data->debug_cb) {
 278				zval_ptr_dtor(&data->debug_cb);
 279			}
 280			data->debug_cb = copyval;
 281			break;
 282		case LIBSSH2_CALLBACK_MACERROR:
 283			internal_handler = php_ssh2_macerror_cb;
 284			if (data->macerror_cb) {
 285				zval_ptr_dtor(&data->macerror_cb);
 286			}
 287			data->macerror_cb = copyval;
 288			break;
 289		case LIBSSH2_CALLBACK_DISCONNECT:
 290			internal_handler = php_ssh2_disconnect_cb;
 291			if (data->disconnect_cb) {
 292				zval_ptr_dtor(&data->disconnect_cb);
 293			}
 294			data->disconnect_cb = copyval;
 295			break;
 296		default:
 297			zval_ptr_dtor(&copyval);
 298			return -1;
 299	}
 300
 301	libssh2_session_callback_set(session, callback_type, internal_handler);
 302
 303	return 0;
 304}
 305/* }}} */
 306
 307/* {{{ php_ssh2_set_method
 308 * Try to set a method if it's passed in with the hash table
 309 */
 310static int php_ssh2_set_method(LIBSSH2_SESSION *session, HashTable *ht, char *method, int method_len, int method_type)
 311{
 312	zval **value;
 313
 314	if (zend_hash_find(ht, method, method_len + 1, (void**)&value) == FAILURE) {
 315		return 0;
 316	}
 317
 318	if (!value || !*value || (Z_TYPE_PP(value) != IS_STRING)) {
 319		return -1;
 320	}
 321
 322	return libssh2_session_method_pref(session, method_type, Z_STRVAL_PP(value));
 323}
 324/* }}} */
 325
 326/* {{{ php_ssh2_session_connect
 327 * Connect to an SSH server with requested methods
 328 */
 329LIBSSH2_SESSION *php_ssh2_session_connect(char *host, int port, zval *methods, zval *callbacks TSRMLS_DC)
 330{
 331	LIBSSH2_SESSION *session;
 332	int socket;
 333	php_ssh2_session_data *data;
 334	struct timeval tv;
 335
 336	tv.tv_sec = FG(default_socket_timeout);
 337	tv.tv_usec = 0;
 338
 339#if PHP_MAJOR_VERSION > 5 || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 0)
 340	socket = php_network_connect_socket_to_host(host, port, SOCK_STREAM, 0, &tv, NULL, NULL, NULL, 0 TSRMLS_CC);
 341#elif PHP_MAJOR_VERSION == 5
 342	socket = php_network_connect_socket_to_host(host, port, SOCK_STREAM, 0, &tv, NULL, NULL TSRMLS_CC);
 343#else
 344	socket = php_hostconnect(host, port, SOCK_STREAM, &tv TSRMLS_CC);
 345#endif
 346
 347	if (socket <= 0) {
 348		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to connect to %s on port %d", host, port);
 349		return NULL;
 350	}
 351
 352	data = ecalloc(1, sizeof(php_ssh2_session_data));
 353	SSH2_TSRMLS_SET(data);
 354	data->socket = socket;
 355
 356	session = libssh2_session_init_ex(php_ssh2_alloc_cb, php_ssh2_free_cb, php_ssh2_realloc_cb, data);
 357	if (!session) {
 358		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to initialize SSH2 session");
 359		efree(data);
 360		closesocket(socket);
 361		return NULL;
 362	}
 363	libssh2_banner_set(session, LIBSSH2_SSH_DEFAULT_BANNER " PHP");
 364
 365	/* Override method preferences */
 366	if (methods) {
 367		zval **container;
 368
 369		if (php_ssh2_set_method(session, HASH_OF(methods), "kex", sizeof("kex") - 1, LIBSSH2_METHOD_KEX)) {
 370			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed overriding KEX method");
 371		}
 372		if (php_ssh2_set_method(session, HASH_OF(methods), "hostkey", sizeof("hostkey") - 1, LIBSSH2_METHOD_HOSTKEY)) {
 373			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed overriding HOSTKEY method");
 374		}
 375
 376		if (zend_hash_find(HASH_OF(methods), "client_to_server", sizeof("client_to_server"), (void**)&container) == SUCCESS &&
 377			container && *container && Z_TYPE_PP(container) == IS_ARRAY) {
 378			if (php_ssh2_set_method(session, HASH_OF(*container), "crypt", sizeof("crypt") - 1, LIBSSH2_METHOD_CRYPT_CS)) {
 379				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed overriding client to server CRYPT method");
 380			}
 381			if (php_ssh2_set_method(session, HASH_OF(*container), "mac", sizeof("mac") - 1, LIBSSH2_METHOD_MAC_CS)) {
 382				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed overriding client to server MAC method");
 383			}
 384			if (php_ssh2_set_method(session, HASH_OF(*container), "comp", sizeof("comp") - 1, LIBSSH2_METHOD_COMP_CS)) {
 385				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed overriding client to server COMP method");
 386			}
 387			if (php_ssh2_set_method(session, HASH_OF(*container), "lang", sizeof("lang") - 1, LIBSSH2_METHOD_LANG_CS)) {
 388				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed overriding client to server LANG method");
 389			}
 390		}
 391
 392		if (zend_hash_find(HASH_OF(methods), "server_to_client", sizeof("server_to_client"), (void**)&container) == SUCCESS &&
 393			container && *container && Z_TYPE_PP(container) == IS_ARRAY) {
 394			if (php_ssh2_set_method(session, HASH_OF(*container), "crypt", sizeof("crypt") - 1, LIBSSH2_METHOD_CRYPT_SC)) {
 395				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed overriding server to client CRYPT method");
 396			}
 397			if (php_ssh2_set_method(session, HASH_OF(*container), "mac", sizeof("mac") - 1, LIBSSH2_METHOD_MAC_SC)) {
 398				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed overriding server to client MAC method");
 399			}
 400			if (php_ssh2_set_method(session, HASH_OF(*container), "comp", sizeof("comp") - 1, LIBSSH2_METHOD_COMP_SC)) {
 401				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed overriding server to client COMP method");
 402			}
 403			if (php_ssh2_set_method(session, HASH_OF(*container), "lang", sizeof("lang") - 1, LIBSSH2_METHOD_LANG_SC)) {
 404				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed overriding server to client LANG method");
 405			}
 406		}
 407	}
 408
 409	/* Register Callbacks */
 410	if (callbacks) {
 411		/* ignore debug disconnect macerror */
 412
 413		if (php_ssh2_set_callback(session, HASH_OF(callbacks), "ignore", sizeof("ignore") - 1, LIBSSH2_CALLBACK_IGNORE, data TSRMLS_CC)) {
 414			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed setting IGNORE callback");
 415		}
 416
 417		if (php_ssh2_set_callback(session, HASH_OF(callbacks), "debug", sizeof("debug") - 1, LIBSSH2_CALLBACK_DEBUG, data TSRMLS_CC)) {
 418			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed setting DEBUG callback");
 419		}
 420
 421		if (php_ssh2_set_callback(session, HASH_OF(callbacks), "macerror", sizeof("macerror") - 1, LIBSSH2_CALLBACK_MACERROR, data TSRMLS_CC)) {
 422			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed setting MACERROR callback");
 423		}
 424
 425		if (php_ssh2_set_callback(session, HASH_OF(callbacks), "disconnect", sizeof("disconnect") - 1, LIBSSH2_CALLBACK_DISCONNECT, data TSRMLS_CC)) {
 426			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed setting DISCONNECT callback");
 427		}
 428	}
 429
 430	if (libssh2_session_startup(session, socket)) {
 431		int last_error = 0;
 432		char *error_msg = NULL;
 433
 434		last_error = libssh2_session_last_error(session, &error_msg, NULL, 0);
 435		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error starting up SSH connection(%d): %s", last_error, error_msg);
 436		closesocket(socket);
 437		libssh2_session_free(session);
 438		efree(data);
 439		return NULL;
 440	}
 441
 442	return session;
 443}
 444/* }}} */
 445
 446/* {{{ proto resource ssh2_connect(string host[, int port[, array methods[, array callbacks]]])
 447 * Establish a connection to a remote SSH server and return a resource on success, false on error
 448 */
 449PHP_FUNCTION(ssh2_connect)
 450{
 451	LIBSSH2_SESSION *session;
 452	zval *methods = NULL, *callbacks = NULL;
 453	char *host;
 454	long port = PHP_SSH2_DEFAULT_PORT;
 455	int host_len;
 456
 457	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|la!a!", &host, &host_len, &port, &methods, &callbacks) == FAILURE) {
 458		RETURN_FALSE;
 459	}
 460
 461	session = php_ssh2_session_connect(host, port, methods, callbacks TSRMLS_CC);
 462	if (!session) {
 463		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to connect to %s", host);
 464		RETURN_FALSE;
 465	}
 466
 467	ZEND_REGISTER_RESOURCE(return_value, session, le_ssh2_session);
 468}
 469/* }}} */
 470
 471/* {{{ proto array ssh2_methods_negotiated(resource session)
 472 * Return list of negotiaed methods
 473 */
 474PHP_FUNCTION(ssh2_methods_negotiated)
 475{
 476	LIBSSH2_SESSION *session;
 477	zval *zsession, *endpoint;
 478	char *kex, *hostkey, *crypt_cs, *crypt_sc, *mac_cs, *mac_sc, *comp_cs, *comp_sc, *lang_cs, *lang_sc;
 479
 480	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zsession) == FAILURE) {
 481		RETURN_FALSE;
 482	}
 483
 484	ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session);
 485
 486#if defined(LIBSSH2_APINO) && LIBSSH2_APINO < 200412301450
 487	libssh2_session_methods(session, &kex, &hostkey, &crypt_cs, &crypt_sc, &mac_cs, &mac_sc, &comp_cs, &comp_sc, &lang_cs, &lang_sc);
 488#else
 489	kex = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_KEX);
 490	hostkey = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_HOSTKEY);
 491	crypt_cs = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_CRYPT_CS);
 492	crypt_sc = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_CRYPT_SC);
 493	mac_cs = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_MAC_CS);
 494	mac_sc = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_MAC_SC);
 495	comp_cs = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_COMP_CS);
 496	comp_sc = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_COMP_SC);
 497	lang_cs = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_LANG_CS);
 498	lang_sc = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_LANG_SC);
 499#endif
 500
 501	array_init(return_value);
 502	add_assoc_string(return_value, "kex", kex, 1);
 503	add_assoc_string(return_value, "hostkey", hostkey, 1);
 504
 505	ALLOC_INIT_ZVAL(endpoint);
 506	array_init(endpoint);
 507	add_assoc_string(endpoint, "crypt", crypt_cs, 1);
 508	add_assoc_string(endpoint, "mac", mac_cs, 1);
 509	add_assoc_string(endpoint, "comp", comp_cs, 1);
 510	add_assoc_string(endpoint, "lang", lang_cs, 1);
 511	add_assoc_zval(return_value, "client_to_server", endpoint);
 512
 513	ALLOC_INIT_ZVAL(endpoint);
 514	array_init(endpoint);
 515	add_assoc_string(endpoint, "crypt", crypt_sc, 1);
 516	add_assoc_string(endpoint, "mac", mac_sc, 1);
 517	add_assoc_string(endpoint, "comp", comp_sc, 1);
 518	add_assoc_string(endpoint, "lang", lang_sc, 1);
 519	add_assoc_zval(return_value, "server_to_client", endpoint);
 520}
 521/* }}} */
 522
 523/* {{{ proto string ssh2_fingerprint(resource session[, int flags])
 524 * Returns a server hostkey hash from an active session
 525 * Defaults to MD5 fingerprint encoded as ASCII hex values
 526 */
 527PHP_FUNCTION(ssh2_fingerprint)
 528{
 529	LIBSSH2_SESSION *session;
 530	zval *zsession;
 531	const char *fingerprint;
 532	long flags = 0;
 533	int i, fingerprint_len;
 534
 535	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|l", &zsession, &flags) == FAILURE) {
 536		RETURN_FALSE;
 537	}
 538	fingerprint_len = (flags & PHP_SSH2_FINGERPRINT_SHA1) ? SHA_DIGEST_LENGTH : MD5_DIGEST_LENGTH;
 539
 540	ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session);
 541
 542	fingerprint = (char*)libssh2_hostkey_hash(session, (flags & PHP_SSH2_FINGERPRINT_SHA1) ? LIBSSH2_HOSTKEY_HASH_SHA1 : LIBSSH2_HOSTKEY_HASH_MD5);
 543	if (!fingerprint) {
 544		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to retreive fingerprint from specified session");
 545		RETURN_FALSE;
 546	}
 547
 548	for(i = 0; i < fingerprint_len; i++) {
 549		if (fingerprint[i] != '\0') {
 550			goto fingerprint_good;
 551		}
 552	}
 553	php_error_docref(NULL TSRMLS_CC, E_WARNING, "No fingerprint available using specified hash");
 554	RETURN_NULL();
 555 fingerprint_good:
 556	if (flags & PHP_SSH2_FINGERPRINT_RAW) {
 557		RETURN_STRINGL(fingerprint, fingerprint_len, 1);
 558	} else {
 559		char *hexchars;
 560
 561		hexchars = emalloc((fingerprint_len * 2) + 1);
 562		for(i = 0; i < fingerprint_len; i++) {
 563			snprintf(hexchars + (2 * i), 3, "%02X", (unsigned char)fingerprint[i]);
 564		}
 565		RETURN_STRINGL(hexchars, 2 * fingerprint_len, 0);
 566	}
 567}
 568/* }}} */
 569
 570/* {{{ proto array ssh2_auth_none(resource session, string username)
 571 * Attempt "none" authentication, returns a list of allowed methods on failed authentication, 
 572 * false on utter failure, or true on success
 573 */
 574PHP_FUNCTION(ssh2_auth_none)
 575{
 576	LIBSSH2_SESSION *session;
 577	zval *zsession;
 578	char *username, *methods, *s, *p;
 579	int username_len;
 580
 581	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &zsession, &username, &username_len) == FAILURE) {
 582		RETURN_FALSE;
 583	}
 584
 585	ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session);
 586
 587	s = methods = libssh2_userauth_list(session, username, username_len);
 588	if (!methods) {
 589		/* Either bad failure, or unexpected success */
 590		RETURN_BOOL(libssh2_userauth_authenticated(session));
 591	}
 592
 593	array_init(return_value);
 594	while ((p = strchr(s, ','))) {
 595		if ((p - s) > 0) {
 596			add_next_index_stringl(return_value, s, p - s, 1);
 597		}
 598		s = p + 1;
 599	}
 600	if (strlen(s)) {
 601		add_next_index_string(return_value, s, 1);
 602	}
 603}
 604/* }}} */
 605
 606/* {{{ proto bool ssh2_auth_password(resource session, string username, string password)
 607 * Authenticate over SSH using a plain password
 608 */
 609PHP_FUNCTION(ssh2_auth_password)
 610{
 611	LIBSSH2_SESSION *session;
 612	zval *zsession;
 613	char *username, *password;
 614	int username_len, password_len;
 615
 616	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rss", &zsession, &username, &username_len, &password, &password_len) == FAILURE) {
 617		RETURN_FALSE;
 618	}
 619
 620	ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session);
 621
 622	/* TODO: Support password change callback */
 623	if (libssh2_userauth_password_ex(session, username, username_len, password, password_len, NULL)) {
 624		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Authentication failed for %s using password", username);
 625		RETURN_FALSE;
 626	}
 627
 628	RETURN_TRUE;
 629}
 630/* }}} */
 631
 632/* {{{ proto bool ssh2_auth_pubkey_file(resource session, string username, string pubkeyfile, string privkeyfile[, string passphrase])
 633 * Authenticate using a public key
 634 */
 635PHP_FUNCTION(ssh2_auth_pubkey_file)
 636{
 637	LIBSSH2_SESSION *session;
 638	zval *zsession;
 639	char *username, *pubkey, *privkey, *passphrase = NULL;
 640	int username_len, pubkey_len, privkey_len, passphrase_len;
 641
 642	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsss|s", &zsession,	&username, &username_len,
 643																				&pubkey, &pubkey_len,
 644																				&privkey, &privkey_len,
 645																				&passphrase, &passphrase_len) == FAILURE) {
 646		RETURN_FALSE;
 647	}
 648
 649	if (SSH2_OPENBASEDIR_CHECKPATH(pubkey) || SSH2_OPENBASEDIR_CHECKPATH(privkey)) {
 650		RETURN_FALSE;
 651	}
 652
 653	ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session);
 654
 655	/* TODO: Support passphrase callback */
 656	if (libssh2_userauth_publickey_fromfile_ex(session, username, username_len, pubkey, privkey, passphrase)) {
 657		char *buf;
 658		int len;
 659		libssh2_session_last_error(session, &buf, &len, 0);
 660		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Authentication failed for %s using public key: %s", username, buf);
 661		RETURN_FALSE;
 662	}
 663
 664	RETURN_TRUE;
 665}
 666/* }}} */
 667
 668#ifdef PHP_SSH2_HOSTBASED_AUTH
 669/* {{{ proto bool ssh2_auth_hostbased_file(resource session, string username, string local_hostname, string pubkeyfile, string privkeyfile[, string passphrase[, string local_username]])
 670 * Authenticate using a hostkey
 671 */
 672PHP_FUNCTION(ssh2_auth_hostbased_file)
 673{
 674	LIBSSH2_SESSION *session;
 675	zval *zsession;
 676	char *username, *hostname, *pubkey, *privkey, *passphrase = NULL, *local_username = NULL;
 677	int username_len, hostname_len, pubkey_len, privkey_len, passphrase_len, local_username_len;
 678
 679	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rssss|s!s!", &zsession,	&username, &username_len,
 680																					&hostname, &hostname_len,
 681																					&pubkey, &pubkey_len,
 682																					&privkey, &privkey_len,
 683																					&passphrase, &passphrase_len,
 684																					&local_username, &local_username_len) == FAILURE) {
 685		RETURN_FALSE;
 686	}
 687
 688	if (SSH2_OPENBASEDIR_CHECKPATH(pubkey) || SSH2_OPENBASEDIR_CHECKPATH(privkey)) {
 689		RETURN_FALSE;
 690	}
 691
 692	ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session);
 693
 694	if (!local_username) {
 695		local_username = username;
 696		local_username_len = username_len;
 697	}
 698
 699	/* TODO: Support passphrase callback */
 700	if (libssh2_userauth_hostbased_fromfile_ex(session, username, username_len, pubkey, privkey, passphrase, hostname, hostname_len, local_username, local_username_len)) {
 701		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Authentication failed for %s using hostbased public key", username);
 702		RETURN_FALSE;
 703	}
 704
 705	RETURN_TRUE;
 706}
 707/* }}} */
 708#endif /* PHP_SSH2_HOSTBASED_AUTH */
 709
 710#ifdef PHP_SSH2_REMOTE_FORWARDING
 711/* {{{ proto resource ssh2_forward_listen(resource session, int port[, string host[, long max_connections]])
 712 * Bind a port on the remote server and listen for connections
 713 */
 714PHP_FUNCTION(ssh2_forward_listen)
 715{
 716	zval *zsession;
 717	LIBSSH2_SESSION *session;
 718	LIBSSH2_LISTENER *listener;
 719	php_ssh2_listener_data *data;
 720	long port;
 721	char *host = NULL;
 722	int host_len;
 723	long max_connections = PHP_SSH2_LISTEN_MAX_QUEUED;
 724
 725	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl|sl", &zsession, &port, &host, &host_len, &max_connections) == FAILURE) {
 726		RETURN_FALSE;
 727	}
 728
 729	ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session);
 730
 731	listener = libssh2_channel_forward_listen_ex(session, host, port, NULL, max_connections);	
 732
 733	if (!listener) {
 734		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failure listening on remote port");
 735		RETURN_FALSE;
 736	}
 737
 738	data = emalloc(sizeof(php_ssh2_listener_data));
 739	data->session = session;
 740	data->session_rsrcid = Z_LVAL_P(zsession);
 741	zend_list_addref(data->session_rsrcid);
 742	data->listener = listener;
 743
 744	ZEND_REGISTER_RESOURCE(return_value, data, le_ssh2_listener);
 745}
 746/* }}} */ 
 747
 748/* {{{ proto stream ssh2_forward_accept(resource listener[, string &shost[, long &sport]])
 749 * Accept a connection created by a listener
 750 */
 751PHP_FUNCTION(ssh2_forward_accept)
 752{
 753	zval *zlistener;
 754	php_ssh2_listener_data *data;
 755	LIBSSH2_CHANNEL *channel;
 756	php_ssh2_channel_data *channel_data;
 757	php_stream *stream;
 758
 759	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zlistener) == FAILURE) {
 760		RETURN_FALSE;
 761	}
 762
 763	ZEND_FETCH_RESOURCE(data, php_ssh2_listener_data*, &zlistener, -1, PHP_SSH2_LISTENER_RES_NAME, le_ssh2_listener);
 764
 765	channel = libssh2_channel_forward_accept(data->listener);
 766
 767	if (!channel) {
 768		RETURN_FALSE;
 769	}
 770
 771	channel_data = emalloc(sizeof(php_ssh2_channel_data));
 772	channel_data->channel = channel;
 773	channel_data->streamid = 0;
 774	channel_data->is_blocking = 0;
 775	channel_data->session_rsrc = data->session_rsrcid;
 776	channel_data->refcount = NULL;
 777
 778	stream = php_stream_alloc(&php_ssh2_channel_stream_ops, channel_data, 0, "r+");
 779	if (!stream) {
 780		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failure allocating stream");
 781		efree(channel_data);
 782		libssh2_channel_free(channel);
 783		RETURN_FALSE;
 784	}
 785	zend_list_addref(channel_data->session_rsrc);
 786
 787	php_stream_to_zval(stream, return_value);
 788}
 789/* }}} */
 790#endif /* PHP_SSH2_REMOTE_FORWARDING */
 791
 792#ifdef PHP_SSH2_POLL
 793/* {{{ proto int ssh2_poll(array &polldes[, int timeout])
 794 * Poll the channels/listeners/streams for events
 795 * Returns number of descriptors which returned non-zero revents
 796 * Input array should be of the form:
 797 * array(
 798 *   0 => array(
 799 *     [resource] => $channel,$listener, or $stream
 800 *     [events] => SSH2_POLL* flags bitwise ORed together
 801 *   ),
 802 *   1 => ...
 803 * )
 804 * Each subarray will be populated with an revents element on return
 805 */
 806PHP_FUNCTION(ssh2_poll)
 807{
 808	zval *zdesc, **subarray;
 809	long timeout = PHP_SSH2_DEFAULT_POLL_TIMEOUT;
 810	LIBSSH2_POLLFD *pollfds;
 811	int numfds, i = 0, fds_ready;
 812	int le_stream = php_file_le_stream();
 813	int le_pstream = php_file_le_pstream();
 814	zval ***pollmap;
 815
 816	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &zdesc, &timeout) == FAILURE) {
 817		RETURN_NULL();
 818	}
 819
 820	numfds = zend_hash_num_elements(Z_ARRVAL_P(zdesc));
 821	pollfds = safe_emalloc(sizeof(LIBSSH2_POLLFD), numfds, 0);
 822	pollmap = safe_emalloc(sizeof(zval**), numfds, 0);
 823
 824	for(zend_hash_internal_pointer_reset(Z_ARRVAL_P(zdesc));
 825		zend_hash_get_current_data(Z_ARRVAL_P(zdesc), (void**)&subarray) == SUCCESS;
 826		zend_hash_move_forward(Z_ARRVAL_P(zdesc))) {
 827		zval **tmpzval;
 828		int res_type = 0;
 829		void *res;
 830
 831		if (Z_TYPE_PP(subarray) != IS_ARRAY) {
 832			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid element in poll array, not a sub array");
 833			numfds--;
 834			continue;
 835		}
 836		if (zend_hash_find(Z_ARRVAL_PP(subarray), "events", sizeof("events"), (void**)&tmpzval) == FAILURE ||
 837			Z_TYPE_PP(tmpzval) != IS_LONG) {
 838			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid data in subarray, no events element, or not a bitmask");
 839			numfds--;
 840			continue;
 841		}
 842		pollfds[i].events = Z_LVAL_PP(tmpzval);
 843		if (zend_hash_find(Z_ARRVAL_PP(subarray), "resource", sizeof("resource"), (void**)&tmpzval) == FAILURE ||
 844			Z_TYPE_PP(tmpzval) != IS_RESOURCE) {
 845			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid data in subarray, no resource element, or not of type resource");
 846			numfds--;
 847			continue;
 848		}
 849		zend_list_find(Z_LVAL_PP(tmpzval), &res_type);
 850		res = zend_fetch_resource(tmpzval TSRMLS_CC, -1, "Poll Resource", NULL, 1, res_type);
 851		if (res_type == le_ssh2_listener) {
 852			pollfds[i].type = LIBSSH2_POLLFD_LISTENER;
 853			pollfds[i].fd.listener = ((php_ssh2_listener_data*)res)->listener;
 854		} else if ((res_type == le_stream || res_type == le_pstream) && 
 855				   ((php_stream*)res)->ops == &php_ssh2_channel_stream_ops) {
 856			pollfds[i].type = LIBSSH2_POLLFD_CHANNEL;
 857			pollfds[i].fd.channel = ((php_ssh2_channel_data*)(((php_stream*)res)->abstract))->channel;
 858			/* TODO: Add the ability to select against other stream types */
 859		} else {
 860			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid resource type in subarray: %s", zend_rsrc_list_get_rsrc_type(Z_LVAL_PP(tmpzval) TSRMLS_CC));
 861			numfds--;
 862			continue;
 863		}
 864		pollmap[i] = subarray;
 865		i++;
 866	}
 867
 868	fds_ready = libssh2_poll(pollfds, numfds, timeout * 1000);
 869
 870	for(i = 0; i < numfds; i++) {
 871		zval *subarray = *pollmap[i];
 872
 873		if (!Z_ISREF_P(subarray) && Z_REFCOUNT_P(subarray) > 1) {
 874			/* Make a new copy of the subarray zval* */
 875			MAKE_STD_ZVAL(subarray);
 876			*subarray = **pollmap[i];
 877
 878			/* Point the pData to the new zval* and duplicate its resources */
 879			*pollmap[i] = subarray;
 880			zval_copy_ctor(subarray);
 881
 882			/* Fixup its refcount */
 883			Z_UNSET_ISREF_P(subarray);
 884			Z_SET_REFCOUNT_P(subarray, 1);
 885		}
 886		zend_hash_del(Z_ARRVAL_P(subarray), "revents", sizeof("revents"));
 887		add_assoc_long(subarray, "revents", pollfds[i].revents);
 888
 889	}
 890	efree(pollmap);
 891	efree(pollfds);
 892
 893	RETURN_LONG(fds_ready);
 894}
 895/* }}} */
 896#endif /* PHP_SSH2_POLL */
 897
 898#ifdef PHP_SSH2_PUBLICKEY_SUBSYSTEM
 899/* ***********************
 900   * Publickey Subsystem *
 901   *********************** */
 902
 903/* {{{ proto resource ssh2_publickey_init(resource connection)
 904Initialize the publickey subsystem */
 905PHP_FUNCTION(ssh2_publickey_init)
 906{
 907	zval *zsession;
 908	LIBSSH2_SESSION *session;
 909	LIBSSH2_PUBLICKEY *pkey;
 910	php_ssh2_pkey_subsys_data *data;
 911
 912	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zsession) == FAILURE) {
 913		RETURN_FALSE;
 914	}
 915
 916	ZEND_FETCH_RESOURCE(session, LIBSSH2_SESSION*, &zsession, -1, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session);
 917
 918	pkey = libssh2_publickey_init(session);
 919
 920	if (!pkey) {
 921		int last_error = 0;
 922		char *error_msg = NULL;
 923
 924		last_error = libssh2_session_last_error(session, &error_msg, NULL, 0);
 925		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to initialize publickey subsystem(%d) %s", last_error, error_msg);
 926		RETURN_FALSE;
 927	}
 928
 929	data = emalloc(sizeof(php_ssh2_pkey_subsys_data));
 930	data->session = session;
 931	data->session_rsrcid = Z_LVAL_P(zsession);
 932	zend_list_addref(data->session_rsrcid);
 933	data->pkey = pkey;
 934
 935	ZEND_REGISTER_RESOURCE(return_value, data, le_ssh2_pkey_subsys);
 936}
 937/* }}} */
 938
 939/* {{{ proto bool ssh2_publickey_add(resource pkey, string algoname, string blob[, bool overwrite=FALSE [,array attributes=NULL]])
 940Add an additional publickey */
 941PHP_FUNCTION(ssh2_publickey_add)
 942{
 943	zval *zpkey_data, *zattrs = NULL;
 944	php_ssh2_pkey_subsys_data *data;
 945	char *algo, *blob;
 946	int algo_len, blob_len;
 947	unsigned long num_attrs = 0;
 948	libssh2_publickey_attribute *attrs = NULL;
 949	zend_bool overwrite = 0;
 950
 951	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rss|ba", &zpkey_data, &algo, &algo_len, &blob, &blob_len, &overwrite, &zattrs) == FAILURE) {
 952		RETURN_FALSE;
 953	}
 954
 955	ZEND_FETCH_RESOURCE(data, php_ssh2_pkey_subsys_data*, &zpkey_data, -1, PHP_SSH2_PKEY_SUBSYS_RES_NAME, le_ssh2_pkey_subsys);
 956
 957	if (zattrs) {
 958		HashPosition pos;
 959		zval **attr_val;
 960		unsigned long current_attr = 0;
 961
 962		num_attrs = zend_hash_num_elements(Z_ARRVAL_P(zattrs));
 963		attrs = safe_emalloc(num_attrs, sizeof(libssh2_publickey_attribute), 0);
 964
 965		for(zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(zattrs), &pos);
 966			zend_hash_get_current_data_ex(Z_ARRVAL_P(zattrs), (void**)&attr_val, &pos) == SUCCESS;
 967			zend_hash_move_forward_ex(Z_ARRVAL_P(zattrs), &pos)) {
 968			char *key;
 969			int key_len, type;
 970			long idx;
 971			zval copyval = **attr_val;
 972
 973			type = zend_hash_get_current_key_ex(Z_ARRVAL_P(zattrs), &key, &key_len, &idx, 0, &pos);
 974			if (type == HASH_KEY_NON_EXISTANT) {
 975				/* All but impossible */
 976				break;
 977			}
 978			if (type == HASH_KEY_IS_LONG) {
 979				/* Malformed, ignore */
 980				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Malformed attirbute array, contains numeric index");
 981				num_attrs--;
 982				continue;
 983			}
 984
 985			if (key_len == 0 || (key_len == 1 && *key == '*')) {
 986				/* Empty key, ignore */
 987				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty attribute key");
 988				num_attrs--;
 989				continue;
 990			}
 991
 992			zval_copy_ctor(&copyval);
 993			Z_UNSET_ISREF_P(&copyval);
 994			Z_SET_REFCOUNT_P(&copyval, 1);
 995			convert_to_string(&copyval);
 996
 997			if (*key == '*') {
 998				attrs[current_attr].mandatory = 1;
 999				attrs[current_attr].name = key + 1;
1000				attrs[current_attr].name_len = key_len - 2;
1001			} else {
1002				attrs[current_attr].mandatory = 0;
1003				attrs[current_attr].name = key;
1004				attrs[current_attr].name_len = key_len - 1;
1005			}
1006			attrs[current_attr].value_len = Z_STRLEN(copyval);
1007			attrs[current_attr].value = Z_STRVAL(copyval);
1008
1009			/* copyval deliberately not dtor'd, we're stealing the string */
1010			current_attr++;
1011		}
1012	}
1013
1014	if (libssh2_publickey_add_ex(data->pkey, algo, algo_len, blob, blob_len, overwrite, num_attrs, attrs)) {
1015		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to add %s key", algo);
1016		RETVAL_FALSE;
1017	} else {
1018		RETVAL_TRUE;
1019	}
1020
1021	if (attrs) {
1022		unsigned long i;
1023
1024		for(i = 0; i < num_attrs; i++) {
1025			/* name doesn't need freeing */
1026			efree(attrs[i].value);
1027		}
1028		efree(attrs);
1029	}
1030}
1031/* }}} */
1032
1033/* {{{ proto bool ssh2_publickey_remove(resource pkey, string algoname, string blob)
1034Remove a publickey entry */
1035PHP_FUNCTION(ssh2_publickey_remove)
1036{
1037	zval *zpkey_data;
1038	php_ssh2_pkey_subsys_data *data;
1039	char *algo, *blob;
1040	int algo_len, blob_len;
1041
1042	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rss", &zpkey_data, &algo, &algo_len, &blob, &blob_len) == FAILURE) {
1043		RETURN_FALSE;
1044	}
1045
1046	ZEND_FETCH_RESOURCE(data, php_ssh2_pkey_subsys_data*, &zpkey_data, -1, PHP_SSH2_PKEY_SUBSYS_RES_NAME, le_ssh2_pkey_subsys);
1047
1048	if (libssh2_publickey_remove_ex(data->pkey, algo, algo_len, blob, blob_len)) {
1049		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to remove %s key", algo);
1050		RETURN_FALSE;
1051	}
1052
1053	RETURN_TRUE;
1054}
1055/* }}} */
1056
1057/* {{{ proto array ssh2_publickey_list(resource pkey)
1058List currently installed publickey entries */
1059PHP_FUNCTION(ssh2_publickey_list)
1060{
1061	zval *zpkey_data;
1062	php_ssh2_pkey_subsys_data *data;
1063	unsigned long num_keys, i;
1064	libssh2_publickey_list *keys;
1065
1066	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zpkey_data) == FAILURE) {
1067		RETURN_FALSE;
1068	}
1069
1070	ZEND_FETCH_RESOURCE(data, php_ssh2_pkey_subsys_data*, &zpkey_data, -1, PHP_SSH2_PKEY_SUBSYS_RES_NAME, le_ssh2_pkey_subsys);
1071
1072	if (libssh2_publickey_list_fetch(data->pkey, &num_keys, &keys)) {
1073		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to list keys on remote server");
1074		RETURN_FALSE;
1075	}
1076
1077	array_init(return_value);
1078	for(i = 0; i < num_keys; i++) {
1079		zval *key, *attrs;
1080		unsigned long j;
1081
1082		MAKE_STD_ZVAL(key);
1083		array_init(key);
1084
1085		add_assoc_stringl(key, "name", keys[i].name, keys[i].name_len, 1);
1086		add_assoc_stringl(key, "blob", keys[i].blob, keys[i].blob_len, 1);
1087
1088		MAKE_STD_ZVAL(attrs);
1089		array_init(attrs);
1090		for(j = 0; j < keys[i].num_attrs; j++) {
1091			zval *attr;
1092
1093			MAKE_STD_ZVAL(attr);
1094			ZVAL_STRINGL(attr, keys[i].attrs[j].value, keys[i].attrs[j].value_len, 1);
1095			zend_hash_add(Z_ARRVAL_P(attrs), keys[i].attrs[j].name, keys[i].attrs[j].name_len + 1, (void**)&attr, sizeof(zval*), NULL);
1096		}
1097		add_assoc_zval(key, "attrs", attrs);
1098
1099		add_next_index_zval(return_value, key);
1100	}
1101
1102	libssh2_publickey_list_free(data->pkey, keys);
1103}
1104/* }}} */
1105#endif /* PHP_SSH2_PUBLICKEY_SUBSYSTEM */
1106
1107/* ***********************
1108   * Module Housekeeping *
1109   *********************** */
1110
1111static void php_ssh2_session_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
1112{
1113	LIBSSH2_SESSION *session = (LIBSSH2_SESSION*)rsrc->ptr;
1114	php_ssh2_session_data **data = (php_ssh2_session_data**)libssh2_session_abstract(session);
1115
1116	libssh2_session_disconnect(session, "PECL/ssh2 (http://pecl.php.net/packages/ssh2)");
1117
1118	if (*data) {
1119		if ((*data)->ignore_cb) {
1120			zval_ptr_dtor(&(*data)->ignore_cb);
1121		}
1122		if ((*data)->debug_cb) {
1123			zval_ptr_dtor(&(*data)->debug_cb);
1124		}
1125		if ((*data)->macerror_cb) {
1126			zval_ptr_dtor(&(*data)->macerror_cb);
1127		}
1128		if ((*data)->disconnect_cb) {
1129			zval_ptr_dtor(&(*data)->disconnect_cb);
1130		}
1131
1132		closesocket((*data)->socket);
1133
1134		efree(*data);
1135		*data = NULL;
1136	}
1137
1138	libssh2_session_free(session);
1139}
1140
1141#ifdef PHP_SSH2_REMOTE_FORWARDING
1142static void php_ssh2_listener_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
1143{
1144	php_ssh2_listener_data *data = (php_ssh2_listener_data*)rsrc->ptr;
1145	LIBSSH2_LISTENER *listener = data->listener;
1146
1147	libssh2_channel_forward_cancel(listener);
1148	zend_list_delete(data->session_rsrcid);
1149	efree(data);
1150}
1151#endif /* PHP_SSH2_REMOTE_FORWARDING */
1152
1153#ifdef PHP_SSH2_PUBLICKEY_SUBSYSTEM
1154static void php_ssh2_pkey_subsys_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
1155{
1156	php_ssh2_pkey_subsys_data *data = (php_ssh2_pkey_subsys_data*)rsrc->ptr;
1157	LIBSSH2_PUBLICKEY *pkey = data->pkey;
1158
1159	libssh2_publickey_shutdown(pkey);
1160	zend_list_delete(data->session_rsrcid);
1161	efree(data);
1162}
1163#endif /* PHP_SSH2_PUBLICKEY_SUBSYSTEM */
1164
1165/* {{{ PHP_MINIT_FUNCTION
1166 */
1167PHP_MINIT_FUNCTION(ssh2)
1168{
1169	le_ssh2_session		= zend_register_list_destructors_ex(php_ssh2_session_dtor, NULL, PHP_SSH2_SESSION_RES_NAME, module_number);
1170#ifdef PHP_SSH2_REMOTE_FORWARDING
1171	le_ssh2_listener	= zend_register_list_destructors_ex(php_ssh2_listener_dtor, NULL, PHP_SSH2_LISTENER_RES_NAME, module_number);
1172#endif /* PHP_SSH2_REMOTE_FORWARDING */
1173	le_ssh2_sftp		= zend_register_list_destructors_ex(php_ssh2_sftp_dtor, NULL, PHP_SSH2_SFTP_RES_NAME, module_number);
1174#ifdef PHP_SSH2_PUBLICKEY_SUBSYSTEM
1175	le_ssh2_pkey_subsys	= zend_register_list_destructors_ex(php_ssh2_pkey_subsys_dtor, NULL, PHP_SSH2_PKEY_SUBSYS_RES_NAME, module_number);
1176#endif /* PHP_SSH2_PUBLICKEY_SUBSYSTEM */
1177
1178	REGISTER_LONG_CONSTANT("SSH2_FINGERPRINT_MD5",		PHP_SSH2_FINGERPRINT_MD5,		CONST_CS | CONST_PERSISTENT);
1179	REGISTER_LONG_CONSTANT("SSH2_FINGERPRINT_SHA1",		PHP_SSH2_FINGERPRINT_SHA1,		CONST_CS | CONST_PERSISTENT);
1180	REGISTER_LONG_CONSTANT("SSH2_FINGERPRINT_HEX",		PHP_SSH2_FINGERPRINT_HEX,		CONST_CS | CONST_PERSISTENT);
1181	REGISTER_LONG_CONSTANT("SSH2_FINGERPRINT_RAW",		PHP_SSH2_FINGERPRINT_RAW,		CONST_CS | CONST_PERSISTENT);
1182
1183	REGISTER_LONG_CONSTANT("SSH2_TERM_UNIT_CHARS",		PHP_SSH2_TERM_UNIT_CHARS,		CONST_CS | CONST_PERSISTENT);
1184	REGISTER_LONG_CONSTANT("SSH2_TERM_UNIT_PIXELS",		PHP_SSH2_TERM_UNIT_PIXELS,		CONST_CS | CONST_PERSISTENT);
1185
1186	REGISTER_STRING_CONSTANT("SSH2_DEFAULT_TERMINAL",	PHP_SSH2_DEFAULT_TERMINAL,		CONST_CS | CONST_PERSISTENT);
1187	REGISTER_LONG_CONSTANT("SSH2_DEFAULT_TERM_WIDTH",	PHP_SSH2_DEFAULT_TERM_WIDTH,	CONST_CS | CONST_PERSISTENT);
1188	REGISTER_LONG_CONSTANT("SSH2_DEFAULT_TERM_HEIGHT",	PHP_SSH2_DEFAULT_TERM_HEIGHT,	CONST_CS | CONST_PERSISTENT);
1189	REGISTER_LONG_CONSTANT("SSH2_DEFAULT_TERM_UNIT",	PHP_SSH2_DEFAULT_TERM_UNIT,		CONST_CS | CONST_PERSISTENT);
1190
1191	REGISTER_LONG_CONSTANT("SSH2_STREAM_STDIO",			0,								CONST_CS | CONST_PERSISTENT);
1192	REGISTER_LONG_CONSTANT("SSH2_STREAM_STDERR",		SSH_EXTENDED_DATA_STDERR,		CONST_CS | CONST_PERSISTENT);
1193
1194#ifdef PHP_SSH2_POLL
1195	/* events/revents */
1196	REGISTER_LONG_CONSTANT("SSH2_POLLIN",				LIBSSH2_POLLFD_POLLIN,			CONST_CS | CONST_PERSISTENT);
1197	REGISTER_LONG_CONSTANT("SSH2_POLLEXT",				LIBSSH2_POLLFD_POLLEXT,			CONST_CS | CONST_PERSISTENT);
1198	REGISTER_LONG_CONSTANT("SSH2_POLLOUT",				LIBSSH2_POLLFD_POLLOUT,			CONST_CS | CONST_PERSISTENT);
1199
1200	/* revents only */
1201	REGISTER_LONG_CONSTANT("SSH2_POLLERR",				LIBSSH2_POLLFD_POLLERR,			CONST_CS | CONST_PERSISTENT);
1202	REGISTER_LONG_CONSTANT("SSH2_POLLHUP",				LIBSSH2_POLLFD_POLLHUP,			CONST_CS | CONST_PERSISTENT);
1203	REGISTER_LONG_CONSTANT("SSH2_POLLNVAL",				LIBSSH2_POLLFD_POLLNVAL,		CONST_CS | CONST_PERSISTENT);
1204#if (defined(LIBSSH2_APINO) && (LIBSSH2_APINO > 200503221619)) || (defined(LIBSSH2_VERSION_NUM) && LIBSSH2_VERSION_NUM >= 0x001000)
1205	REGISTER_LONG_CONSTANT("SSH2_POLL_SESSION_CLOSED",	LIBSSH2_POLLFD_SESSION_CLOSED,	CONST_CS | CONST_PERSISTENT);
1206	REGISTER_LONG_CONSTANT("SSH2_POLL_CHANNEL_CLOSED",	LIBSSH2_POLLFD_CHANNEL_CLOSED,	CONST_CS | CONST_PERSISTENT);
1207	REGISTER_LONG_CONSTANT("SSH2_POLL_LISTENER_CLOSED",	LIBSSH2_POLLFD_LISTENER_CLOSED,	CONST_CS | CONST_PERSISTENT);
1208#endif /* >= LIBSSH2-0.10 */
1209#endif /* POLL */
1210
1211	return (php_register_url_stream_wrapper("ssh2.shell", &php_ssh2_stream_wrapper_shell TSRMLS_CC) == SUCCESS &&
1212			php_register_url_stream_wrapper("ssh2.exec", &php_ssh2_stream_wrapper_exec TSRMLS_CC) == SUCCESS &&
1213			php_register_url_stream_wrapper("ssh2.tunnel", &php_ssh2_stream_wrapper_tunnel TSRMLS_CC) == SUCCESS &&
1214			php_register_url_stream_wrapper("ssh2.scp", &php_ssh2_stream_wrapper_scp TSRMLS_CC) == SUCCESS &&
1215			php_register_url_stream_wrapper("ssh2.sftp", &php_ssh2_sftp_wrapper TSRMLS_CC) == SUCCESS) ? SUCCESS : FAILURE;
1216}
1217/* }}} */
1218
1219/* {{{ PHP_MSHUTDOWN_FUNCTION
1220 */
1221PHP_MSHUTDOWN_FUNCTION(ssh2)
1222{
1223	return (php_unregister_url_stream_wrapper("ssh2.shell" TSRMLS_CC) == SUCCESS &&
1224			php_unregister_url_stream_wrapper("ssh2.exec" TSRMLS_CC) == SUCCESS &&
1225			php_unregister_url_stream_wrapper("ssh2.tunnel" TSRMLS_CC) == SUCCESS &&
1226			php_unregister_url_stream_wrapper("ssh2.scp" TSRMLS_CC) == SUCCESS &&
1227			php_unregister_url_stream_wrapper("ssh2.sftp" TSRMLS_CC) == SUCCESS) ? SUCCESS : FAILURE;
1228}
1229/* }}} */
1230
1231/* {{{ PHP_MINFO_FUNCTION
1232 */
1233PHP_MINFO_FUNCTION(ssh2)
1234{
1235	php_info_print_table_start();
1236	php_info_print_table_header(2, "SSH2 support", "enabled");
1237	php_info_print_table_row(2, "extension version", PHP_SSH2_VERSION);
1238	php_info_print_table_row(2, "libssh2 version", LIBSSH2_VERSION);
1239	php_info_print_table_row(2, "banner", LIBSSH2_SSH_BANNER);
1240#ifdef PHP_SSH2_REMOTE_FORWARDING
1241	php_info_print_table_row(2, "remote forwarding", "enabled");
1242#endif
1243#ifdef PHP_SSH2_HOSTBASED_AUTH
1244	php_info_print_table_row(2, "hostbased auth", "enabled");
1245#endif
1246#ifdef PHP_SSH2_POLL
1247	php_info_print_table_row(2, "polling support", "enabled");
1248#endif
1249#ifdef PHP_SSH2_PUBLICKEY_SUBSYSTEM
1250	php_info_print_table_row(2, "publickey subsystem", "enabled");
1251#endif
1252	php_info_print_table_end();
1253}
1254/* }}} */
1255
1256/* {{{ ssh2_functions[]
1257 */
1258zend_function_entry ssh2_functions[] = {
1259	PHP_FE(ssh2_connect,						NULL)
1260	PHP_FE(ssh2_methods_negotiated,				NULL)
1261	PHP_FE(ssh2_fingerprint,					NULL)
1262
1263	PHP_FE(ssh2_auth_none,						NULL)
1264	PHP_FE(ssh2_auth_password,					NULL)
1265	PHP_FE(ssh2_auth_pubkey_file,				NULL)
1266#ifdef PHP_SSH2_HOSTBASED_AUTH
1267	PHP_FE(ssh2_auth_hostbased_file,			NULL)
1268#endif /* PHP_SSH2_HOSTBASED_AUTH */
1269
1270#ifdef PHP_SSH2_REMOTE_FORWARDING
1271	PHP_FE(ssh2_forward_listen,					NULL)
1272	PHP_FE(ssh2_forward_accept,					NULL)
1273#endif /* PHP_SSH2_REMOTE_FORWARDING */
1274
1275	/* Stream Stuff */
1276	PHP_FE(ssh2_shell,							NULL)
1277	PHP_FE(ssh2_exec,							NULL)
1278	PHP_FE(ssh2_tunnel,							NULL)
1279	PHP_FE(ssh2_scp_recv,						NULL)
1280	PHP_FE(ssh2_scp_send,						NULL)
1281	PHP_FE(ssh2_fetch_stream,					NULL)
1282#ifdef PHP_SSH2_POLL
1283	PHP_FE(ssh2_poll,							php_ssh2_first_arg_force_ref)
1284#endif
1285
1286	/* SFTP Stuff */
1287	PHP_FE(ssh2_sftp,							NULL)
1288
1289	/* SFTP Wrapper Ops */
1290	PHP_FE(ssh2_sftp_rename,					NULL)
1291	PHP_FE(ssh2_sftp_unlink,					NULL)
1292	PHP_FE(ssh2_sftp_mkdir,						NULL)
1293	PHP_FE(ssh2_sftp_rmdir,						NULL)
1294	PHP_FE(ssh2_sftp_stat,						NULL)
1295	PHP_FE(ssh2_sftp_lstat,						NULL)
1296	PHP_FE(ssh2_sftp_symlink,					NULL)
1297	PHP_FE(ssh2_sftp_readlink,					NULL)
1298	PHP_FE(ssh2_sftp_realpath,					NULL)
1299
1300	/* Publickey subsystem */
1301#ifdef PHP_SSH2_PUBLICKEY_SUBSYSTEM
1302	PHP_FE(ssh2_publickey_init,					NULL)
1303	PHP_FE(ssh2_publickey_add,					NULL)
1304	PHP_FE(ssh2_publickey_remove,				NULL)
1305	PHP_FE(ssh2_publickey_list,					NULL)
1306#endif
1307
1308	{NULL, NULL, NULL}
1309};
1310/* }}} */
1311
1312/* {{{ ssh2_module_entry
1313 */
1314zend_module_entry ssh2_module_entry = {
1315#if ZEND_MODULE_API_NO >= 20010901
1316	STANDARD_MODULE_HEADER,
1317#endif
1318	"ssh2",
1319	ssh2_functions,
1320	PHP_MINIT(ssh2),
1321	PHP_MSHUTDOWN(ssh2),
1322	NULL, /* RINIT */
1323	NULL, /* RSHUTDOWN */
1324	PHP_MINFO(ssh2),
1325#if ZEND_MODULE_API_NO >= 20010901
1326	PHP_SSH2_VERSION,
1327#endif
1328	STANDARD_MODULE_PROPERTIES
1329};
1330/* }}} */
1331
1332#ifdef COMPILE_DL_SSH2
1333ZEND_GET_MODULE(ssh2)
1334#endif
1335
1336/*
1337 * Local variables:
1338 * tab-width: 4
1339 * c-basic-offset: 4
1340 * End:
1341 * vim600: noet sw=4 ts=4 fdm=marker
1342 * vim<600: noet sw=4 ts=4
1343 */