├── TODO ├── tests ├── ssh2_auth.phpt ├── testkey_rsa.pub ├── bug79631.phpt ├── ssh2_skip.inc ├── ssh2_shell.phpt ├── bug63480.phpt ├── ssh2_exec.phpt ├── ssh2_auth_pubkey_file.phpt ├── ssh2_sftp_002.phpt ├── ssh2_auth_pubkey.phpt ├── ssh2_send_eof.phpt ├── ssh2_stream_select.phpt ├── ssh2_sftp_001.phpt ├── ssh2_test.inc ├── ssh2_connect.phpt └── testkey_rsa ├── .travis.yml ├── .gitignore ├── README.md ├── .travis └── build.sh ├── config.w32 ├── config.m4 ├── LICENSE ├── .github └── workflows │ └── build.yml ├── php_ssh2.h ├── package.xml ├── ssh2_sftp.c ├── ssh2_fopen_wrappers.c └── ssh2.c /TODO: -------------------------------------------------------------------------------- 1 | * Create README containing explanation how to setup test environment & execute tests [13-6-2012, langemeijer] 2 | * Create tests [13-6-2012, langemeijer] 3 | * Check all version strings before creating new release (As promised, see bug 59981) [13-6-2012, langemeijer] 4 | 5 | * Complete this todo list 6 | -------------------------------------------------------------------------------- /tests/ssh2_auth.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ssh2_auth_FOO() - Attempt to authenticate to a remote host 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 9 | --FILE-- 10 | 19 | --EXPECT-- 20 | done 21 | -------------------------------------------------------------------------------- /tests/ssh2_skip.inc: -------------------------------------------------------------------------------- 1 | 5 | --FILE-- 6 | 9 | --FILE-- 10 | 23 | --EXPECT-- 24 | yada yada 25 | -------------------------------------------------------------------------------- /tests/ssh2_exec.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ssh2_shell_test() - Tests opening a shell 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | > ~sshuser/.ssh/authorized_keys" 8 | sudo chown sshuser:sshuser ~sshuser/.ssh/authorized_keys 9 | sudo chmod 600 ~sshuser/.ssh/authorized_keys 10 | 11 | phpize 12 | ./configure 13 | make 14 | make install 15 | 16 | php -d variables_order=EGPCS run-tests.php -p $(which php) -d extension=$(pwd)/modules/ssh2.so -d variables_order=EGPCS -g "FAIL,XFAIL,BORK,WARN,LEAK,SKIP" --show-diff --offline --set-timeout 120 17 | -------------------------------------------------------------------------------- /tests/ssh2_auth_pubkey_file.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ssh2_auth_pubkey_file() - Tests authentication with a key 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 5 | --FILE-- 6 | 5 | --FILE-- 6 | 5 | --FILE-- 6 | 0) { 22 | while($line = fgets($shell)) { 23 | echo $line; 24 | } 25 | } 26 | $elapsed = time() - $start; 27 | var_dump(($elapsed < $timeout)); 28 | 29 | --EXPECTF-- 30 | bool(true) 31 | resource(%d) of type (stream) 32 | %a 33 | %a 34 | %a 35 | howdy 36 | %sbool(true) -------------------------------------------------------------------------------- /tests/ssh2_sftp_001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ssh2_sftp - SFTP tests 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 0); 27 | } 28 | 29 | echo "**Negotiation\n"; 30 | $mn = ssh2_methods_negotiated($ssh); 31 | var_dump(ssh2t_strset($mn['kex'])); 32 | var_dump(ssh2t_strset($mn['hostkey'])); 33 | foreach(array('client_to_server', 'server_to_client') as $direction) { 34 | $mnd = $mn[$direction]; 35 | var_dump(ssh2t_strset($mnd['crypt'])); 36 | var_dump(ssh2t_strset($mnd['comp'])); 37 | var_dump(ssh2t_strset($mnd['mac'])); 38 | } 39 | --EXPECT-- 40 | **Connect 41 | bool(true) 42 | string(12) "SSH2 Session" 43 | **Fingerprint MD5 44 | bool(true) 45 | int(32) 46 | bool(true) 47 | **Fingerprint SHA1 48 | bool(true) 49 | int(40) 50 | bool(true) 51 | **Negotiation 52 | bool(true) 53 | bool(true) 54 | bool(true) 55 | bool(true) 56 | bool(true) 57 | bool(true) 58 | bool(true) 59 | bool(true) 60 | 61 | -------------------------------------------------------------------------------- /config.w32: -------------------------------------------------------------------------------- 1 | // vim:ft=javascript 2 | 3 | ARG_WITH("ssh2", "SSH2 support", "no"); 4 | 5 | if (PHP_SSH2 != "no") { 6 | var ssh2_all_ok = true; 7 | 8 | if (!CHECK_LIB("libssh2_a.lib;libssh2.lib", "ssh2", PHP_SSH2)){ 9 | WARNING("ssh2 not enabled: libssh2 libraries not found"); 10 | ssh2_all_ok = false; 11 | } 12 | 13 | if (!CHECK_HEADER_ADD_INCLUDE("libssh2.h", "CFLAGS_SSH2", PHP_PHP_BUILD + "\\include\\libssh2")) { 14 | WARNING("ssh2 not enabled: libssh2 headers not found"); 15 | ssh2_all_ok = false; 16 | } 17 | 18 | if (typeof SETUP_OPENSSL === "function") { 19 | var ret = SETUP_OPENSSL("ssh2", PHP_PHP_BUILD + "\\lib"); 20 | if (ret < 1) { 21 | WARNING("ssh2 support can't be enabled, openssl library not found") 22 | ssh2_all_ok = false; 23 | } 24 | } else { 25 | if (!CHECK_LIB("libeay32.lib", "ssh2", PHP_PHP_BUILD + "\\lib")) { 26 | WARNING("ssh2 support can't be enabled, openssl library not found") 27 | ssh2_all_ok = false; 28 | } 29 | } 30 | 31 | if (!CHECK_LIB("zlib_a.lib", "ssh2", PHP_PHP_BUILD + "\\lib")) { 32 | WARNING("ssh2 support can't be enabled, zlib library not found") 33 | ssh2_all_ok = false; 34 | } 35 | 36 | if (ssh2_all_ok){ 37 | EXTENSION("ssh2", "ssh2.c ssh2_fopen_wrappers.c ssh2_sftp.c"); 38 | AC_DEFINE('HAVE_SSH2LIB', 1); 39 | AC_DEFINE('PHP_SSH2_AGENT_AUTH', 1); 40 | AC_DEFINE('PHP_SSH2_SESSION_TIMEOUT', 1); 41 | ADD_EXTENSION_DEP('ssh2', 'zlib') 42 | ADD_EXTENSION_DEP('ssh2', 'openssl') 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/testkey_rsa: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAnDqb5K4hF9I2571UizdrwPzbt4p4z2YsWHICxHLb7m8UYGYr 3 | KjZnbb6SjdyPgb4iA5856HHPhOwVpvmhegb0A2BnKM6ZY4yIYmtu5VwQfsZXSXLh 4 | LzyiNQ1GpsMvGcLxlxmgiwuzAW183aiXJ6CKd3ItSUJVDmSfqwjZDCy28BliOOca 5 | 95DlM0djG9AdxBFbUWS+EvGb8qXWelpjYSNlp7iaekKGxDzLYtNSei5FkA1cpNjI 6 | /hCPPkbhD30YGDGtpRCOfwWFiF+rhwHW4g3MZsEp+CTrtC6kL6HWsF7eXwPIOyXI 7 | TjIbeEatIWjCOVREVs4qvREbuUoaGN1khYJixwIDAQABAoIBAH9Ysux0GBufygua 8 | ZAjbEyWLt9njsNh2U52AycqySZ4Qw45Umcjd8e8eEb47VGsQvg8vwnc8FkkmW/vw 9 | rfSXKF58PXqg9Z2U1zNKmWmyYuvD5zDCyNXFWf9epAGZRpJ8OovNYvUUg0Sdp9PS 10 | 8etmTJhS5KE7xbNmyFQVcT8gT7pj4IEHzEcZZQuO0w8jmKSnn8E4k5leasSR7+U3 11 | GNRASAmuOPVsG4rp4pl4CIvUlpEPK7yuIAWD6Qwhh4nkqiZflcZ+tNDKUhMIRbez 12 | 5URX3dgZnG7fbZZI++LTRM4M09LojbdFOdT95lWJr0Pm+3h51s965F+zwkR2vYMS 13 | 6g4WcUkCgYEAyscCArrg1nocKx5ubFJVyxe/YT328Et6GhKRqTyYDMq8eznFE8of 14 | 01KiVg1+48YMb2Wc94zjsRBgHsjEz/A/f0hRtLBx3LCaYNwVF5o4DI6LqmAnOlnE 15 | an9yYnhh8Ux4nw9PjszjdpGglIJkf0o9nP6biBgq7TAXzLfSIOxSnFsCgYEAxTvs 16 | sE+PiF3mD4U9bWoBqRyFywDMW9mPueX9TltNqpQksaS1FzP08pCi6fTUIrsxQtXE 17 | 78N5uxiRo1OiNsPm8pluPu2Na7yfI1MQJZE8OmEadqdyPWPVYZVkUVIo13iHGovo 18 | 2eMHirb3k9y2y4WJJBPA2hOZ8azF7u1epD70DwUCgYBT4kWDqZkvQG58q+cUKm9R 19 | R66k5IRN3XtSY9IcB3QC/q8/7qMHT7sgqXN8U7LlON2WC5wGkCL08YhB094PAxzR 20 | Y7JhJAjGOcxzOzgCfoqnLyS5w3MW4WGuiLBh6djigDb53dSUh0+lkhKyvMokGiso 21 | 0e63OwAvBJEj3/suIXT/NwKBgGY+uGZ1+BxjQP5OvhdYWuAGDz27fjUnRaDDH1td 22 | bZe+EH+euNn8b0DHxgyuhMI0dEcHdn4swZBCHk2i73age7rL2yD4GNX/BChVyQsQ 23 | jT5eVE+qYkh81oMt5MFbbBy3OKKEYQOC88fvLc0TIBfFc+Cl9NWYT7e/LyS7HNLc 24 | Z7zZAoGBAKhDfPMvGXeTjha/nBArD8rp9W7R7VrZd/VUIBOmnWhiQCRTv1KVQGKh 25 | OGQ1taWdRt98wbnVkMxFHN4oCrnDv7cpXQNvw9m/WGZxXMgWWX0f1cpJSwewiNBt 26 | Bb4jmKhkeAe8LwTPoxNYXxnzzf0UQox2HhcbKNP17Np/Y22NQPEf 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /config.m4: -------------------------------------------------------------------------------- 1 | dnl config.m4 for extension ssh2 2 | 3 | PHP_ARG_WITH(ssh2, for ssh2 support, 4 | [ --with-ssh2=[DIR] Include ssh2 support]) 5 | 6 | if test "$PHP_SSH2" != "no"; then 7 | SEARCH_PATH="/usr/local /usr" 8 | SEARCH_FOR="/include/libssh2.h" 9 | if test -r $PHP_SSH2/$SEARCH_FOR; then # path given as parameter 10 | SSH2_DIR=$PHP_SSH2 11 | else 12 | AC_MSG_CHECKING([for ssh2 files in default path]) 13 | for i in $SEARCH_PATH ; do 14 | if test -r $i/$SEARCH_FOR; then 15 | SSH2_DIR=$i 16 | AC_MSG_RESULT(found in $i) 17 | fi 18 | done 19 | fi 20 | 21 | if test -z "$SSH2_DIR"; then 22 | AC_MSG_RESULT([not found]) 23 | AC_MSG_ERROR([The required libssh2 library was not found. You can obtain that package from http://sourceforge.net/projects/libssh2/]) 24 | fi 25 | 26 | PHP_ADD_INCLUDE($SSH2_DIR/include) 27 | 28 | PHP_CHECK_LIBRARY(ssh2,libssh2_session_hostkey, 29 | [ 30 | PHP_ADD_LIBRARY_WITH_PATH(ssh2, $SSH2_DIR/lib, SSH2_SHARED_LIBADD) 31 | AC_DEFINE(HAVE_SSH2LIB,1,[Have libssh2]) 32 | ],[ 33 | AC_MSG_ERROR([libssh2 version >= 1.2 not found]) 34 | ],[ 35 | -L$SSH2_DIR/lib -lm 36 | ]) 37 | 38 | PHP_CHECK_LIBRARY(ssh2,libssh2_agent_init, 39 | [ 40 | AC_DEFINE(PHP_SSH2_AGENT_AUTH, 1, [Have libssh2 with ssh-agent support]) 41 | ],[ 42 | AC_MSG_WARN([libssh2 <= 1.2.3, ssh-agent subsystem support not enabled]) 43 | ],[ 44 | -L$SSH2_DIR/lib -lm 45 | ]) 46 | 47 | PHP_CHECK_LIBRARY(ssh2,libssh2_session_set_timeout, 48 | [ 49 | AC_DEFINE(PHP_SSH2_SESSION_TIMEOUT, 1, [Have libssh2 with session timeout support]) 50 | ],[ 51 | AC_MSG_WARN([libssh2 < 1.2.9, session timeout support not enabled]) 52 | ],[ 53 | -L$SSH2_DIR/lib -lm 54 | ]) 55 | 56 | PHP_SUBST(SSH2_SHARED_LIBADD) 57 | 58 | PHP_NEW_EXTENSION(ssh2, ssh2.c ssh2_fopen_wrappers.c ssh2_sftp.c, $ext_shared) 59 | fi 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------- 2 | The PHP License, version 3.01 3 | Copyright (c) 1999 - 2012 The PHP Group. All rights reserved. 4 | -------------------------------------------------------------------- 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, is permitted provided that the following conditions 8 | are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | 13 | 2. Redistributions in binary form must reproduce the above copyright 14 | notice, this list of conditions and the following disclaimer in 15 | the documentation and/or other materials provided with the 16 | distribution. 17 | 18 | 3. The name "PHP" must not be used to endorse or promote products 19 | derived from this software without prior written permission. For 20 | written permission, please contact group@php.net. 21 | 22 | 4. Products derived from this software may not be called "PHP", nor 23 | may "PHP" appear in their name, without prior written permission 24 | from group@php.net. You may indicate that your software works in 25 | conjunction with PHP by saying "Foo for PHP" instead of calling 26 | it "PHP Foo" or "phpfoo" 27 | 28 | 5. The PHP Group may publish revised and/or new versions of the 29 | license from time to time. Each version will be given a 30 | distinguishing version number. 31 | Once covered code has been published under a particular version 32 | of the license, you may always continue to use it under the terms 33 | of that version. You may also choose to use such covered code 34 | under the terms of any subsequent version of the license 35 | published by the PHP Group. No one other than the PHP Group has 36 | the right to modify the terms applicable to covered code created 37 | under this License. 38 | 39 | 6. Redistributions of any form whatsoever must retain the following 40 | acknowledgment: 41 | "This product includes PHP software, freely available from 42 | ". 43 | 44 | THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND 45 | ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 46 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 47 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP 48 | DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 49 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 50 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 51 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 52 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 53 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 54 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 55 | OF THE POSSIBILITY OF SUCH DAMAGE. 56 | 57 | -------------------------------------------------------------------- 58 | 59 | This software consists of voluntary contributions made by many 60 | individuals on behalf of the PHP Group. 61 | 62 | The PHP Group can be contacted via Email at group@php.net. 63 | 64 | For more information on the PHP Group and the PHP project, 65 | please see . 66 | 67 | PHP includes the Zend Engine, freely available at 68 | . -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | release: 8 | types: [created] 9 | create: 10 | 11 | jobs: 12 | windows: 13 | runs-on: windows-latest 14 | name: "Windows: Build and test" 15 | defaults: 16 | run: 17 | shell: cmd 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | php: ["8.0", "8.1", "8.2", "8.3", "8.4"] 22 | arch: [x86, x64] 23 | ts: [nts, ts] 24 | experimental: [false] 25 | steps: 26 | - name: Checkout Repository 27 | uses: actions/checkout@v4 28 | - name: Extract Version 29 | shell: powershell 30 | run: | 31 | chcp 65001 32 | $r = Select-String -Path php_ssh2.h -Pattern 'PHP_SSH2_VERSION\s+"(.*)"' 33 | $s = $r.Matches[0].Groups[1] 34 | echo "$s" 35 | $extension_version = 'EXTENSION_VERSION=' + $s 36 | echo $extension_version >> $env:GITHUB_ENV 37 | - name: Setup PHP 38 | id: setup-php 39 | uses: php/setup-php-sdk@v0.10 40 | with: 41 | version: ${{matrix.php}} 42 | arch: ${{matrix.arch}} 43 | ts: ${{matrix.ts}} 44 | deps: "libssh2,openssl,zlib" 45 | - name: Enable Developer Command Prompt 46 | uses: ilammy/msvc-dev-cmd@v1 47 | with: 48 | arch: ${{matrix.arch}} 49 | toolset: ${{steps.setup-php.outputs.toolset}} 50 | - name: Generate Build Files 51 | run: phpize 52 | - name: Configure Build 53 | run: configure --with-ssh2 --with-prefix=${{steps.setup-php.outputs.prefix}} 54 | - name: Build 55 | run: nmake 56 | - name: Define Module Env 57 | shell: powershell 58 | run: | 59 | chcp 65001 60 | 61 | $dir = (Get-Location).Path + '\' 62 | if ('x64' -eq '${{matrix.arch}}') { $dir = $dir + 'x64\' } 63 | $dir = $dir + 'Release' 64 | if ('ts' -eq '${{matrix.ts}}') { $dir = $dir + '_TS' } 65 | 66 | $artifact_name = 'php_ssh2-${{env.EXTENSION_VERSION}}-${{matrix.php}}' 67 | 68 | if ('7.2' -eq '${{matrix.php}}') { $artifact_name = $artifact_name + '-vc15' } 69 | if ('7.3' -eq '${{matrix.php}}') { $artifact_name = $artifact_name + '-vc15' } 70 | if ('7.4' -eq '${{matrix.php}}') { $artifact_name = $artifact_name + '-vc15' } 71 | if ('8.0' -eq '${{matrix.php}}') { $artifact_name = $artifact_name + '-vs16' } 72 | if ('8.1' -eq '${{matrix.php}}') { $artifact_name = $artifact_name + '-vs16' } 73 | if ('8.2' -eq '${{matrix.php}}') { $artifact_name = $artifact_name + '-vs16' } 74 | if ('8.3' -eq '${{matrix.php}}') { $artifact_name = $artifact_name + '-vs16' } 75 | if ('8.4' -eq '${{matrix.php}}') { $artifact_name = $artifact_name + '-vs17' } 76 | 77 | if ('nts' -eq '${{matrix.ts}}') { $artifact_name = $artifact_name + '-nts' } 78 | if ('x64' -eq '${{matrix.arch}}') { $artifact_name = $artifact_name + '-x86_64' } 79 | 80 | $extension_artifact_name = "ARTIFACT_NAME=" + $artifact_name 81 | echo $extension_artifact_name >> $env:GITHUB_ENV 82 | 83 | $from = $dir + '\php_ssh2.dll' 84 | $to = $from + ".zip" 85 | Compress-Archive $from $to 86 | $extension_artifact = "ARTIFACT=" + $from 87 | echo $extension_artifact >> $env:GITHUB_ENV 88 | 89 | - name: Upload artifacts 90 | uses: actions/upload-artifact@v4 91 | with: 92 | name: ${{env.ARTIFACT_NAME}} 93 | path: ${{env.ARTIFACT}} 94 | - name: Publish Binaries to Release 95 | if: ${{ startsWith(github.ref, 'refs/tags') }} 96 | uses: svenstaro/upload-release-action@v2 97 | with: 98 | asset_name: ${{env.ARTIFACT_NAME}}.zip 99 | file: ${{env.ARTIFACT}}.zip 100 | -------------------------------------------------------------------------------- /php_ssh2.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 7 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2016 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.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 | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | #ifndef PHP_SSH2_H 20 | #define PHP_SSH2_H 21 | 22 | #include 23 | #include 24 | #include "ext/standard/url.h" 25 | #include "main/php_network.h" 26 | 27 | #define PHP_SSH2_VERSION "1.4.1" 28 | #define PHP_SSH2_DEFAULT_PORT 22 29 | 30 | /* Exported Constants */ 31 | #define PHP_SSH2_FINGERPRINT_MD5 0x0000 32 | #define PHP_SSH2_FINGERPRINT_SHA1 0x0001 33 | #define PHP_SSH2_FINGERPRINT_HEX 0x0000 34 | #define PHP_SSH2_FINGERPRINT_RAW 0x0002 35 | 36 | #define PHP_SSH2_TERM_UNIT_CHARS 0x0000 37 | #define PHP_SSH2_TERM_UNIT_PIXELS 0x0001 38 | 39 | #define PHP_SSH2_DEFAULT_TERMINAL "vanilla" 40 | #define PHP_SSH2_DEFAULT_TERM_WIDTH 80 41 | #define PHP_SSH2_DEFAULT_TERM_HEIGHT 25 42 | #define PHP_SSH2_DEFAULT_TERM_UNIT PHP_SSH2_TERM_UNIT_CHARS 43 | 44 | #define PHP_SSH2_SESSION_RES_NAME "SSH2 Session" 45 | #define PHP_SSH2_CHANNEL_STREAM_NAME "SSH2 Channel" 46 | #define PHP_SSH2_LISTENER_RES_NAME "SSH2 Listener" 47 | #define PHP_SSH2_SFTP_RES_NAME "SSH2 SFTP" 48 | #define PHP_SSH2_PKEY_SUBSYS_RES_NAME "SSH2 Publickey Subsystem" 49 | 50 | #define PHP_SSH2_SFTP_STREAM_NAME "SSH2 SFTP File" 51 | #define PHP_SSH2_SFTP_DIRSTREAM_NAME "SSH2 SFTP Directory" 52 | #define PHP_SSH2_SFTP_WRAPPER_NAME "SSH2 SFTP" 53 | 54 | #define PHP_SSH2_LISTEN_MAX_QUEUED 16 55 | 56 | #define PHP_SSH2_DEFAULT_POLL_TIMEOUT 30 57 | 58 | extern zend_module_entry ssh2_module_entry; 59 | #define phpext_ssh2_ptr &ssh2_module_entry 60 | 61 | typedef struct _php_ssh2_session_data { 62 | /* Userspace callback functions */ 63 | zval *ignore_cb; 64 | zval *debug_cb; 65 | zval *macerror_cb; 66 | zval *disconnect_cb; 67 | 68 | php_socket_t socket; 69 | } php_ssh2_session_data; 70 | 71 | typedef struct _php_ssh2_sftp_data { 72 | LIBSSH2_SESSION *session; 73 | LIBSSH2_SFTP *sftp; 74 | 75 | zend_resource *session_rsrc; 76 | } php_ssh2_sftp_data; 77 | 78 | typedef struct _php_ssh2_listener_data { 79 | LIBSSH2_SESSION *session; 80 | LIBSSH2_LISTENER *listener; 81 | 82 | zend_resource *session_rsrc; 83 | } php_ssh2_listener_data; 84 | 85 | #include "libssh2_publickey.h" 86 | 87 | typedef struct _php_ssh2_pkey_subsys_data { 88 | LIBSSH2_SESSION *session; 89 | LIBSSH2_PUBLICKEY *pkey; 90 | 91 | zend_resource *session_rsrc; 92 | } php_ssh2_pkey_subsys_data; 93 | 94 | #define SSH2_FETCH_NONAUTHENTICATED_SESSION(session, zsession) \ 95 | if ((session = (LIBSSH2_SESSION *)zend_fetch_resource(Z_RES_P(zsession), PHP_SSH2_SESSION_RES_NAME, le_ssh2_session)) == NULL) { \ 96 | RETURN_FALSE; \ 97 | } \ 98 | if (libssh2_userauth_authenticated(session)) { \ 99 | php_error_docref(NULL, E_WARNING, "Connection already authenticated"); \ 100 | RETURN_FALSE; \ 101 | } 102 | 103 | #define SSH2_FETCH_AUTHENTICATED_SESSION(session, zsession) \ 104 | if ((session = (LIBSSH2_SESSION *)zend_fetch_resource(Z_RES_P(zsession), PHP_SSH2_SESSION_RES_NAME, le_ssh2_session)) == NULL) { \ 105 | RETURN_FALSE; \ 106 | } \ 107 | if (!libssh2_userauth_authenticated(session)) { \ 108 | php_error_docref(NULL, E_WARNING, "Connection not authenticated"); \ 109 | RETURN_FALSE; \ 110 | } 111 | 112 | typedef struct _php_ssh2_channel_data { 113 | LIBSSH2_CHANNEL *channel; 114 | 115 | /* Distinguish which stream we should read/write from/to */ 116 | unsigned int streamid; 117 | char is_blocking; 118 | long timeout; 119 | 120 | /* Resource */ 121 | zend_resource *session_rsrc; 122 | 123 | /* Allow one stream to be closed while the other is kept open */ 124 | unsigned char *refcount; 125 | 126 | } php_ssh2_channel_data; 127 | 128 | /* In ssh2_fopen_wrappers.c */ 129 | PHP_FUNCTION(ssh2_shell); 130 | PHP_FUNCTION(ssh2_exec); 131 | PHP_FUNCTION(ssh2_tunnel); 132 | PHP_FUNCTION(ssh2_scp_recv); 133 | PHP_FUNCTION(ssh2_scp_send); 134 | PHP_FUNCTION(ssh2_fetch_stream); 135 | PHP_FUNCTION(ssh2_send_eof); 136 | PHP_FUNCTION(ssh2_shell_resize); 137 | 138 | /* In ssh2_sftp.c */ 139 | PHP_FUNCTION(ssh2_sftp); 140 | 141 | PHP_FUNCTION(ssh2_sftp_rename); 142 | PHP_FUNCTION(ssh2_sftp_unlink); 143 | PHP_FUNCTION(ssh2_sftp_mkdir); 144 | PHP_FUNCTION(ssh2_sftp_rmdir); 145 | PHP_FUNCTION(ssh2_sftp_chmod); 146 | PHP_FUNCTION(ssh2_sftp_stat); 147 | PHP_FUNCTION(ssh2_sftp_lstat); 148 | PHP_FUNCTION(ssh2_sftp_symlink); 149 | PHP_FUNCTION(ssh2_sftp_readlink); 150 | PHP_FUNCTION(ssh2_sftp_realpath); 151 | 152 | LIBSSH2_SESSION *php_ssh2_session_connect(char *host, int port, zval *methods, zval *callbacks); 153 | void php_ssh2_sftp_dtor(zend_resource *rsrc); 154 | php_url *php_ssh2_fopen_wrapper_parse_path(const char *path, char *type, php_stream_context *context, 155 | LIBSSH2_SESSION **psession, zend_resource **presource, 156 | LIBSSH2_SFTP **psftp, zend_resource **psftp_rsrc); 157 | 158 | extern php_stream_ops php_ssh2_channel_stream_ops; 159 | 160 | extern php_stream_wrapper php_ssh2_stream_wrapper_shell; 161 | extern php_stream_wrapper php_ssh2_stream_wrapper_exec; 162 | extern php_stream_wrapper php_ssh2_stream_wrapper_tunnel; 163 | extern php_stream_wrapper php_ssh2_stream_wrapper_scp; 164 | extern php_stream_wrapper php_ssh2_sftp_wrapper; 165 | 166 | /* Resource list entries */ 167 | extern int le_ssh2_session; 168 | extern int le_ssh2_sftp; 169 | 170 | #if PHP_VERSION_ID < 70300 171 | #define SSH2_URL_STR(a) (a) 172 | #define SSH2_URL_LEN(a) strlen(a) 173 | #else 174 | #define SSH2_URL_STR(a) ZSTR_VAL(a) 175 | #define SSH2_URL_LEN(a) ZSTR_LEN(a) 176 | #endif 177 | 178 | #endif /* PHP_SSH2_H */ 179 | 180 | /* 181 | * Local variables: 182 | * tab-width: 4 183 | * c-basic-offset: 4 184 | * indent-tabs-mode: t 185 | * End: 186 | */ 187 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | ssh2 4 | pecl.php.net 5 | Bindings for the libssh2 library 6 | 7 | Provides bindings to the functions of libssh2 which implements the SSH2 protocol. 8 | libssh2 is available from http://libssh2.org/ 9 | 10 | 11 | Casper Langemeijer 12 | langemeijer 13 | langemeijer@php.net 14 | yes 15 | 16 | 17 | Pierre Joye 18 | pajoye 19 | pierre@php.net 20 | yes 21 | 22 | 23 | Mike Sullivan 24 | mikesul 25 | mikesul@php.net 26 | no 27 | 28 | 29 | Sara Golemon 30 | pollita 31 | pollita@php.net 32 | no 33 | 34 | 2024-02-09 35 | 36 | 1.4.1 37 | 1.0 38 | 39 | 40 | stable 41 | stable 42 | 43 | PHP License 44 | 45 | - End zend_function_entry ssh2_functions list with PHP_FE_END [PR #67] (Gerdriaan Mulder) 46 | - Remove implicit switch-case fallthrough [PR #66] (Gerdriaan Mulder) 47 | - Windows build setup with GitHub Actions [PR #75] (Derick Rethans) 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 7.0.0 83 | 84 | 85 | 1.4.0 86 | 87 | 88 | 89 | ssh2 90 | 91 | 92 | 93 | 94 | 95 | 2023-04-15 96 | 97 | 1.4 98 | 1.0 99 | 100 | 101 | - Add ssh2_shell_resize function [PR #55] (jentian) 102 | - Add ssh2_auth_pubkey to allow public and private keys as strings [PR #56] (Andreas Treichel) 103 | 104 | 105 | 106 | 2021-03-02 107 | 108 | 1.3 109 | 1.0 110 | 111 | 112 | - 1.3.1 release only fixed the PHP >= 8.0 requirement in the package.xml. No code changes. 113 | 114 | Changes in 1.3: 115 | 116 | - Add ssh2_send_eof() [PR #45] (Calvin Buckley) 117 | - PHP stream cast function for SSH channel objects for stream_select() support. [PR #38] (Robert Wolf) 118 | - Fix for PHP 7.4 and 8 [PR #44] (Andy Postnikov and Remi Collet) 119 | - Fixed debug and disconnected callback [PR #37] (Piotr Rogowski) 120 | - Various stability and memory issue fixes [PR #39, #41] (Robert Wolf) 121 | - Segfault fix for bug #79757 [PR #43] (Konrad K) 122 | - Various stability and bug fixes #79631, #63480, #80294, #75523, #80729 [PR #46, #47, #48, #49, #50 and #51] (Christoph M. Becker) 123 | 124 | 125 | 126 | 2019-09-17 127 | 128 | 1.2 129 | 1.0 130 | 131 | 132 | - Fix multiple reflection/documentation disparities [PR #29] (Michael Moravec) 133 | - Fix PHP7 port. Restored commented reference counting. [Dmitry Stogov] 134 | - Updates for php_url structure changes [Daniel Ciochiu] 135 | - Make the PR generic for all PHP7 versions [Jan Ehrhardt] 136 | - Fix compatibility with PHP 7.3 [Jan Ehrhardt] 137 | - Fix config.w32 for compatibility with OpenSSL 1.1.x [Anatol] 138 | - Make static inline for php_ssh2_sftp_attr2ssb [Andy Postnikov] 139 | - Enable 7.1-7.3 on Travis CI [Andy Postnikov] 140 | 141 | 142 | 143 | 2017-06-14 144 | 145 | 1.1 146 | 1.0 147 | 148 | 149 | - Fixed bug #72988 (Libor M.) 150 | - Fixed bug #73198 (Langemeijer) 151 | - Fixed php_url_parse issue (Remi Collet) 152 | - fix Invalid write of size 1 (Remi Collet) 153 | - Fixed bug #73819 (Mitch Hagstrand) 154 | - Fixed bug #73410 (Mitch Hagstrand) 155 | - Travis CI (Mitch Hagstrand) 156 | - Various other fixes on PHP 7 code and code cleanup (Mitch Hagstrand, Libor M., Anatol Belski) 157 | 158 | 159 | 160 | 2016-06-12 161 | 162 | 1.0 163 | 1.0 164 | 165 | 166 | - Release for PHP 7 (Sean DuBois) 167 | - Made win32 builds depend on zlib and openssl extensions (Credomane Evonguard) 168 | - Add blocking call to php_ssh2_direct_tcpip (Credomane Evonguard) 169 | - Added explicit ssh2_disconnect function (Credomane Evonguard) 170 | - Fixed bug #72150 - Fixing segfault when passing env variables (Krakjoe) 171 | 172 | 173 | 174 | 2016-06-12 175 | 176 | 0.13 177 | 0.13 178 | 179 | 180 | - Fixed bug #63660 php_ssh2_fopen_wrapper_parse_path segfaults 181 | - Fixed bug #63855 compilation fails on windows (patch from Erez H) 182 | - Fixed bug #64535 php_ssh2_sftp_dirstream_read segfault on error (Matt Pelmear) 183 | - Add reflection API support (Frédéric Hardy) 184 | - Add exit-status support for ssh2 file wrappers (Vladimir Zidar) 185 | - Fixed bug #58893 Unable to request a channel from remote host (Vladimir Zidar) 186 | - Fix segfault when trying to authenticate in servers that do not support authentication (none) (Felipe Weckx) 187 | 188 | 189 | 190 | 2012-10-15 191 | 192 | 0.12 193 | 0.12 194 | 195 | 196 | - Bumped libssh2 version requirement to 1.2 (aug 2009) 197 | - Added ssh2_auth_agent() - SSH agent authentication support (with libssh >= 1.2.3) 198 | - Added ssh2_sftp_chmod() (fixed bug #59880) 199 | - Added support for stream_set_timeout() to channel streams (fixed bug #56377) (with libssh >= 1.2.9) 200 | - Added keyboard-interactive to ssh2_auth_password() (fixed bugs #61760 and #54916) 201 | - Add license file as requested in bug #59980 202 | - Allow for tilde (~) in public and private keyfile paths 203 | - Be more verbose about any errors within ssh2_scp_send 204 | - Fixed bug #56425 - Problem with ssh2_scp_send 205 | - Fixed bug #59794 - ssh2.sftp protocol wrapper works incorrectly for paths which contain a '#' 206 | - Fixed bug #63192 - Check if connection is authenticated. 207 | - Fixed bug #58576 - Handle error values from reads and writes to a channel. 208 | 209 | 210 | 211 | 2011-09-22 212 | 213 | 0.11.3 214 | 0.11.0 215 | 216 | 217 | - Fixed bug#24349 (ssh2_sftp_mkdir fails with trailing slash) 218 | - Fixed bug#22729 (using ssh2.sftp stream on 64bit vs. 32bit) 219 | - Fixed bug#22671 (trailing spaces trimmed from filenames) 220 | - Fixed bug#17142 (Missing EOF detection in ssh2.sftp:// streams) 221 | - Fixed bug#16944 (Segmentation fault SIGSEGV with protected variable with ssh2) 222 | 223 | 224 | 225 | 2009-11-28 226 | 227 | 0.11.1 228 | 0.11.0 229 | 230 | 231 | beta 232 | beta 233 | 234 | PHP License 235 | 236 | - Fixed the extension version info 237 | . no other changes since 0.11.1 238 | 239 | 240 | 241 | 2009-11-28 242 | 243 | 0.11.1 244 | 0.11.0 245 | 246 | 247 | beta 248 | beta 249 | 250 | PHP License 251 | 252 | - Fixed bug #9500, SSH2 sockets not being closed properly because of close vs closesocket difference 253 | - Fixed Windows support 254 | - Support for latest libssh2 release 255 | - Fix build with PHP 5.3 256 | - Fixed incorrect return values for rename/unlink/mkdir/rmdir with sftp functions/streams 257 | - Fixed various memory leaks and segfaults 258 | 259 | 260 | 261 | 2009-11-28 262 | 263 | 0.11.1-dev 264 | 0.11.0 265 | 266 | 267 | beta 268 | beta 269 | 270 | PHP License 271 | 272 | - Support for latest libssh2 release 273 | - Fix build with PHP 5.3 274 | - Fixed incorrect return values for rename/unlink/mkdir/rmdir with sftp functions/streams 275 | - Fixed various memory leaks and segfaults 276 | 277 | 278 | 279 | 2008-12-08 280 | 281 | 0.11.0 282 | 0.11.0 283 | 284 | 285 | beta 286 | beta 287 | 288 | PHP License 289 | 290 | - Support for latest libssh2 release 291 | - Fix build with PHP 5.3 292 | - Fixed incorrect return values for rename/unlink/mkdir/rmdir with sftp functions/streams 293 | - Fixed various memory leaks and segfaults 294 | 295 | 296 | 297 | 298 | -------------------------------------------------------------------------------- /ssh2_sftp.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 7 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2016 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.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 | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | #ifdef HAVE_CONFIG_H 20 | #include "config.h" 21 | #endif 22 | 23 | #include "php.h" 24 | #include "php_ssh2.h" 25 | #include "ext/standard/php_string.h" 26 | 27 | /* ************************* 28 | * Resource Housekeeping * 29 | ************************* */ 30 | 31 | void php_ssh2_sftp_dtor(zend_resource *rsrc) 32 | { 33 | php_ssh2_sftp_data *data = (php_ssh2_sftp_data*)rsrc->ptr; 34 | 35 | if (!data) { 36 | return; 37 | } 38 | 39 | if (data->session_rsrc->ptr != NULL) { 40 | libssh2_sftp_shutdown(data->sftp); 41 | } 42 | 43 | zend_list_delete(data->session_rsrc); 44 | 45 | efree(data); 46 | } 47 | 48 | /* ***************** 49 | * SFTP File Ops * 50 | ***************** */ 51 | 52 | unsigned long php_ssh2_parse_fopen_modes(char *openmode) { 53 | unsigned long flags = 0; 54 | 55 | if (strchr(openmode, 'a')) { 56 | flags |= LIBSSH2_FXF_APPEND; 57 | } 58 | 59 | if (strchr(openmode, 'w')) { 60 | flags |= LIBSSH2_FXF_WRITE | LIBSSH2_FXF_TRUNC | LIBSSH2_FXF_CREAT; 61 | } 62 | 63 | if (strchr(openmode, 'r')) { 64 | flags |= LIBSSH2_FXF_READ; 65 | } 66 | 67 | if (strchr(openmode, '+')) { 68 | flags |= LIBSSH2_FXF_READ | LIBSSH2_FXF_WRITE; 69 | } 70 | 71 | if (strchr(openmode, 'x')) { 72 | flags |= LIBSSH2_FXF_WRITE | LIBSSH2_FXF_TRUNC | LIBSSH2_FXF_EXCL | LIBSSH2_FXF_CREAT; 73 | } 74 | 75 | return flags; 76 | } 77 | 78 | static inline int php_ssh2_sftp_attr2ssb(php_stream_statbuf *ssb, LIBSSH2_SFTP_ATTRIBUTES *attrs) 79 | { 80 | memset(ssb, 0, sizeof(php_stream_statbuf)); 81 | if (attrs->flags & LIBSSH2_SFTP_ATTR_SIZE) { 82 | ssb->sb.st_size = attrs->filesize; 83 | } 84 | 85 | if (attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) { 86 | ssb->sb.st_uid = attrs->uid; 87 | ssb->sb.st_gid = attrs->gid; 88 | } 89 | if (attrs->flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) { 90 | ssb->sb.st_mode = attrs->permissions; 91 | } 92 | if (attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) { 93 | ssb->sb.st_atime = attrs->atime; 94 | ssb->sb.st_mtime = attrs->mtime; 95 | } 96 | 97 | return 0; 98 | } 99 | 100 | typedef struct _php_ssh2_sftp_handle_data { 101 | LIBSSH2_SFTP_HANDLE *handle; 102 | 103 | zend_resource *sftp_rsrc; 104 | } php_ssh2_sftp_handle_data; 105 | 106 | /* {{{ php_ssh2_sftp_stream_write 107 | */ 108 | #if PHP_VERSION_ID < 70400 109 | static size_t php_ssh2_sftp_stream_write(php_stream *stream, const char *buf, size_t count) 110 | #else 111 | static ssize_t php_ssh2_sftp_stream_write(php_stream *stream, const char *buf, size_t count) 112 | #endif 113 | { 114 | php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data*)stream->abstract; 115 | ssize_t bytes_written; 116 | 117 | bytes_written = libssh2_sftp_write(data->handle, buf, count); 118 | 119 | #if PHP_VERSION_ID < 70400 120 | return (size_t)(bytes_written<0 ? 0 : bytes_written); 121 | #else 122 | return bytes_written; 123 | #endif 124 | } 125 | /* }}} */ 126 | 127 | /* {{{ php_ssh2_sftp_stream_read 128 | */ 129 | #if PHP_VERSION_ID < 70400 130 | static size_t php_ssh2_sftp_stream_read(php_stream *stream, char *buf, size_t count) 131 | #else 132 | static ssize_t php_ssh2_sftp_stream_read(php_stream *stream, char *buf, size_t count) 133 | #endif 134 | { 135 | php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data*)stream->abstract; 136 | ssize_t bytes_read; 137 | 138 | bytes_read = libssh2_sftp_read(data->handle, buf, count); 139 | 140 | stream->eof = (bytes_read <= 0 && bytes_read != LIBSSH2_ERROR_EAGAIN); 141 | 142 | #if PHP_VERSION_ID < 70400 143 | return (size_t)(bytes_read<0 ? 0 : bytes_read); 144 | #else 145 | return bytes_read; 146 | #endif 147 | } 148 | /* }}} */ 149 | 150 | /* {{{ php_ssh2_sftp_stream_close 151 | */ 152 | static int php_ssh2_sftp_stream_close(php_stream *stream, int close_handle) 153 | { 154 | php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data*)stream->abstract; 155 | 156 | libssh2_sftp_close(data->handle); 157 | zend_list_delete(data->sftp_rsrc); 158 | efree(data); 159 | 160 | return 0; 161 | } 162 | /* }}} */ 163 | 164 | /* {{{ php_ssh2_sftp_stream_seek 165 | */ 166 | static int php_ssh2_sftp_stream_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset) 167 | { 168 | php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data*)stream->abstract; 169 | 170 | switch (whence) { 171 | case SEEK_END: 172 | { 173 | LIBSSH2_SFTP_ATTRIBUTES attrs; 174 | 175 | if (libssh2_sftp_fstat(data->handle, &attrs)) { 176 | return -1; 177 | } 178 | if ((attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) == 0) { 179 | return -1; 180 | } 181 | offset += attrs.filesize; 182 | break; 183 | } 184 | case SEEK_CUR: 185 | { 186 | zend_off_t current_offset = libssh2_sftp_tell(data->handle); 187 | 188 | if (current_offset < 0) { 189 | return -1; 190 | } 191 | 192 | offset += current_offset; 193 | break; 194 | } 195 | } 196 | 197 | libssh2_sftp_seek(data->handle, offset); 198 | 199 | if (newoffset) { 200 | *newoffset = offset; 201 | } 202 | 203 | return 0; 204 | } 205 | /* }}} */ 206 | 207 | /* {{{ php_ssh2_sftp_stream_fstat 208 | */ 209 | static int php_ssh2_sftp_stream_fstat(php_stream *stream, php_stream_statbuf *ssb) 210 | { 211 | php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data*)stream->abstract; 212 | LIBSSH2_SFTP_ATTRIBUTES attrs; 213 | 214 | if (libssh2_sftp_fstat(data->handle, &attrs)) { 215 | return -1; 216 | } 217 | 218 | return php_ssh2_sftp_attr2ssb(ssb, &attrs); 219 | } 220 | /* }}} */ 221 | 222 | static php_stream_ops php_ssh2_sftp_stream_ops = { 223 | php_ssh2_sftp_stream_write, 224 | php_ssh2_sftp_stream_read, 225 | php_ssh2_sftp_stream_close, 226 | NULL, /* flush */ 227 | PHP_SSH2_SFTP_STREAM_NAME, 228 | php_ssh2_sftp_stream_seek, 229 | NULL, /* cast */ 230 | php_ssh2_sftp_stream_fstat, 231 | NULL, /* set_option */ 232 | }; 233 | 234 | /* {{{ php_ssh2_sftp_stream_opener 235 | */ 236 | 237 | static php_stream *php_ssh2_sftp_stream_opener(php_stream_wrapper *wrapper, const char *filename, const char *mode, 238 | int options, zend_string **opened_path, php_stream_context *context STREAMS_DC) 239 | { 240 | php_ssh2_sftp_handle_data *data; 241 | LIBSSH2_SESSION *session = NULL; 242 | LIBSSH2_SFTP *sftp = NULL; 243 | LIBSSH2_SFTP_HANDLE *handle; 244 | php_stream *stream; 245 | zend_resource *rsrc = NULL, *sftp_rsrc = NULL; 246 | php_url *resource; 247 | unsigned long flags; 248 | long perms = 0644; 249 | 250 | resource = php_ssh2_fopen_wrapper_parse_path(filename, "sftp", context, &session, &rsrc, &sftp, &sftp_rsrc); 251 | if (!resource || !session || !sftp || !sftp_rsrc) { 252 | return NULL; 253 | } 254 | 255 | flags = php_ssh2_parse_fopen_modes((char *)mode); 256 | 257 | handle = libssh2_sftp_open(sftp, SSH2_URL_STR(resource->path), flags, perms); 258 | if (!handle) { 259 | php_error_docref(NULL, E_WARNING, "Unable to open %s on remote host", filename); 260 | php_url_free(resource); 261 | zend_list_delete(sftp_rsrc); 262 | return NULL; 263 | } 264 | 265 | data = emalloc(sizeof(php_ssh2_sftp_handle_data)); 266 | data->handle = handle; 267 | data->sftp_rsrc = sftp_rsrc; 268 | 269 | stream = php_stream_alloc(&php_ssh2_sftp_stream_ops, data, 0, mode); 270 | if (!stream) { 271 | libssh2_sftp_close(handle); 272 | zend_list_delete(sftp_rsrc); 273 | efree(data); 274 | } 275 | php_url_free(resource); 276 | 277 | return stream; 278 | } 279 | /* }}} */ 280 | 281 | /* ********************** 282 | * SFTP Directory Ops * 283 | ********************** */ 284 | 285 | /* {{{ php_ssh2_sftp_dirstream_read 286 | */ 287 | #if PHP_VERSION_ID < 70400 288 | static size_t php_ssh2_sftp_dirstream_read(php_stream *stream, char *buf, size_t count) 289 | #else 290 | static ssize_t php_ssh2_sftp_dirstream_read(php_stream *stream, char *buf, size_t count) 291 | #endif 292 | { 293 | php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data*)stream->abstract; 294 | php_stream_dirent *ent = (php_stream_dirent*)buf; 295 | int bytesread = libssh2_sftp_readdir(data->handle, ent->d_name, sizeof(ent->d_name) - 1, NULL); 296 | zend_string *basename; 297 | 298 | if (bytesread <= 0) { 299 | return 0; 300 | } 301 | ent->d_name[bytesread] = 0; 302 | 303 | basename = php_basename(ent->d_name, bytesread, NULL, 0); 304 | if (!basename) { 305 | return 0; 306 | } 307 | 308 | bytesread = MIN(sizeof(ent->d_name) - 1, basename->len); 309 | memcpy(ent->d_name, basename->val, bytesread); 310 | ent->d_name[bytesread] = 0; 311 | zend_string_release(basename); 312 | 313 | return sizeof(php_stream_dirent); 314 | } 315 | /* }}} */ 316 | 317 | /* {{{ php_ssh2_sftp_dirstream_close 318 | */ 319 | static int php_ssh2_sftp_dirstream_close(php_stream *stream, int close_handle) 320 | { 321 | php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data*)stream->abstract; 322 | 323 | libssh2_sftp_close(data->handle); 324 | zend_list_delete(data->sftp_rsrc); 325 | efree(data); 326 | 327 | return 0; 328 | } 329 | /* }}} */ 330 | 331 | static php_stream_ops php_ssh2_sftp_dirstream_ops = { 332 | NULL, /* write */ 333 | php_ssh2_sftp_dirstream_read, 334 | php_ssh2_sftp_dirstream_close, 335 | NULL, /* flush */ 336 | PHP_SSH2_SFTP_DIRSTREAM_NAME, 337 | NULL, /* seek */ 338 | NULL, /* cast */ 339 | NULL, /* fstat */ 340 | NULL, /* set_option */ 341 | }; 342 | 343 | /* {{{ php_ssh2_sftp_dirstream_opener 344 | */ 345 | static php_stream *php_ssh2_sftp_dirstream_opener(php_stream_wrapper *wrapper, const char *filename, const char *mode, 346 | int options, zend_string **opened_path, php_stream_context *context STREAMS_DC) 347 | { 348 | php_ssh2_sftp_handle_data *data; 349 | LIBSSH2_SESSION *session = NULL; 350 | LIBSSH2_SFTP *sftp = NULL; 351 | LIBSSH2_SFTP_HANDLE *handle; 352 | php_stream *stream; 353 | zend_resource *rsrc = NULL, *sftp_rsrc = NULL; 354 | php_url *resource; 355 | 356 | resource = php_ssh2_fopen_wrapper_parse_path(filename, "sftp", context, &session, &rsrc, &sftp, &sftp_rsrc); 357 | if (!resource || !session || !sftp) { 358 | return NULL; 359 | } 360 | 361 | handle = libssh2_sftp_opendir(sftp, SSH2_URL_STR(resource->path)); 362 | if (!handle) { 363 | php_error_docref(NULL, E_WARNING, "Unable to open %s on remote host", filename); 364 | php_url_free(resource); 365 | zend_list_delete(sftp_rsrc); 366 | return NULL; 367 | } 368 | 369 | data = emalloc(sizeof(php_ssh2_sftp_handle_data)); 370 | data->handle = handle; 371 | data->sftp_rsrc = sftp_rsrc; 372 | 373 | stream = php_stream_alloc(&php_ssh2_sftp_dirstream_ops, data, 0, mode); 374 | if (!stream) { 375 | libssh2_sftp_close(handle); 376 | zend_list_delete(sftp_rsrc); 377 | efree(data); 378 | } 379 | php_url_free(resource); 380 | 381 | return stream; 382 | } 383 | /* }}} */ 384 | 385 | /* **************** 386 | * SFTP Wrapper * 387 | **************** */ 388 | 389 | /* {{{ php_ssh2_sftp_urlstat 390 | */ 391 | static int php_ssh2_sftp_urlstat(php_stream_wrapper *wrapper, const char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context) 392 | { 393 | LIBSSH2_SFTP_ATTRIBUTES attrs; 394 | LIBSSH2_SESSION *session = NULL; 395 | LIBSSH2_SFTP *sftp = NULL; 396 | zend_resource *rsrc = NULL, *sftp_rsrc = NULL; 397 | php_url *resource; 398 | 399 | resource = php_ssh2_fopen_wrapper_parse_path(url, "sftp", context, &session, &rsrc, &sftp, &sftp_rsrc); 400 | if (!resource || !session || !sftp || !resource->path) { 401 | return -1; 402 | } 403 | 404 | if (libssh2_sftp_stat_ex(sftp, SSH2_URL_STR(resource->path), SSH2_URL_LEN(resource->path), 405 | (flags & PHP_STREAM_URL_STAT_LINK) ? LIBSSH2_SFTP_LSTAT : LIBSSH2_SFTP_STAT, &attrs)) { 406 | php_url_free(resource); 407 | //zend_list_delete(sftp_rsrcid); 408 | return -1; 409 | } 410 | 411 | php_url_free(resource); 412 | 413 | /* parse_path addrefs the resource, but we're not holding on to it so we have to delref it before we leave */ 414 | //zend_list_delete(sftp_rsrcid); 415 | 416 | return php_ssh2_sftp_attr2ssb(ssb, &attrs); 417 | } 418 | /* }}} */ 419 | 420 | /* {{{ php_ssh2_sftp_unlink 421 | */ 422 | static int php_ssh2_sftp_unlink(php_stream_wrapper *wrapper, const char *url, int options, php_stream_context *context) 423 | { 424 | LIBSSH2_SESSION *session = NULL; 425 | LIBSSH2_SFTP *sftp = NULL; 426 | zend_resource *rsrc = NULL, *sftp_rsrc = NULL; 427 | php_url *resource; 428 | int result; 429 | 430 | resource = php_ssh2_fopen_wrapper_parse_path(url, "sftp", context, &session, &rsrc, &sftp, &sftp_rsrc); 431 | if (!resource || !session || !sftp || !resource->path) { 432 | if (resource) { 433 | php_url_free(resource); 434 | } 435 | return 0; 436 | } 437 | 438 | result = libssh2_sftp_unlink(sftp, SSH2_URL_STR(resource->path)); 439 | php_url_free(resource); 440 | 441 | //zend_list_delete(sftp_rsrcid); 442 | 443 | /* libssh2 uses 0 for success and the streams API uses 0 for failure, so invert */ 444 | return (result == 0) ? -1 : 0; 445 | } 446 | /* }}} */ 447 | 448 | /* {{{ php_ssh2_sftp_rename 449 | */ 450 | static int php_ssh2_sftp_rename(php_stream_wrapper *wrapper, const char *url_from, const char *url_to, int options, php_stream_context *context) 451 | { 452 | LIBSSH2_SESSION *session = NULL; 453 | LIBSSH2_SFTP *sftp = NULL; 454 | zend_resource *rsrc = NULL, *sftp_rsrc = NULL; 455 | php_url *resource, *resource_to; 456 | int result; 457 | 458 | if (strncmp(url_from, "ssh2.sftp://", sizeof("ssh2.sftp://") - 1) || 459 | strncmp(url_to, "ssh2.sftp://", sizeof("ssh2.sftp://") - 1)) { 460 | return 0; 461 | } 462 | 463 | resource_to = php_url_parse(url_to); 464 | if (!resource_to || !resource_to->path) { 465 | if (resource_to) { 466 | php_url_free(resource_to); 467 | } 468 | return 0; 469 | } 470 | 471 | resource = php_ssh2_fopen_wrapper_parse_path(url_from, "sftp", context, &session, &rsrc, &sftp, &sftp_rsrc); 472 | if (!resource || !session || !sftp || !resource->path) { 473 | if (resource) { 474 | php_url_free(resource); 475 | } 476 | php_url_free(resource_to); 477 | return 0; 478 | } 479 | 480 | result = libssh2_sftp_rename(sftp, SSH2_URL_STR(resource->path), SSH2_URL_STR(resource_to->path)); 481 | php_url_free(resource); 482 | php_url_free(resource_to); 483 | 484 | //zend_list_delete(sftp_rsrcid); 485 | 486 | /* libssh2 uses 0 for success and the streams API uses 0 for failure, so invert */ 487 | return (result == 0) ? -1 : 0; 488 | } 489 | /* }}} */ 490 | 491 | /* {{{ php_ssh2_sftp_mkdir 492 | */ 493 | static int php_ssh2_sftp_mkdir(php_stream_wrapper *wrapper, const char *url, int mode, int options, php_stream_context *context) 494 | { 495 | LIBSSH2_SESSION *session = NULL; 496 | LIBSSH2_SFTP *sftp = NULL; 497 | zend_resource *rsrc = NULL, *sftp_rsrc = NULL; 498 | php_url *resource; 499 | int result; 500 | 501 | resource = php_ssh2_fopen_wrapper_parse_path(url, "sftp", context, &session, &rsrc, &sftp, &sftp_rsrc); 502 | if (!resource || !session || !sftp || !resource->path) { 503 | if (resource) { 504 | php_url_free(resource); 505 | } 506 | return 0; 507 | } 508 | 509 | if (options & PHP_STREAM_MKDIR_RECURSIVE) { 510 | /* Just attempt to make every directory, some will fail, but we only care about the last success/failure */ 511 | char *p = SSH2_URL_STR(resource->path); 512 | while ((p = strchr(p + 1, '/'))) { 513 | libssh2_sftp_mkdir_ex(sftp, SSH2_URL_STR(resource->path), p - SSH2_URL_STR(resource->path), mode); 514 | } 515 | } 516 | 517 | result = libssh2_sftp_mkdir(sftp, SSH2_URL_STR(resource->path), mode); 518 | php_url_free(resource); 519 | 520 | //zend_list_delete(sftp_rsrcid); 521 | 522 | /* libssh2 uses 0 for success and the streams API uses 0 for failure, so invert */ 523 | return (result == 0) ? -1 : 0; 524 | } 525 | /* }}} */ 526 | 527 | /* {{{ php_ssh2_sftp_rmdir 528 | */ 529 | static int php_ssh2_sftp_rmdir(php_stream_wrapper *wrapper, const char *url, int options, php_stream_context *context) 530 | { 531 | LIBSSH2_SESSION *session = NULL; 532 | LIBSSH2_SFTP *sftp = NULL; 533 | zend_resource *rsrc = NULL, *sftp_rsrc = NULL; 534 | php_url *resource; 535 | int result; 536 | 537 | resource = php_ssh2_fopen_wrapper_parse_path(url, "sftp", context, &session, &rsrc, &sftp, &sftp_rsrc); 538 | if (!resource || !session || !sftp || !resource->path) { 539 | if (resource) { 540 | php_url_free(resource); 541 | } 542 | return 0; 543 | } 544 | 545 | result = libssh2_sftp_rmdir(sftp, SSH2_URL_STR(resource->path)); 546 | php_url_free(resource); 547 | 548 | //zend_list_delete(sftp_rsrcid); 549 | 550 | /* libssh2 uses 0 for success and the streams API uses 0 for failure, so invert */ 551 | return (result == 0) ? -1 : 0; 552 | } 553 | /* }}} */ 554 | 555 | static php_stream_wrapper_ops php_ssh2_sftp_wrapper_ops = { 556 | php_ssh2_sftp_stream_opener, 557 | NULL, /* close */ 558 | NULL, /* stat */ 559 | php_ssh2_sftp_urlstat, 560 | php_ssh2_sftp_dirstream_opener, 561 | PHP_SSH2_SFTP_WRAPPER_NAME, 562 | php_ssh2_sftp_unlink, 563 | php_ssh2_sftp_rename, 564 | php_ssh2_sftp_mkdir, 565 | php_ssh2_sftp_rmdir, 566 | }; 567 | 568 | php_stream_wrapper php_ssh2_sftp_wrapper = { 569 | &php_ssh2_sftp_wrapper_ops, 570 | NULL, 571 | 1, 572 | }; 573 | 574 | /* ***************** 575 | * Userspace API * 576 | ***************** */ 577 | 578 | 579 | /* {{{ proto resource ssh2_sftp(resource session) 580 | * Request the SFTP subsystem from an already connected SSH2 server 581 | */ 582 | PHP_FUNCTION(ssh2_sftp) 583 | { 584 | LIBSSH2_SESSION *session; 585 | LIBSSH2_SFTP *sftp; 586 | php_ssh2_sftp_data *data; 587 | zval *zsession; 588 | 589 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zsession) == FAILURE) { 590 | return; 591 | } 592 | 593 | if ((session = (LIBSSH2_SESSION *)zend_fetch_resource(Z_RES_P(zsession), PHP_SSH2_SESSION_RES_NAME, le_ssh2_session)) == NULL) { 594 | RETURN_FALSE; 595 | } 596 | 597 | sftp = libssh2_sftp_init(session); 598 | if (!sftp) { 599 | char *sess_err = "Unknown"; 600 | 601 | libssh2_session_last_error(session, &sess_err, NULL, 0); 602 | php_error_docref(NULL, E_WARNING, "Unable to startup SFTP subsystem: %s", sess_err); 603 | RETURN_FALSE; 604 | } 605 | 606 | data = emalloc(sizeof(php_ssh2_sftp_data)); 607 | data->session = session; 608 | data->sftp = sftp; 609 | data->session_rsrc = Z_RES_P(zsession); 610 | Z_ADDREF_P(zsession); 611 | 612 | RETURN_RES(zend_register_resource(data, le_ssh2_sftp)); 613 | } 614 | /* }}} */ 615 | 616 | /* Much of the stuff below can be done via wrapper ops as of PHP5, but is included here for PHP 4.3 users */ 617 | 618 | /* {{{ proto bool ssh2_sftp_rename(resource sftp, string from, string to) 619 | */ 620 | PHP_FUNCTION(ssh2_sftp_rename) 621 | { 622 | php_ssh2_sftp_data *data; 623 | zval *zsftp; 624 | zend_string *src, *dst; 625 | 626 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "rSS", &zsftp, &src, &dst) == FAILURE) { 627 | return; 628 | } 629 | 630 | if ((data = (php_ssh2_sftp_data *)zend_fetch_resource(Z_RES_P(zsftp), PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp)) == NULL) { 631 | RETURN_FALSE; 632 | } 633 | 634 | RETURN_BOOL(!libssh2_sftp_rename_ex(data->sftp, src->val, src->len, dst->val, dst->len, 635 | LIBSSH2_SFTP_RENAME_OVERWRITE | LIBSSH2_SFTP_RENAME_ATOMIC | LIBSSH2_SFTP_RENAME_NATIVE)); 636 | } 637 | /* }}} */ 638 | 639 | /* {{{ proto bool ssh2_sftp_unlink(resource sftp, string filename) 640 | */ 641 | PHP_FUNCTION(ssh2_sftp_unlink) 642 | { 643 | php_ssh2_sftp_data *data; 644 | zval *zsftp; 645 | zend_string *filename; 646 | 647 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "rS", &zsftp, &filename) == FAILURE) { 648 | return; 649 | } 650 | 651 | if ((data = (php_ssh2_sftp_data *)zend_fetch_resource(Z_RES_P(zsftp), PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp)) == NULL) { 652 | RETURN_FALSE; 653 | } 654 | 655 | RETURN_BOOL(!libssh2_sftp_unlink_ex(data->sftp, filename->val, filename->len)); 656 | } 657 | /* }}} */ 658 | 659 | /* {{{ proto bool ssh2_sftp_mkdir(resource sftp, string dirname[, int mode[, int recursive]]) 660 | */ 661 | PHP_FUNCTION(ssh2_sftp_mkdir) 662 | { 663 | php_ssh2_sftp_data *data; 664 | zval *zsftp; 665 | zend_string *dirname; 666 | zend_long mode = 0777; 667 | zend_bool recursive = 0; 668 | char *p; 669 | 670 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "rS|lb", &zsftp, &dirname, &mode, &recursive) == FAILURE) { 671 | return; 672 | } 673 | 674 | if (!dirname) { 675 | RETURN_FALSE; 676 | } 677 | 678 | if ((data = (php_ssh2_sftp_data *)zend_fetch_resource(Z_RES_P(zsftp), PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp)) == NULL) { 679 | RETURN_FALSE; 680 | } 681 | 682 | if (recursive) { 683 | /* Just attempt to make every directory, some will fail, but we only care about the last success/failure */ 684 | p = dirname->val; 685 | while ((p = strchr(p + 1, '/'))) { 686 | if ((p - dirname->val) + 1 == dirname->len) { 687 | break; 688 | } 689 | libssh2_sftp_mkdir_ex(data->sftp, dirname->val, p - dirname->val, mode); 690 | } 691 | } 692 | 693 | 694 | RETURN_BOOL(!libssh2_sftp_mkdir_ex(data->sftp, dirname->val, dirname->len, mode)); 695 | } 696 | /* }}} */ 697 | 698 | /* {{{ proto bool ssh2_sftp_rmdir(resource sftp, string dirname) 699 | */ 700 | PHP_FUNCTION(ssh2_sftp_rmdir) 701 | { 702 | php_ssh2_sftp_data *data; 703 | zval *zsftp; 704 | zend_string *dirname; 705 | 706 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "rS", &zsftp, &dirname) == FAILURE) { 707 | return; 708 | } 709 | 710 | if ((data = (php_ssh2_sftp_data *)zend_fetch_resource(Z_RES_P(zsftp), PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp)) == NULL) { 711 | RETURN_FALSE; 712 | } 713 | 714 | RETURN_BOOL(!libssh2_sftp_rmdir_ex(data->sftp, dirname->val, dirname->len)); 715 | } 716 | /* }}} */ 717 | 718 | /* {{{ proto bool ssh2_sftp_chmod(resource sftp, string filename, int mode) 719 | */ 720 | PHP_FUNCTION(ssh2_sftp_chmod) 721 | { 722 | php_ssh2_sftp_data *data; 723 | zval *zsftp; 724 | zend_string *filename; 725 | zend_long mode; 726 | LIBSSH2_SFTP_ATTRIBUTES attrs; 727 | 728 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "rSl", &zsftp, &filename, &mode) == FAILURE) { 729 | return; 730 | } 731 | 732 | if (ZSTR_LEN(filename) < 1) { 733 | RETURN_FALSE; 734 | } 735 | 736 | if ((data = (php_ssh2_sftp_data *)zend_fetch_resource(Z_RES_P(zsftp), PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp)) == NULL) { 737 | RETURN_FALSE; 738 | } 739 | 740 | attrs.permissions = mode; 741 | attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS; 742 | 743 | RETURN_BOOL(!libssh2_sftp_stat_ex(data->sftp, filename->val, filename->len, LIBSSH2_SFTP_SETSTAT, &attrs)); 744 | } 745 | /* }}} */ 746 | 747 | /* {{{ php_ssh2_sftp_stat_func 748 | * In PHP4.3 this is the only way to request stat into, in PHP >= 5 you can use the fopen wrapper approach 749 | * Both methods will return identical structures 750 | * (well, the other one will include other values set to 0 but they don't count) 751 | */ 752 | static void php_ssh2_sftp_stat_func(INTERNAL_FUNCTION_PARAMETERS, int stat_type) 753 | { 754 | php_ssh2_sftp_data *data; 755 | LIBSSH2_SFTP_ATTRIBUTES attrs; 756 | zval *zsftp; 757 | zend_string *path; 758 | 759 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "rS", &zsftp, &path) == FAILURE) { 760 | return; 761 | } 762 | 763 | if ((data = (php_ssh2_sftp_data *)zend_fetch_resource(Z_RES_P(zsftp), PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp)) == NULL) { 764 | RETURN_FALSE; 765 | } 766 | 767 | if (libssh2_sftp_stat_ex(data->sftp, path->val, path->len, stat_type, &attrs)) { 768 | php_error_docref(NULL, E_WARNING, "Failed to stat remote file"); 769 | RETURN_FALSE; 770 | } 771 | 772 | array_init(return_value); 773 | 774 | if (attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) { 775 | add_index_long(return_value, 7, attrs.filesize); 776 | add_assoc_long(return_value, "size", attrs.filesize); 777 | } 778 | if (attrs.flags & LIBSSH2_SFTP_ATTR_UIDGID) { 779 | add_index_long(return_value, 4, attrs.uid); 780 | add_assoc_long(return_value, "uid", attrs.uid); 781 | 782 | add_index_long(return_value, 5, attrs.gid); 783 | add_assoc_long(return_value, "gid", attrs.gid); 784 | } 785 | if (attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) { 786 | add_index_long(return_value, 2, attrs.permissions); 787 | add_assoc_long(return_value, "mode", attrs.permissions); 788 | } 789 | if (attrs.flags & LIBSSH2_SFTP_ATTR_ACMODTIME) { 790 | add_index_long(return_value, 8, attrs.atime); 791 | add_assoc_long(return_value, "atime", attrs.atime); 792 | 793 | add_index_long(return_value, 9, attrs.mtime); 794 | add_assoc_long(return_value, "mtime", attrs.mtime); 795 | } 796 | } 797 | /* }}} */ 798 | 799 | /* {{{ proto array ssh2_sftp_stat(resource sftp, string path) 800 | */ 801 | PHP_FUNCTION(ssh2_sftp_stat) 802 | { 803 | php_ssh2_sftp_stat_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, LIBSSH2_SFTP_STAT); 804 | } 805 | /* }}} */ 806 | 807 | /* {{{ proto array ssh2_sftp_lstat(resource sftp, string path) 808 | */ 809 | PHP_FUNCTION(ssh2_sftp_lstat) 810 | { 811 | php_ssh2_sftp_stat_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, LIBSSH2_SFTP_LSTAT); 812 | } 813 | /* }}} */ 814 | 815 | /* {{{ proto bool ssh2_sftp_symlink(resource sftp, string target, string link) 816 | */ 817 | PHP_FUNCTION(ssh2_sftp_symlink) 818 | { 819 | php_ssh2_sftp_data *data; 820 | zval *zsftp; 821 | zend_string *targ, *link; 822 | 823 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "rSS", &zsftp, &targ, &link) == FAILURE) { 824 | return; 825 | } 826 | 827 | if ((data = (php_ssh2_sftp_data *)zend_fetch_resource(Z_RES_P(zsftp), PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp)) == NULL) { 828 | RETURN_FALSE; 829 | } 830 | 831 | RETURN_BOOL(!libssh2_sftp_symlink_ex(data->sftp, targ->val, targ->len, link->val, link->len, LIBSSH2_SFTP_SYMLINK)); 832 | } 833 | /* }}} */ 834 | 835 | /* {{{ proto string ssh2_sftp_readlink(resource sftp, string link) 836 | */ 837 | PHP_FUNCTION(ssh2_sftp_readlink) 838 | { 839 | php_ssh2_sftp_data *data; 840 | zval *zsftp; 841 | zend_string *link; 842 | int targ_len = 0; 843 | char targ[8192]; 844 | 845 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "rS", &zsftp, &link) == FAILURE) { 846 | return; 847 | } 848 | 849 | if ((data = (php_ssh2_sftp_data *)zend_fetch_resource(Z_RES_P(zsftp), PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp)) == NULL) { 850 | RETURN_FALSE; 851 | } 852 | 853 | if ((targ_len = libssh2_sftp_symlink_ex(data->sftp, link->val, link->len, targ, 8192, LIBSSH2_SFTP_READLINK)) < 0) { 854 | php_error_docref(NULL, E_WARNING, "Unable to read link '%s'", ZSTR_VAL(link)); 855 | RETURN_FALSE; 856 | } 857 | 858 | RETURN_STRINGL(targ, targ_len); 859 | } 860 | /* }}} */ 861 | 862 | /* {{{ proto string ssh2_sftp_realpath(resource sftp, string filename) 863 | */ 864 | PHP_FUNCTION(ssh2_sftp_realpath) 865 | { 866 | php_ssh2_sftp_data *data; 867 | zval *zsftp; 868 | zend_string *link; 869 | int targ_len = 0; 870 | char targ[8192]; 871 | 872 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "rS", &zsftp, &link) == FAILURE) { 873 | return; 874 | } 875 | 876 | if ((data = (php_ssh2_sftp_data *)zend_fetch_resource(Z_RES_P(zsftp), PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp)) == NULL) { 877 | RETURN_FALSE; 878 | } 879 | 880 | if (data->session_rsrc->ptr == NULL) { 881 | RETURN_FALSE; 882 | } 883 | 884 | if ((targ_len = libssh2_sftp_symlink_ex(data->sftp, link->val, link->len, targ, 8192, LIBSSH2_SFTP_REALPATH)) < 0) { 885 | php_error_docref(NULL, E_WARNING, "Unable to resolve realpath for '%s'", link->val); 886 | RETURN_FALSE; 887 | } 888 | 889 | RETURN_STRINGL(targ, targ_len); 890 | } 891 | /* }}} */ 892 | 893 | 894 | /* 895 | * Local variables: 896 | * tab-width: 4 897 | * c-basic-offset: 4 898 | * indent-tabs-mode: t 899 | * End: 900 | */ 901 | 902 | -------------------------------------------------------------------------------- /ssh2_fopen_wrappers.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 7 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2016 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.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 | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | #ifdef HAVE_CONFIG_H 20 | #include "config.h" 21 | #endif 22 | 23 | #include "php.h" 24 | #include "php_ssh2.h" 25 | 26 | void *php_ssh2_zval_from_resource_handle(int handle) { 27 | zval *val; 28 | zend_resource *zr; 29 | ZEND_HASH_FOREACH_VAL(&EG(regular_list), val) { 30 | zr = Z_RES_P(val); 31 | if (zr->handle == handle) { 32 | return val; 33 | } 34 | } ZEND_HASH_FOREACH_END(); 35 | return NULL; 36 | } 37 | 38 | 39 | /* ********************** 40 | * channel_stream_ops * 41 | ********************** */ 42 | 43 | #if PHP_VERSION_ID < 70400 44 | static size_t php_ssh2_channel_stream_write(php_stream *stream, const char *buf, size_t count) 45 | #else 46 | static ssize_t php_ssh2_channel_stream_write(php_stream *stream, const char *buf, size_t count) 47 | #endif 48 | { 49 | php_ssh2_channel_data *abstract = (php_ssh2_channel_data*)stream->abstract; 50 | ssize_t writestate; 51 | LIBSSH2_SESSION *session; 52 | 53 | libssh2_channel_set_blocking(abstract->channel, abstract->is_blocking); 54 | session = (LIBSSH2_SESSION *)zend_fetch_resource(abstract->session_rsrc, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); 55 | 56 | 57 | 58 | #ifdef PHP_SSH2_SESSION_TIMEOUT 59 | if (abstract->is_blocking) { 60 | libssh2_session_set_timeout(session, abstract->timeout); 61 | } 62 | #endif 63 | 64 | writestate = libssh2_channel_write_ex(abstract->channel, abstract->streamid, buf, count); 65 | 66 | #ifdef PHP_SSH2_SESSION_TIMEOUT 67 | if (abstract->is_blocking) { 68 | libssh2_session_set_timeout(session, 0); 69 | } 70 | #endif 71 | 72 | if (writestate == LIBSSH2_ERROR_EAGAIN) { 73 | #if PHP_VERSION_ID < 70400 74 | writestate = 0; 75 | #endif 76 | } else if (writestate < 0) { 77 | char *error_msg = NULL; 78 | if (libssh2_session_last_error(session, &error_msg, NULL, 0) == writestate) { 79 | php_error_docref(NULL, E_WARNING, "Failure '%s' (%ld)", error_msg, writestate); 80 | } 81 | 82 | stream->eof = 1; 83 | #if PHP_VERSION_ID < 70400 84 | writestate = 0; 85 | #endif 86 | } 87 | 88 | return writestate; 89 | } 90 | 91 | #if PHP_VERSION_ID < 70400 92 | static size_t php_ssh2_channel_stream_read(php_stream *stream, char *buf, size_t count) 93 | #else 94 | static ssize_t php_ssh2_channel_stream_read(php_stream *stream, char *buf, size_t count) 95 | #endif 96 | { 97 | php_ssh2_channel_data *abstract = (php_ssh2_channel_data*)stream->abstract; 98 | ssize_t readstate; 99 | LIBSSH2_SESSION *session; 100 | 101 | stream->eof = libssh2_channel_eof(abstract->channel); 102 | libssh2_channel_set_blocking(abstract->channel, abstract->is_blocking); 103 | session = (LIBSSH2_SESSION *)zend_fetch_resource(abstract->session_rsrc, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); 104 | 105 | #ifdef PHP_SSH2_SESSION_TIMEOUT 106 | if (abstract->is_blocking) { 107 | libssh2_session_set_timeout(session, abstract->timeout); 108 | } 109 | #endif 110 | 111 | readstate = libssh2_channel_read_ex(abstract->channel, abstract->streamid, buf, count); 112 | 113 | #ifdef PHP_SSH2_SESSION_TIMEOUT 114 | if (abstract->is_blocking) { 115 | libssh2_session_set_timeout(session, 0); 116 | } 117 | #endif 118 | 119 | if (readstate == LIBSSH2_ERROR_EAGAIN) { 120 | #if PHP_VERSION_ID < 70400 121 | readstate = 0; 122 | #endif 123 | } else if (readstate < 0) { 124 | char *error_msg = NULL; 125 | if (libssh2_session_last_error(session, &error_msg, NULL, 0) == readstate) { 126 | php_error_docref(NULL, E_WARNING, "Failure '%s' (%ld)", error_msg, readstate); 127 | } 128 | 129 | stream->eof = 1; 130 | readstate = 0; 131 | } 132 | return readstate; 133 | } 134 | 135 | static int php_ssh2_channel_stream_close(php_stream *stream, int close_handle) 136 | { 137 | php_ssh2_channel_data *abstract = (php_ssh2_channel_data*)stream->abstract; 138 | 139 | if (!abstract->refcount || (--(*(abstract->refcount)) == 0)) { 140 | /* Last one out, turn off the lights */ 141 | if (abstract->refcount) { 142 | efree(abstract->refcount); 143 | } 144 | libssh2_channel_eof(abstract->channel); 145 | libssh2_channel_free(abstract->channel); 146 | zend_list_delete(abstract->session_rsrc); 147 | } 148 | efree(abstract); 149 | 150 | return 0; 151 | } 152 | 153 | static int php_ssh2_channel_stream_flush(php_stream *stream) 154 | { 155 | php_ssh2_channel_data *abstract = (php_ssh2_channel_data*)stream->abstract; 156 | 157 | return libssh2_channel_flush_ex(abstract->channel, abstract->streamid); 158 | } 159 | 160 | static int php_ssh2_channel_stream_cast(php_stream *stream, int castas, void **ret) 161 | { 162 | php_ssh2_channel_data *abstract = (php_ssh2_channel_data*)stream->abstract; 163 | LIBSSH2_SESSION *session; 164 | php_ssh2_session_data **session_data; 165 | 166 | session = (LIBSSH2_SESSION *)zend_fetch_resource(abstract->session_rsrc, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); 167 | session_data = (php_ssh2_session_data **)libssh2_session_abstract(session); 168 | 169 | switch (castas) { 170 | case PHP_STREAM_AS_FD: 171 | case PHP_STREAM_AS_FD_FOR_SELECT: 172 | case PHP_STREAM_AS_SOCKETD: 173 | if (ret) { 174 | *(php_socket_t *)ret = (*session_data)->socket; 175 | } 176 | return SUCCESS; 177 | default: 178 | return FAILURE; 179 | } 180 | } 181 | 182 | static int php_ssh2_channel_stream_set_option(php_stream *stream, int option, int value, void *ptrparam) 183 | { 184 | php_ssh2_channel_data *abstract = (php_ssh2_channel_data*)stream->abstract; 185 | int ret; 186 | 187 | switch (option) { 188 | case PHP_STREAM_OPTION_BLOCKING: 189 | ret = abstract->is_blocking; 190 | abstract->is_blocking = value; 191 | return ret; 192 | break; 193 | 194 | case PHP_STREAM_OPTION_META_DATA_API: 195 | add_assoc_long((zval*)ptrparam, "exit_status", libssh2_channel_get_exit_status(abstract->channel)); 196 | break; 197 | 198 | case PHP_STREAM_OPTION_READ_TIMEOUT: 199 | ret = abstract->timeout; 200 | #ifdef PHP_SSH2_SESSION_TIMEOUT 201 | struct timeval tv = *(struct timeval*)ptrparam; 202 | abstract->timeout = tv.tv_sec * 1000 + (tv.tv_usec / 1000); 203 | #else 204 | php_error_docref(NULL, E_WARNING, "No support for ssh2 stream timeout. Please recompile with libssh2 >= 1.2.9"); 205 | #endif 206 | return ret; 207 | break; 208 | 209 | case PHP_STREAM_OPTION_CHECK_LIVENESS: 210 | return stream->eof = libssh2_channel_eof(abstract->channel); 211 | break; 212 | } 213 | 214 | return -1; 215 | } 216 | 217 | php_stream_ops php_ssh2_channel_stream_ops = { 218 | php_ssh2_channel_stream_write, 219 | php_ssh2_channel_stream_read, 220 | php_ssh2_channel_stream_close, 221 | php_ssh2_channel_stream_flush, 222 | PHP_SSH2_CHANNEL_STREAM_NAME, 223 | NULL, /* seek */ 224 | php_ssh2_channel_stream_cast, 225 | NULL, /* stat */ 226 | php_ssh2_channel_stream_set_option, 227 | }; 228 | 229 | /* ********************* 230 | * Magic Path Helper * 231 | ********************* */ 232 | 233 | /* {{{ php_ssh2_fopen_wrapper_parse_path 234 | * Parse an ssh2.*:// path 235 | */ 236 | php_url *php_ssh2_fopen_wrapper_parse_path(const char *path, char *type, php_stream_context *context, 237 | LIBSSH2_SESSION **psession, zend_resource **presource, 238 | LIBSSH2_SFTP **psftp, zend_resource **psftp_rsrc) 239 | { 240 | php_ssh2_sftp_data *sftp_data = NULL; 241 | LIBSSH2_SESSION *session; 242 | php_url *resource; 243 | zval *methods = NULL, *callbacks = NULL, zsession, *tmpzval; 244 | zend_long resource_id; 245 | char *h, *username = NULL, *password = NULL, *pubkey_file = NULL, *privkey_file = NULL; 246 | int username_len = 0, password_len = 0; 247 | 248 | h = strstr(path, "Resource id #"); 249 | if (h) { 250 | /* Starting with 5.6.28, 7.0.13 need to be clean, else php_url_parse will fail */ 251 | char *tmp = estrdup(path); 252 | 253 | strncpy(tmp + (h-path), h + sizeof("Resource id #")-1, strlen(tmp)-sizeof("Resource id #")); 254 | resource = php_url_parse(tmp); 255 | efree(tmp); 256 | } else { 257 | resource = php_url_parse(path); 258 | } 259 | if (!resource || !resource->path) { 260 | return NULL; 261 | } 262 | 263 | if (strncmp(SSH2_URL_STR(resource->scheme), "ssh2.", sizeof("ssh2.") - 1)) { 264 | /* Not an ssh wrapper */ 265 | php_url_free(resource); 266 | return NULL; 267 | } 268 | 269 | if (strcmp(SSH2_URL_STR(resource->scheme) + sizeof("ssh2.") - 1, type)) { 270 | /* Wrong ssh2. wrapper type */ 271 | php_url_free(resource); 272 | return NULL; 273 | } 274 | 275 | if (!resource->host) { 276 | return NULL; 277 | } 278 | 279 | /* 280 | Find resource->path in the path string, then copy the entire string from the original path. 281 | This includes ?query#fragment in the path string 282 | */ 283 | // TODO copy seems uneeded 284 | #if PHP_VERSION_ID < 70300 285 | { 286 | char * s; 287 | 288 | s = resource->path; 289 | resource->path = estrdup(strstr(path, resource->path)); 290 | efree(s); 291 | } 292 | #else 293 | { 294 | zend_string *tmp; 295 | 296 | tmp = resource->path; 297 | resource->path = zend_string_init(ZSTR_VAL(resource->path), ZSTR_LEN(resource->path), 0); 298 | zend_string_release(tmp); 299 | } 300 | #endif 301 | 302 | /* Look for a resource ID to reuse a session */ 303 | if (is_numeric_string(SSH2_URL_STR(resource->host), SSH2_URL_LEN(resource->host), &resource_id, NULL, 0) == IS_LONG) { 304 | php_ssh2_sftp_data *sftp_data; 305 | zval *zresource; 306 | 307 | if ((zresource = php_ssh2_zval_from_resource_handle(resource_id)) == NULL) { 308 | php_url_free(resource); 309 | return NULL; 310 | } 311 | 312 | if (psftp) { 313 | /* suppress potential warning by passing NULL as resource_type_name */ 314 | sftp_data = (php_ssh2_sftp_data *)zend_fetch_resource(Z_RES_P(zresource), NULL, le_ssh2_sftp); 315 | if (sftp_data) { 316 | /* Want the sftp layer */ 317 | Z_ADDREF_P(zresource); 318 | *psftp_rsrc = Z_RES_P(zresource); 319 | *psftp = sftp_data->sftp; 320 | *presource = sftp_data->session_rsrc; 321 | *psession = sftp_data->session; 322 | return resource; 323 | } 324 | } 325 | session = (LIBSSH2_SESSION *)zend_fetch_resource(Z_RES_P(zresource), PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); 326 | if (session) { 327 | if (psftp) { 328 | /* We need an sftp layer too */ 329 | LIBSSH2_SFTP *sftp = libssh2_sftp_init(session); 330 | 331 | if (!sftp) { 332 | php_url_free(resource); 333 | return NULL; 334 | } 335 | sftp_data = emalloc(sizeof(php_ssh2_sftp_data)); 336 | sftp_data->sftp = sftp; 337 | sftp_data->session = session; 338 | sftp_data->session_rsrc = Z_RES_P(zresource); 339 | Z_ADDREF_P(zresource); 340 | *psftp_rsrc = zend_register_resource(sftp_data, le_ssh2_sftp); 341 | *psftp = sftp; 342 | *presource = Z_RES_P(zresource); 343 | *psession = session; 344 | return resource; 345 | } 346 | Z_ADDREF_P(zresource); 347 | *presource = Z_RES_P(zresource); 348 | *psession = session; 349 | return resource; 350 | } 351 | } 352 | 353 | /* Fallback on finding it in the context */ 354 | if (SSH2_URL_STR(resource->host)[0] == 0 && context && psftp && 355 | (tmpzval = php_stream_context_get_option(context, "ssh2", "sftp")) != NULL && 356 | Z_TYPE_P(tmpzval) == IS_RESOURCE) { 357 | php_ssh2_sftp_data *sftp_data; 358 | sftp_data = (php_ssh2_sftp_data *)zend_fetch_resource(Z_RES_P(tmpzval), PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp); 359 | if (sftp_data) { 360 | Z_ADDREF_P(tmpzval); 361 | *psftp_rsrc = Z_RES_P(tmpzval); 362 | *psftp = sftp_data->sftp; 363 | *presource = sftp_data->session_rsrc; 364 | *psession = sftp_data->session; 365 | return resource; 366 | } 367 | } 368 | if (SSH2_URL_STR(resource->host)[0] == 0 && context && 369 | (tmpzval = php_stream_context_get_option(context, "ssh2", "session")) != NULL && 370 | Z_TYPE_P(tmpzval) == IS_RESOURCE) { 371 | session = (LIBSSH2_SESSION *)zend_fetch_resource(Z_RES_P(tmpzval), PHP_SSH2_SESSION_RES_NAME, le_ssh2_session); 372 | if (session) { 373 | if (psftp) { 374 | /* We need an SFTP layer too! */ 375 | LIBSSH2_SFTP *sftp = libssh2_sftp_init(session); 376 | php_ssh2_sftp_data *sftp_data; 377 | 378 | if (!sftp) { 379 | php_url_free(resource); 380 | return NULL; 381 | } 382 | sftp_data = emalloc(sizeof(php_ssh2_sftp_data)); 383 | sftp_data->sftp = sftp; 384 | sftp_data->session = session; 385 | sftp_data->session_rsrc = Z_RES_P(tmpzval); 386 | Z_ADDREF_P(tmpzval); 387 | *psftp_rsrc = zend_register_resource(sftp_data, le_ssh2_sftp); 388 | *psftp = sftp; 389 | *presource = Z_RES_P(tmpzval); 390 | *psession = session; 391 | return resource; 392 | } 393 | Z_ADDREF_P(tmpzval); 394 | *psession = session; 395 | *presource = Z_RES_P(tmpzval); 396 | return resource; 397 | } 398 | } 399 | 400 | /* Make our own connection then */ 401 | if (!resource->port) { 402 | resource->port = 22; 403 | } 404 | 405 | if (context && 406 | (tmpzval = php_stream_context_get_option(context, "ssh2", "methods")) != NULL && 407 | Z_TYPE_P(tmpzval) == IS_ARRAY) { 408 | methods = tmpzval; 409 | } 410 | 411 | if (context && 412 | (tmpzval = php_stream_context_get_option(context, "ssh2", "callbacks")) != NULL && 413 | Z_TYPE_P(tmpzval) == IS_ARRAY) { 414 | callbacks = tmpzval; 415 | } 416 | 417 | if (context && 418 | (tmpzval = php_stream_context_get_option(context, "ssh2", "username")) != NULL && 419 | Z_TYPE_P(tmpzval) == IS_STRING) { 420 | username = Z_STRVAL_P(tmpzval); 421 | username_len = Z_STRLEN_P(tmpzval); 422 | } 423 | 424 | if (context && 425 | (tmpzval = php_stream_context_get_option(context, "ssh2", "password")) != NULL && 426 | Z_TYPE_P(tmpzval) == IS_STRING) { 427 | password = Z_STRVAL_P(tmpzval); 428 | password_len = Z_STRLEN_P(tmpzval); 429 | } 430 | 431 | if (context && 432 | (tmpzval = php_stream_context_get_option(context, "ssh2", "pubkey_file")) != NULL && 433 | Z_TYPE_P(tmpzval) == IS_STRING) { 434 | pubkey_file = Z_STRVAL_P(tmpzval); 435 | } 436 | 437 | if (context && 438 | (tmpzval = php_stream_context_get_option(context, "ssh2", "privkey_file")) != NULL && 439 | Z_TYPE_P(tmpzval) == IS_STRING) { 440 | privkey_file = Z_STRVAL_P(tmpzval); 441 | } 442 | 443 | if (resource->user) { 444 | int len = SSH2_URL_LEN(resource->user); 445 | 446 | if (len) { 447 | username = SSH2_URL_STR(resource->user); 448 | username_len = len; 449 | } 450 | } 451 | 452 | if (resource->pass) { 453 | int len = SSH2_URL_LEN(resource->pass); 454 | 455 | if (len) { 456 | password = SSH2_URL_STR(resource->pass); 457 | password_len = len; 458 | } 459 | } 460 | 461 | if (!username) { 462 | /* username is a minimum */ 463 | php_url_free(resource); 464 | return NULL; 465 | } 466 | 467 | session = php_ssh2_session_connect(SSH2_URL_STR(resource->host), resource->port, methods, callbacks); 468 | if (!session) { 469 | /* Unable to connect! */ 470 | php_url_free(resource); 471 | return NULL; 472 | } 473 | 474 | /* Authenticate */ 475 | if (pubkey_file && privkey_file) { 476 | if (php_check_open_basedir(pubkey_file) || php_check_open_basedir(privkey_file)) { 477 | php_url_free(resource); 478 | return NULL; 479 | } 480 | 481 | /* Attempt pubkey authentication */ 482 | if (!libssh2_userauth_publickey_fromfile(session, username, pubkey_file, privkey_file, password)) { 483 | goto session_authed; 484 | } 485 | } 486 | 487 | if (password) { 488 | /* Attempt password authentication */ 489 | if (libssh2_userauth_password_ex(session, username, username_len, password, password_len, NULL) == 0) { 490 | goto session_authed; 491 | } 492 | } 493 | 494 | /* Auth failure */ 495 | php_url_free(resource); 496 | if (Z_RES(zsession)) { 497 | zend_list_delete(Z_RES(zsession)); 498 | } 499 | return NULL; 500 | 501 | session_authed: 502 | ZVAL_RES(&zsession, zend_register_resource(session, le_ssh2_session)); 503 | 504 | if (psftp && psftp_rsrc) { 505 | LIBSSH2_SFTP *sftp; 506 | 507 | sftp = libssh2_sftp_init(session); 508 | if (!sftp) { 509 | php_url_free(resource); 510 | zend_list_delete(Z_RES(zsession)); 511 | return NULL; 512 | } 513 | 514 | sftp_data = emalloc(sizeof(php_ssh2_sftp_data)); 515 | sftp_data->session = session; 516 | sftp_data->sftp = sftp; 517 | sftp_data->session_rsrc = Z_RES(zsession); 518 | Z_ADDREF(zsession); 519 | *psftp_rsrc = zend_register_resource(sftp_data, le_ssh2_sftp); 520 | *psftp = sftp; 521 | } 522 | 523 | *presource = Z_RES(zsession); 524 | *psession = session; 525 | 526 | return resource; 527 | } 528 | /* }}} */ 529 | 530 | /* ***************** 531 | * Shell Wrapper * 532 | ***************** */ 533 | 534 | /* {{{ php_ssh2_shell_open 535 | * Make a stream from a session 536 | */ 537 | 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) 538 | { 539 | LIBSSH2_CHANNEL *channel; 540 | php_ssh2_channel_data *channel_data; 541 | php_stream *stream; 542 | 543 | libssh2_session_set_blocking(session, 1); 544 | 545 | channel = libssh2_channel_open_session(session); 546 | if (!channel) { 547 | php_error_docref(NULL, E_WARNING, "Unable to request a channel from remote host"); 548 | return NULL; 549 | } 550 | 551 | if (environment) { 552 | zend_string *key; 553 | int key_type; 554 | zend_ulong idx; 555 | 556 | for(zend_hash_internal_pointer_reset(HASH_OF(environment)); 557 | (key_type = zend_hash_get_current_key(HASH_OF(environment), &key, &idx)) != HASH_KEY_NON_EXISTENT; 558 | zend_hash_move_forward(HASH_OF(environment))) { 559 | if (key_type == HASH_KEY_IS_STRING) { 560 | zval *value; 561 | 562 | if ((value = zend_hash_get_current_data(HASH_OF(environment))) != NULL) { 563 | zval copyval = *value; 564 | 565 | zval_copy_ctor(©val); 566 | convert_to_string(©val); 567 | if (libssh2_channel_setenv_ex(channel, key->val, key->len, Z_STRVAL(copyval), Z_STRLEN(copyval))) { 568 | php_error_docref(NULL, E_WARNING, "Failed setting %s=%s on remote end", ZSTR_VAL(key), Z_STRVAL(copyval)); 569 | } 570 | zval_dtor(©val); 571 | } 572 | } else { 573 | php_error_docref(NULL, E_NOTICE, "Skipping numeric index in environment array"); 574 | } 575 | } 576 | } 577 | 578 | if (type == PHP_SSH2_TERM_UNIT_CHARS) { 579 | if (libssh2_channel_request_pty_ex(channel, term, term_len, NULL, 0, width, height, 0, 0)) { 580 | php_error_docref(NULL, E_WARNING, "Failed allocating %s pty at %ldx%ld characters", term, width, height); 581 | libssh2_channel_free(channel); 582 | return NULL; 583 | } 584 | } else { 585 | if (libssh2_channel_request_pty_ex(channel, term, term_len, NULL, 0, 0, 0, width, height)) { 586 | php_error_docref(NULL, E_WARNING, "Failed allocating %s pty at %ldx%ld pixels", term, width, height); 587 | libssh2_channel_free(channel); 588 | return NULL; 589 | } 590 | } 591 | 592 | if (libssh2_channel_shell(channel)) { 593 | php_error_docref(NULL, E_WARNING, "Unable to request shell from remote host"); 594 | libssh2_channel_free(channel); 595 | return NULL; 596 | } 597 | 598 | /* Turn it into a stream */ 599 | channel_data = emalloc(sizeof(php_ssh2_channel_data)); 600 | channel_data->channel = channel; 601 | channel_data->streamid = 0; 602 | channel_data->is_blocking = 0; 603 | channel_data->timeout = 0; 604 | channel_data->session_rsrc = resource; 605 | channel_data->refcount = NULL; 606 | 607 | stream = php_stream_alloc(&php_ssh2_channel_stream_ops, channel_data, 0, "r+"); 608 | 609 | return stream; 610 | } 611 | /* }}} */ 612 | 613 | /* {{{ php_ssh2_fopen_wrapper_shell 614 | * ssh2.shell:// fopen wrapper 615 | */ 616 | 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) 617 | { 618 | LIBSSH2_SESSION *session = NULL; 619 | php_stream *stream; 620 | zval *tmpzval, *environment = NULL; 621 | char *terminal = PHP_SSH2_DEFAULT_TERMINAL; 622 | zend_long width = PHP_SSH2_DEFAULT_TERM_WIDTH; 623 | zend_long height = PHP_SSH2_DEFAULT_TERM_HEIGHT; 624 | zend_long type = PHP_SSH2_DEFAULT_TERM_UNIT; 625 | zend_resource *rsrc = NULL; 626 | int terminal_len = sizeof(PHP_SSH2_DEFAULT_TERMINAL) - 1; 627 | php_url *resource; 628 | char *s; 629 | 630 | resource = php_ssh2_fopen_wrapper_parse_path(path, "shell", context, &session, &rsrc, NULL, NULL); 631 | if (!resource || !session) { 632 | return NULL; 633 | } 634 | 635 | if (context && 636 | (tmpzval = php_stream_context_get_option(context, "ssh2", "env")) != NULL && Z_TYPE_P(tmpzval) == IS_ARRAY) { 637 | environment = tmpzval; 638 | } 639 | 640 | if (context && 641 | (tmpzval = php_stream_context_get_option(context, "ssh2", "term")) != NULL && Z_TYPE_P(tmpzval) == IS_STRING) { 642 | terminal = Z_STRVAL_P(tmpzval); 643 | terminal_len = Z_STRLEN_P(tmpzval); 644 | } 645 | 646 | if (context && 647 | (tmpzval = php_stream_context_get_option(context, "ssh2", "term_width")) != NULL) { 648 | zval copyval; 649 | copyval = *tmpzval; 650 | convert_to_long(©val); 651 | width = Z_LVAL_P(©val); 652 | zval_ptr_dtor(©val); 653 | } 654 | 655 | if (context && 656 | (tmpzval = php_stream_context_get_option(context, "ssh2", "term_height")) != NULL) { 657 | zval copyval; 658 | copyval = *tmpzval; 659 | convert_to_long(©val); 660 | height = Z_LVAL_P(©val); 661 | zval_ptr_dtor(©val); 662 | } 663 | 664 | if (context && 665 | (tmpzval = php_stream_context_get_option(context, "ssh2", "term_units")) != NULL) { 666 | zval copyval; 667 | copyval = *tmpzval; 668 | convert_to_long(©val); 669 | type = Z_LVAL_P(©val); 670 | zval_ptr_dtor(©val); 671 | } 672 | 673 | s = resource->path ? SSH2_URL_STR(resource->path) : NULL; 674 | 675 | if (s && s[0] == '/') { 676 | /* Terminal type encoded into URL overrides context terminal type */ 677 | char *p; 678 | 679 | s++; 680 | p = strchr(s, '/'); 681 | if (p) { 682 | if (p - s) { 683 | terminal = s; 684 | terminal_len = p - terminal; 685 | s += terminal_len + 1; 686 | } else { 687 | /* "null" terminal given, skip it */ 688 | s++; 689 | } 690 | } else { 691 | int len; 692 | 693 | if ((len = strlen(path + 1))) { 694 | terminal = s; 695 | terminal_len = len; 696 | s += len; 697 | } 698 | } 699 | } 700 | 701 | /* TODO: Accept resolution and environment vars as URL style parameters 702 | * ssh2.shell://hostorresource/terminal/99x99c?envvar=envval&envvar=envval.... 703 | */ 704 | stream = php_ssh2_shell_open(session, rsrc, terminal, terminal_len, environment, width, height, type); 705 | if (!stream) { 706 | zend_list_delete(rsrc); 707 | } 708 | php_url_free(resource); 709 | 710 | return stream; 711 | } 712 | /* }}} */ 713 | 714 | static php_stream_wrapper_ops php_ssh2_shell_stream_wops = { 715 | php_ssh2_fopen_wrapper_shell, 716 | NULL, /* stream_close */ 717 | NULL, /* stat */ 718 | NULL, /* stat_url */ 719 | NULL, /* opendir */ 720 | "ssh2.shell" 721 | }; 722 | 723 | php_stream_wrapper php_ssh2_stream_wrapper_shell = { 724 | &php_ssh2_shell_stream_wops, 725 | NULL, 726 | 0 727 | }; 728 | 729 | /* {{{ proto stream ssh2_shell(resource session[, string term_type[, array env[, int width, int height[, int width_height_type]]]]) 730 | * Open a shell at the remote end and allocate a channel for it 731 | */ 732 | PHP_FUNCTION(ssh2_shell) 733 | { 734 | LIBSSH2_SESSION *session; 735 | php_stream *stream; 736 | zval *zsession; 737 | zval *environment = NULL; 738 | char *term = PHP_SSH2_DEFAULT_TERMINAL; 739 | size_t term_len = sizeof(PHP_SSH2_DEFAULT_TERMINAL) - 1; 740 | zend_long width = PHP_SSH2_DEFAULT_TERM_WIDTH; 741 | zend_long height = PHP_SSH2_DEFAULT_TERM_HEIGHT; 742 | zend_long type = PHP_SSH2_DEFAULT_TERM_UNIT; 743 | int argc = ZEND_NUM_ARGS(); 744 | 745 | if (argc == 5) { 746 | php_error_docref(NULL, E_ERROR, "width specified without height parameter"); 747 | RETURN_FALSE; 748 | } 749 | 750 | if (zend_parse_parameters(argc, "r|sa!lll", &zsession, &term, &term_len, &environment, &width, &height, &type) == FAILURE) { 751 | return; 752 | } 753 | 754 | SSH2_FETCH_AUTHENTICATED_SESSION(session, zsession); 755 | 756 | stream = php_ssh2_shell_open(session, Z_RES_P(zsession), term, term_len, environment, width, height, type); 757 | if (!stream) { 758 | RETURN_FALSE; 759 | } 760 | 761 | /* Ensure that channels are freed BEFORE the sessions they belong to */ 762 | Z_ADDREF_P(zsession); 763 | 764 | php_stream_to_zval(stream, return_value); 765 | } 766 | /* }}} */ 767 | 768 | PHP_FUNCTION(ssh2_shell_resize) 769 | { 770 | zend_long width; 771 | zend_long height; 772 | zend_long width_px = 0; 773 | zend_long height_px = 0; 774 | zval *zparent; 775 | php_stream *parent; 776 | php_ssh2_channel_data *data; 777 | 778 | int argc = ZEND_NUM_ARGS(); 779 | 780 | if (zend_parse_parameters(argc, "rll|ll", &zparent, &width, &height, &width_px, &height_px) == FAILURE) { 781 | return; 782 | } 783 | 784 | php_stream_from_zval(parent, zparent); 785 | 786 | if (parent->ops != &php_ssh2_channel_stream_ops) { 787 | php_error_docref(NULL, E_WARNING, "Provided stream is not of type " PHP_SSH2_CHANNEL_STREAM_NAME); 788 | RETURN_FALSE; 789 | } 790 | 791 | data = (php_ssh2_channel_data*)parent->abstract; 792 | 793 | libssh2_channel_request_pty_size_ex(data->channel, width, height, width_px, height_px); 794 | 795 | RETURN_TRUE; 796 | } 797 | 798 | /* **************** 799 | * Exec Wrapper * 800 | **************** */ 801 | 802 | /* {{{ php_ssh2_exec_command 803 | * Make a stream from a session 804 | */ 805 | 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) 806 | { 807 | LIBSSH2_CHANNEL *channel; 808 | php_ssh2_channel_data *channel_data; 809 | php_stream *stream; 810 | 811 | libssh2_session_set_blocking(session, 1); 812 | 813 | channel = libssh2_channel_open_session(session); 814 | if (!channel) { 815 | php_error_docref(NULL, E_WARNING, "Unable to request a channel from remote host"); 816 | return NULL; 817 | } 818 | 819 | if (environment) { 820 | zend_string *key = NULL; 821 | int key_type; 822 | zend_ulong idx = 0; 823 | HashPosition pos; 824 | 825 | for(zend_hash_internal_pointer_reset_ex(HASH_OF(environment), &pos); 826 | (key_type = zend_hash_get_current_key_ex(HASH_OF(environment), &key, &idx, &pos)) != HASH_KEY_NON_EXISTENT; 827 | zend_hash_move_forward_ex(HASH_OF(environment), &pos)) { 828 | if (key_type == HASH_KEY_IS_STRING) { 829 | zval *value; 830 | 831 | if ((value = zend_hash_get_current_data(HASH_OF(environment))) != NULL) { 832 | zval copyval = *value; 833 | 834 | zval_copy_ctor(©val); 835 | convert_to_string(©val); 836 | if (libssh2_channel_setenv_ex(channel, key->val, key->len, Z_STRVAL(copyval), Z_STRLEN(copyval))) { 837 | php_error_docref(NULL, E_WARNING, "Failed setting %s=%s on remote end", ZSTR_VAL(key), Z_STRVAL(copyval)); 838 | } 839 | zval_dtor(©val); 840 | } 841 | } else { 842 | php_error_docref(NULL, E_NOTICE, "Skipping numeric index in environment array"); 843 | } 844 | } 845 | } 846 | 847 | if (term) { 848 | if (type == PHP_SSH2_TERM_UNIT_CHARS) { 849 | if (libssh2_channel_request_pty_ex(channel, term, term_len, NULL, 0, width, height, 0, 0)) { 850 | php_error_docref(NULL, E_WARNING, "Failed allocating %s pty at %ldx%ld characters", term, width, height); 851 | libssh2_channel_free(channel); 852 | return NULL; 853 | } 854 | } else { 855 | if (libssh2_channel_request_pty_ex(channel, term, term_len, NULL, 0, 0, 0, width, height)) { 856 | php_error_docref(NULL, E_WARNING, "Failed allocating %s pty at %ldx%ld pixels", term, width, height); 857 | libssh2_channel_free(channel); 858 | return NULL; 859 | } 860 | } 861 | } 862 | 863 | if (libssh2_channel_exec(channel, command)) { 864 | php_error_docref(NULL, E_WARNING, "Unable to request command execution on remote host"); 865 | libssh2_channel_free(channel); 866 | return NULL; 867 | } 868 | 869 | /* Turn it into a stream */ 870 | channel_data = emalloc(sizeof(php_ssh2_channel_data)); 871 | channel_data->channel = channel; 872 | channel_data->streamid = 0; 873 | channel_data->is_blocking = 0; 874 | channel_data->timeout = 0; 875 | channel_data->session_rsrc = rsrc; 876 | channel_data->refcount = NULL; 877 | 878 | stream = php_stream_alloc(&php_ssh2_channel_stream_ops, channel_data, 0, "r+"); 879 | 880 | return stream; 881 | } 882 | /* }}} */ 883 | 884 | /* {{{ php_ssh2_fopen_wrapper_exec 885 | * ssh2.exec:// fopen wrapper 886 | */ 887 | 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) 888 | { 889 | LIBSSH2_SESSION *session = NULL; 890 | php_stream *stream; 891 | zval *tmpzval, *environment = NULL; 892 | zend_resource *rsrc = NULL; 893 | php_url *resource; 894 | char *terminal = NULL; 895 | int terminal_len = 0; 896 | long width = PHP_SSH2_DEFAULT_TERM_WIDTH; 897 | long height = PHP_SSH2_DEFAULT_TERM_HEIGHT; 898 | long type = PHP_SSH2_DEFAULT_TERM_UNIT; 899 | 900 | resource = php_ssh2_fopen_wrapper_parse_path(path, "exec", context, &session, &rsrc, NULL, NULL); 901 | if (!resource || !session) { 902 | return NULL; 903 | } 904 | if (!resource->path) { 905 | php_url_free(resource); 906 | zend_list_delete(rsrc); 907 | return NULL; 908 | } 909 | 910 | if (context && 911 | (tmpzval = php_stream_context_get_option(context, "ssh2", "env")) != NULL && Z_TYPE_P(tmpzval) == IS_ARRAY) { 912 | environment = tmpzval; 913 | } 914 | 915 | if (context && 916 | (tmpzval = php_stream_context_get_option(context, "ssh2", "term")) != NULL && Z_TYPE_P(tmpzval) == IS_STRING) { 917 | terminal = Z_STRVAL_P(tmpzval); 918 | terminal_len = Z_STRLEN_P(tmpzval); 919 | } 920 | 921 | if (context && 922 | (tmpzval = php_stream_context_get_option(context, "ssh2", "term_width")) != NULL) { 923 | zval copyval; 924 | copyval = *tmpzval; 925 | convert_to_long(©val); 926 | width = Z_LVAL_P(©val); 927 | zval_ptr_dtor(©val); 928 | } 929 | 930 | if (context && 931 | (tmpzval = php_stream_context_get_option(context, "ssh2", "term_height")) != NULL) { 932 | zval copyval; 933 | copyval = *tmpzval; 934 | convert_to_long(©val); 935 | height = Z_LVAL_P(©val); 936 | zval_ptr_dtor(©val); 937 | } 938 | 939 | if (context && 940 | (tmpzval = php_stream_context_get_option(context, "ssh2", "term_units")) != NULL) { 941 | zval *copyval; 942 | copyval = tmpzval; 943 | convert_to_long(copyval); 944 | type = Z_LVAL_P(copyval); 945 | zval_ptr_dtor(copyval); 946 | } 947 | 948 | stream = php_ssh2_exec_command(session, rsrc, SSH2_URL_STR(resource->path) + 1, terminal, terminal_len, environment, width, height, type); 949 | if (!stream) { 950 | zend_list_delete(rsrc); 951 | } 952 | php_url_free(resource); 953 | 954 | return stream; 955 | } 956 | /* }}} */ 957 | 958 | static php_stream_wrapper_ops php_ssh2_exec_stream_wops = { 959 | php_ssh2_fopen_wrapper_exec, 960 | NULL, /* stream_close */ 961 | NULL, /* stat */ 962 | NULL, /* stat_url */ 963 | NULL, /* opendir */ 964 | "ssh2.exec" 965 | }; 966 | 967 | php_stream_wrapper php_ssh2_stream_wrapper_exec = { 968 | &php_ssh2_exec_stream_wops, 969 | NULL, 970 | 0 971 | }; 972 | 973 | /* {{{ proto stream ssh2_exec(resource session, string command[, string pty[, array env[, int width[, int height[, int width_height_type]]]]]) 974 | * Execute a command at the remote end and allocate a channel for it 975 | * 976 | * This function has a dirty little secret.... pty and env can be in either order.... shhhh... don't tell anyone 977 | */ 978 | PHP_FUNCTION(ssh2_exec) 979 | { 980 | LIBSSH2_SESSION *session; 981 | php_stream *stream; 982 | zval *zsession; 983 | zval *environment = NULL; 984 | zval *zpty = NULL; 985 | char *command; 986 | size_t command_len; 987 | zend_long width = PHP_SSH2_DEFAULT_TERM_WIDTH; 988 | zend_long height = PHP_SSH2_DEFAULT_TERM_HEIGHT; 989 | zend_long type = PHP_SSH2_DEFAULT_TERM_UNIT; 990 | char *term = NULL; 991 | int term_len = 0; 992 | 993 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs|z!z!lll", &zsession, &command, &command_len, &zpty, &environment, &width, &height, &type) == FAILURE) { 994 | return; 995 | } 996 | 997 | if (zpty && Z_TYPE_P(zpty) == IS_ARRAY) { 998 | /* Swap pty and environment -- old call style */ 999 | zval *tmp = zpty; 1000 | zpty = environment; 1001 | environment = tmp; 1002 | } 1003 | 1004 | if (environment && Z_TYPE_P(environment) != IS_ARRAY) { 1005 | php_error_docref(NULL, E_WARNING, "ssh2_exec() expects arg 4 to be of type array"); 1006 | RETURN_FALSE; 1007 | } 1008 | 1009 | if (zpty) { 1010 | convert_to_string(zpty); 1011 | term = Z_STRVAL_P(zpty); 1012 | term_len = Z_STRLEN_P(zpty); 1013 | } 1014 | 1015 | SSH2_FETCH_AUTHENTICATED_SESSION(session, zsession); 1016 | 1017 | stream = php_ssh2_exec_command(session, Z_RES_P(zsession), command, term, term_len, environment, width, height, type); 1018 | if (!stream) { 1019 | RETURN_FALSE; 1020 | } 1021 | 1022 | /* Ensure that channels are freed BEFORE the sessions they belong to */ 1023 | Z_ADDREF_P(zsession); 1024 | 1025 | php_stream_to_zval(stream, return_value); 1026 | } 1027 | /* }}} */ 1028 | 1029 | /* *************** 1030 | * SCP Wrapper * 1031 | *************** */ 1032 | 1033 | /* {{{ php_ssh2_scp_xfer 1034 | * Make a stream from a session 1035 | */ 1036 | static php_stream *php_ssh2_scp_xfer(LIBSSH2_SESSION *session, zend_resource *rsrc, char *filename) 1037 | { 1038 | LIBSSH2_CHANNEL *channel; 1039 | php_ssh2_channel_data *channel_data; 1040 | php_stream *stream; 1041 | 1042 | channel = libssh2_scp_recv(session, filename, NULL); 1043 | if (!channel) { 1044 | char *error = ""; 1045 | libssh2_session_last_error(session, &error, NULL, 0); 1046 | php_error_docref(NULL, E_WARNING, "Unable to request a channel from remote host: %s", error); 1047 | return NULL; 1048 | } 1049 | 1050 | /* Turn it into a stream */ 1051 | channel_data = emalloc(sizeof(php_ssh2_channel_data)); 1052 | channel_data->channel = channel; 1053 | channel_data->streamid = 0; 1054 | channel_data->is_blocking = 0; 1055 | channel_data->timeout = 0; 1056 | channel_data->session_rsrc = rsrc; 1057 | channel_data->refcount = NULL; 1058 | 1059 | stream = php_stream_alloc(&php_ssh2_channel_stream_ops, channel_data, 0, "r"); 1060 | 1061 | return stream; 1062 | } 1063 | /* }}} */ 1064 | 1065 | /* {{{ php_ssh2_fopen_wrapper_scp 1066 | * 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) 1067 | */ 1068 | 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) 1069 | { 1070 | LIBSSH2_SESSION *session = NULL; 1071 | php_stream *stream; 1072 | zend_resource *rsrc = NULL; 1073 | php_url *resource; 1074 | 1075 | if (strchr(mode, '+') || strchr(mode, 'a') || strchr(mode, 'w')) { 1076 | return NULL; 1077 | } 1078 | 1079 | resource = php_ssh2_fopen_wrapper_parse_path(path, "scp", context, &session, &rsrc, NULL, NULL); 1080 | if (!resource || !session) { 1081 | return NULL; 1082 | } 1083 | if (!resource->path) { 1084 | php_url_free(resource); 1085 | zend_list_delete(rsrc); 1086 | return NULL; 1087 | } 1088 | 1089 | stream = php_ssh2_scp_xfer(session, rsrc, SSH2_URL_STR(resource->path)); 1090 | if (!stream) { 1091 | zend_list_delete(rsrc); 1092 | } 1093 | php_url_free(resource); 1094 | 1095 | return stream; 1096 | } 1097 | /* }}} */ 1098 | 1099 | static php_stream_wrapper_ops php_ssh2_scp_stream_wops = { 1100 | php_ssh2_fopen_wrapper_scp, 1101 | NULL, /* stream_close */ 1102 | NULL, /* stat */ 1103 | NULL, /* stat_url */ 1104 | NULL, /* opendir */ 1105 | "ssh2.scp" 1106 | }; 1107 | 1108 | php_stream_wrapper php_ssh2_stream_wrapper_scp = { 1109 | &php_ssh2_scp_stream_wops, 1110 | NULL, 1111 | 0 1112 | }; 1113 | 1114 | /* {{{ proto bool ssh2_scp_recv(resource session, string remote_file, string local_file) 1115 | * Request a file via SCP 1116 | */ 1117 | PHP_FUNCTION(ssh2_scp_recv) 1118 | { 1119 | LIBSSH2_SESSION *session; 1120 | LIBSSH2_CHANNEL *remote_file; 1121 | struct stat sb; 1122 | php_stream *local_file; 1123 | zval *zsession; 1124 | char *remote_filename, *local_filename; 1125 | size_t remote_filename_len, local_filename_len; 1126 | 1127 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "rss", &zsession, &remote_filename, &remote_filename_len, 1128 | &local_filename, &local_filename_len) == FAILURE) { 1129 | return; 1130 | } 1131 | 1132 | SSH2_FETCH_AUTHENTICATED_SESSION(session, zsession); 1133 | 1134 | remote_file = libssh2_scp_recv(session, remote_filename, &sb); 1135 | if (!remote_file) { 1136 | php_error_docref(NULL, E_WARNING, "Unable to receive remote file"); 1137 | RETURN_FALSE; 1138 | } 1139 | libssh2_channel_set_blocking(remote_file, 1); 1140 | 1141 | local_file = php_stream_open_wrapper(local_filename, "wb", REPORT_ERRORS, NULL); 1142 | if (!local_file) { 1143 | php_error_docref(NULL, E_WARNING, "Unable to write to local file"); 1144 | libssh2_channel_free(remote_file); 1145 | RETURN_FALSE; 1146 | } 1147 | 1148 | while (sb.st_size) { 1149 | char buffer[8192]; 1150 | ssize_t bytes_read, bytes_written; 1151 | 1152 | bytes_read = libssh2_channel_read(remote_file, buffer, sb.st_size > 8192 ? 8192 : sb.st_size); 1153 | if (bytes_read < 0) { 1154 | php_error_docref(NULL, E_WARNING, "Error reading from remote file"); 1155 | libssh2_channel_free(remote_file); 1156 | php_stream_close(local_file); 1157 | RETURN_FALSE; 1158 | } 1159 | bytes_written = php_stream_write(local_file, buffer, bytes_read); 1160 | if (bytes_written < 0) { 1161 | php_error_docref(NULL, E_WARNING, "Error writing to local file"); 1162 | libssh2_channel_free(remote_file); 1163 | php_stream_close(local_file); 1164 | RETURN_FALSE; 1165 | } 1166 | if (bytes_read != bytes_written) { 1167 | php_error_docref(NULL, E_WARNING, "Mismatch in bytes read from remote file and bytes written to local file"); 1168 | libssh2_channel_free(remote_file); 1169 | php_stream_close(local_file); 1170 | RETURN_FALSE; 1171 | } 1172 | sb.st_size -= bytes_read; 1173 | } 1174 | 1175 | libssh2_channel_free(remote_file); 1176 | php_stream_close(local_file); 1177 | 1178 | RETURN_TRUE; 1179 | } 1180 | /* }}} */ 1181 | 1182 | /* {{{ proto stream ssh2_scp_send(resource session, string local_file, string remote_file[, int create_mode = 0644]) 1183 | * Send a file via SCP 1184 | */ 1185 | PHP_FUNCTION(ssh2_scp_send) 1186 | { 1187 | LIBSSH2_SESSION *session; 1188 | LIBSSH2_CHANNEL *remote_file; 1189 | php_stream *local_file; 1190 | zval *zsession; 1191 | char *local_filename, *remote_filename; 1192 | size_t local_filename_len, remote_filename_len; 1193 | zend_long create_mode = 0644; 1194 | php_stream_statbuf ssb; 1195 | int argc = ZEND_NUM_ARGS(); 1196 | 1197 | if (zend_parse_parameters(argc, "rss|l", &zsession, &local_filename, &local_filename_len, 1198 | &remote_filename, &remote_filename_len, &create_mode) == FAILURE) { 1199 | return; 1200 | } 1201 | 1202 | SSH2_FETCH_AUTHENTICATED_SESSION(session, zsession); 1203 | 1204 | local_file = php_stream_open_wrapper(local_filename, "rb", REPORT_ERRORS, NULL); 1205 | if (!local_file) { 1206 | php_error_docref(NULL, E_WARNING, "Unable to read source file"); 1207 | RETURN_FALSE; 1208 | } 1209 | 1210 | if (php_stream_stat(local_file, &ssb)) { 1211 | php_error_docref(NULL, E_WARNING, "Failed statting local file"); 1212 | php_stream_close(local_file); 1213 | RETURN_FALSE; 1214 | } 1215 | 1216 | if (argc < 4) { 1217 | create_mode = ssb.sb.st_mode & 0777; 1218 | } 1219 | 1220 | remote_file = libssh2_scp_send_ex(session, remote_filename, create_mode, ssb.sb.st_size, ssb.sb.st_atime, ssb.sb.st_mtime); 1221 | if (!remote_file) { 1222 | int last_error = 0; 1223 | char *error_msg = NULL; 1224 | 1225 | last_error = libssh2_session_last_error(session, &error_msg, NULL, 0); 1226 | php_error_docref(NULL, E_WARNING, "Failure creating remote file: %s (%d)", error_msg, last_error); 1227 | php_stream_close(local_file); 1228 | RETURN_FALSE; 1229 | } 1230 | libssh2_channel_set_blocking(remote_file, 1); 1231 | 1232 | while (ssb.sb.st_size) { 1233 | char buffer[8192]; 1234 | size_t toread = MIN(8192, ssb.sb.st_size); 1235 | ssize_t bytesread = php_stream_read(local_file, buffer, toread); 1236 | size_t sent = 0; 1237 | ssize_t justsent = 0; 1238 | 1239 | if (bytesread <= 0 || bytesread > toread) { 1240 | php_error_docref(NULL, E_WARNING, "Failed copying file 2"); 1241 | php_stream_close(local_file); 1242 | libssh2_channel_free(remote_file); 1243 | RETURN_FALSE; 1244 | } 1245 | 1246 | 1247 | while (bytesread - sent > 0) { 1248 | if ((justsent = libssh2_channel_write(remote_file, (buffer + sent), bytesread - sent)) < 0) { 1249 | 1250 | switch (justsent) { 1251 | case LIBSSH2_ERROR_EAGAIN: 1252 | php_error_docref(NULL, E_WARNING, "Operation would block"); 1253 | break; 1254 | 1255 | case LIBSSH2_ERROR_ALLOC: 1256 | php_error_docref(NULL,E_WARNING, "An internal memory allocation call failed"); 1257 | break; 1258 | 1259 | case LIBSSH2_ERROR_SOCKET_SEND: 1260 | php_error_docref(NULL,E_WARNING, "Unable to send data on socket"); 1261 | break; 1262 | 1263 | case LIBSSH2_ERROR_CHANNEL_CLOSED: 1264 | php_error_docref(NULL,E_WARNING, "The channel has been closed"); 1265 | break; 1266 | 1267 | case LIBSSH2_ERROR_CHANNEL_EOF_SENT: 1268 | php_error_docref(NULL,E_WARNING, "The channel has been requested to be closed"); 1269 | break; 1270 | } 1271 | 1272 | php_stream_close(local_file); 1273 | libssh2_channel_free(remote_file); 1274 | RETURN_FALSE; 1275 | } 1276 | sent = sent + justsent; 1277 | } 1278 | ssb.sb.st_size -= bytesread; 1279 | } 1280 | 1281 | libssh2_channel_flush_ex(remote_file, LIBSSH2_CHANNEL_FLUSH_ALL); 1282 | php_stream_close(local_file); 1283 | libssh2_channel_free(remote_file); 1284 | RETURN_TRUE; 1285 | } 1286 | /* }}} */ 1287 | 1288 | /* *************************** 1289 | * Direct TCP/IP Transport * 1290 | *************************** */ 1291 | 1292 | /* {{{ php_ssh2_direct_tcpip 1293 | * Make a stream from a session 1294 | */ 1295 | static php_stream *php_ssh2_direct_tcpip(LIBSSH2_SESSION *session, zend_resource *rsrc, char *host, int port) 1296 | { 1297 | LIBSSH2_CHANNEL *channel; 1298 | php_ssh2_channel_data *channel_data; 1299 | php_stream *stream; 1300 | 1301 | libssh2_session_set_blocking(session, 1); 1302 | 1303 | channel = libssh2_channel_direct_tcpip(session, host, port); 1304 | if (!channel) { 1305 | php_error_docref(NULL, E_WARNING, "Unable to request a channel from remote host"); 1306 | return NULL; 1307 | } 1308 | 1309 | /* Turn it into a stream */ 1310 | channel_data = emalloc(sizeof(php_ssh2_channel_data)); 1311 | channel_data->channel = channel; 1312 | channel_data->streamid = 0; 1313 | channel_data->is_blocking = 0; 1314 | channel_data->timeout = 0; 1315 | channel_data->session_rsrc = rsrc; 1316 | channel_data->refcount = NULL; 1317 | 1318 | stream = php_stream_alloc(&php_ssh2_channel_stream_ops, channel_data, 0, "r+"); 1319 | 1320 | return stream; 1321 | } 1322 | /* }}} */ 1323 | 1324 | /* {{{ php_ssh2_fopen_wrapper_tunnel 1325 | * ssh2.tunnel:// fopen wrapper 1326 | */ 1327 | 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) 1328 | { 1329 | LIBSSH2_SESSION *session = NULL; 1330 | php_stream *stream = NULL; 1331 | php_url *resource; 1332 | char *host = NULL; 1333 | int port = 0; 1334 | zend_resource *rsrc; 1335 | 1336 | resource = php_ssh2_fopen_wrapper_parse_path(path, "tunnel", context, &session, &rsrc, NULL, NULL); 1337 | if (!resource || !session) { 1338 | return NULL; 1339 | } 1340 | 1341 | if (resource->path && SSH2_URL_STR(resource->path)[0] == '/') { 1342 | char *colon; 1343 | 1344 | host = SSH2_URL_STR(resource->path) + 1; 1345 | if (*host == '[') { 1346 | /* IPv6 Encapsulated Format */ 1347 | host++; 1348 | colon = strstr(host, "]:"); 1349 | if (colon) { 1350 | *colon = 0; 1351 | colon += 2; 1352 | } 1353 | } else { 1354 | colon = strchr(host, ':'); 1355 | if (colon) { 1356 | *(colon++) = 0; 1357 | } 1358 | } 1359 | if (colon) { 1360 | port = atoi(colon); 1361 | } 1362 | } 1363 | 1364 | if ((port <= 0) || (port > 65535) || !host || (strlen(host) == 0)) { 1365 | /* Invalid connection criteria */ 1366 | php_url_free(resource); 1367 | zend_list_delete(rsrc); 1368 | return NULL; 1369 | } 1370 | 1371 | stream = php_ssh2_direct_tcpip(session, rsrc, host, port); 1372 | if (!stream) { 1373 | zend_list_delete(rsrc); 1374 | } 1375 | php_url_free(resource); 1376 | 1377 | return stream; 1378 | } 1379 | /* }}} */ 1380 | 1381 | static php_stream_wrapper_ops php_ssh2_tunnel_stream_wops = { 1382 | php_ssh2_fopen_wrapper_tunnel, 1383 | NULL, /* stream_close */ 1384 | NULL, /* stat */ 1385 | NULL, /* stat_url */ 1386 | NULL, /* opendir */ 1387 | "ssh2.tunnel" 1388 | }; 1389 | 1390 | php_stream_wrapper php_ssh2_stream_wrapper_tunnel = { 1391 | &php_ssh2_tunnel_stream_wops, 1392 | NULL, 1393 | 0 1394 | }; 1395 | 1396 | /* {{{ proto stream ssh2_tunnel(resource session, string host, int port) 1397 | * Tunnel to remote TCP/IP host/port 1398 | */ 1399 | PHP_FUNCTION(ssh2_tunnel) 1400 | { 1401 | LIBSSH2_SESSION *session; 1402 | php_stream *stream; 1403 | zval *zsession; 1404 | char *host; 1405 | size_t host_len; 1406 | zend_long port; 1407 | 1408 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "rsl", &zsession, &host, &host_len, &port) == FAILURE) { 1409 | return; 1410 | } 1411 | 1412 | SSH2_FETCH_AUTHENTICATED_SESSION(session, zsession); 1413 | 1414 | stream = php_ssh2_direct_tcpip(session, Z_RES_P(zsession), host, port); 1415 | if (!stream) { 1416 | RETURN_FALSE; 1417 | } 1418 | 1419 | /* Ensure that channels are freed BEFORE the sessions they belong to */ 1420 | Z_ADDREF_P(zsession); 1421 | 1422 | php_stream_to_zval(stream, return_value); 1423 | } 1424 | /* }}} */ 1425 | 1426 | /* ****************** 1427 | * Generic Helper * 1428 | ****************** */ 1429 | 1430 | /* {{{ proto stream ssh2_fetch_stream(stream channel, int streamid) 1431 | * Fetch an extended data stream 1432 | */ 1433 | PHP_FUNCTION(ssh2_fetch_stream) 1434 | { 1435 | php_ssh2_channel_data *data, *stream_data; 1436 | php_stream *parent, *stream; 1437 | zval *zparent; 1438 | zend_long streamid; 1439 | 1440 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl", &zparent, &streamid) == FAILURE) { 1441 | return; 1442 | } 1443 | 1444 | if (streamid < 0) { 1445 | php_error_docref(NULL, E_WARNING, "Invalid stream ID requested"); 1446 | RETURN_FALSE; 1447 | } 1448 | 1449 | php_stream_from_zval(parent, zparent); 1450 | 1451 | if (parent->ops != &php_ssh2_channel_stream_ops) { 1452 | php_error_docref(NULL, E_WARNING, "Provided stream is not of type " PHP_SSH2_CHANNEL_STREAM_NAME); 1453 | RETURN_FALSE; 1454 | } 1455 | 1456 | data = (php_ssh2_channel_data*)parent->abstract; 1457 | 1458 | if (!data->refcount) { 1459 | data->refcount = emalloc(sizeof(unsigned char)); 1460 | *(data->refcount) = 1; 1461 | } 1462 | 1463 | if (*(data->refcount) == 255) { 1464 | php_error_docref(NULL, E_WARNING, "Too many streams associated to a single channel"); 1465 | RETURN_FALSE; 1466 | } 1467 | 1468 | (*(data->refcount))++; 1469 | 1470 | stream_data = emalloc(sizeof(php_ssh2_channel_data)); 1471 | memcpy(stream_data, data, sizeof(php_ssh2_channel_data)); 1472 | stream_data->streamid = streamid; 1473 | 1474 | stream = php_stream_alloc(&php_ssh2_channel_stream_ops, stream_data, 0, "r+"); 1475 | if (!stream) { 1476 | php_error_docref(NULL, E_WARNING, "Error opening substream"); 1477 | efree(stream_data); 1478 | (data->refcount)--; 1479 | RETURN_FALSE; 1480 | } 1481 | 1482 | php_stream_to_zval(stream, return_value); 1483 | } 1484 | /* }}} */ 1485 | 1486 | /* {{{ proto stream ssh2_send_eof(stream channel) 1487 | * Sends EOF to a stream. Primary use is to close stdin of an stdio stream. 1488 | */ 1489 | PHP_FUNCTION(ssh2_send_eof) 1490 | { 1491 | php_ssh2_channel_data *data; 1492 | php_stream *parent; 1493 | zval *zparent; 1494 | int ssh2_ret; 1495 | 1496 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zparent) == FAILURE) { 1497 | return; 1498 | } 1499 | 1500 | php_stream_from_zval(parent, zparent); 1501 | if (parent->ops != &php_ssh2_channel_stream_ops) { 1502 | php_error_docref(NULL, E_WARNING, "Provided stream is not of type " PHP_SSH2_CHANNEL_STREAM_NAME); 1503 | RETURN_FALSE; 1504 | } 1505 | 1506 | data = (php_ssh2_channel_data*)parent->abstract; 1507 | if (!data) { 1508 | php_error_docref(NULL, E_WARNING, "Abstract in stream is null"); 1509 | RETURN_FALSE; 1510 | } 1511 | 1512 | ssh2_ret = libssh2_channel_send_eof(data->channel); 1513 | if (ssh2_ret < 0) { 1514 | php_error_docref(NULL, E_WARNING, "Couldn't send EOF to channel (Return code %d)", ssh2_ret); 1515 | RETURN_FALSE; 1516 | } 1517 | 1518 | RETURN_TRUE; 1519 | } 1520 | /* }}} */ 1521 | 1522 | /* 1523 | * Local variables: 1524 | * tab-width: 4 1525 | * c-basic-offset: 4 1526 | * End: 1527 | * vim600: noet sw=4 ts=4 fdm=marker 1528 | * vim<600: noet sw=4 ts=4 1529 | */ 1530 | -------------------------------------------------------------------------------- /ssh2.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 7 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2016 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.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 | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | #ifdef HAVE_CONFIG_H 20 | #include "config.h" 21 | #endif 22 | 23 | #include "php.h" 24 | #include "ext/standard/info.h" 25 | #include "ext/standard/file.h" 26 | #include "php_ssh2.h" 27 | #include "main/php_network.h" 28 | 29 | #if (OPENSSL_VERSION_NUMBER >= 0x00908000L) 30 | #include 31 | #endif 32 | 33 | /* Internal Constants */ 34 | #ifndef SHA_DIGEST_LENGTH 35 | #define SHA_DIGEST_LENGTH 20 36 | #endif 37 | 38 | #ifndef MD5_DIGEST_LENGTH 39 | #define MD5_DIGEST_LENGTH 16 40 | #endif 41 | 42 | /* True global resources - no need for thread safety here */ 43 | int le_ssh2_session; 44 | int le_ssh2_listener; 45 | int le_ssh2_sftp; 46 | int le_ssh2_pkey_subsys; 47 | 48 | /* ************* 49 | * Callbacks * 50 | ************* */ 51 | 52 | /* {{{ php_ssh2_alloc_cb 53 | * Wrap emalloc() 54 | */ 55 | static LIBSSH2_ALLOC_FUNC(php_ssh2_alloc_cb) 56 | { 57 | return emalloc(count); 58 | } 59 | /* }}} */ 60 | 61 | /* {{{ php_ssh2_free_cb 62 | * Wrap efree() 63 | */ 64 | static LIBSSH2_FREE_FUNC(php_ssh2_free_cb) 65 | { 66 | efree(ptr); 67 | } 68 | /* }}} */ 69 | 70 | /* {{{ php_ssh2_realloc_cb 71 | * Wrap erealloc() 72 | */ 73 | static LIBSSH2_REALLOC_FUNC(php_ssh2_realloc_cb) 74 | { 75 | return erealloc(ptr, count); 76 | } 77 | /* }}} */ 78 | 79 | /* {{{ php_ssh2_debug_cb 80 | * Debug packets 81 | */ 82 | LIBSSH2_DEBUG_FUNC(php_ssh2_debug_cb) 83 | { 84 | php_ssh2_session_data *data; 85 | zval args[3]; 86 | 87 | if (!abstract || !*abstract) { 88 | return; 89 | } 90 | data = (php_ssh2_session_data*)*abstract; 91 | if (!data->debug_cb) { 92 | return; 93 | } 94 | 95 | ZVAL_STRINGL(&args[0], message, message_len); 96 | ZVAL_STRINGL(&args[1], language, language_len); 97 | ZVAL_LONG(&args[2], always_display); 98 | 99 | zval retval; 100 | if (FAILURE == call_user_function(NULL, NULL, data->debug_cb, &retval, 3, args)) { 101 | php_error_docref(NULL, E_WARNING, "Failure calling debug callback"); 102 | } 103 | 104 | if (!Z_ISUNDEF(retval)) { 105 | zval_ptr_dtor(&retval); 106 | } 107 | } 108 | /* }}} */ 109 | 110 | /* {{{ php_ssh2_ignore_cb 111 | * Ignore packets 112 | */ 113 | LIBSSH2_IGNORE_FUNC(php_ssh2_ignore_cb) 114 | { 115 | php_ssh2_session_data *data; 116 | zval zretval; 117 | zval args[1]; 118 | 119 | if (!abstract || !*abstract) { 120 | return; 121 | } 122 | data = (php_ssh2_session_data*)*abstract; 123 | if (!data->ignore_cb) { 124 | return; 125 | } 126 | 127 | ZVAL_STRINGL(&args[0], message, message_len); 128 | 129 | if (FAILURE == call_user_function(NULL, NULL, data->ignore_cb, &zretval, 1, args)) { 130 | php_error_docref(NULL, E_WARNING, "Failure calling ignore callback"); 131 | } 132 | if (Z_TYPE_P(&zretval) != IS_UNDEF) { 133 | zval_ptr_dtor(&zretval); 134 | } 135 | } 136 | /* }}} */ 137 | 138 | /* {{{ php_ssh2_macerror_cb 139 | * Called when a MAC error occurs, offers the chance to ignore 140 | * WHY ARE YOU IGNORING MAC ERRORS?????? 141 | */ 142 | LIBSSH2_MACERROR_FUNC(php_ssh2_macerror_cb) 143 | { 144 | php_ssh2_session_data *data; 145 | zval zretval; 146 | zval args[1]; 147 | int retval = -1; 148 | 149 | if (!abstract || !*abstract) { 150 | return -1; 151 | } 152 | data = (php_ssh2_session_data*)*abstract; 153 | if (!data->macerror_cb) { 154 | return -1; 155 | } 156 | 157 | ZVAL_STRINGL(&args[0], packet, packet_len); 158 | 159 | if (FAILURE == call_user_function(NULL, NULL, data->macerror_cb, &zretval, 1, args)) { 160 | php_error_docref(NULL, E_WARNING, "Failure calling macerror callback"); 161 | } else { 162 | retval = zval_is_true(&zretval) ? 0 : -1; 163 | } 164 | if (Z_TYPE_P(&zretval) != IS_UNDEF) { 165 | zval_ptr_dtor(&zretval); 166 | } 167 | 168 | return retval; 169 | } 170 | /* }}} */ 171 | 172 | /* {{{ php_ssh2_disconnect_cb 173 | * Connection closed by foreign host 174 | */ 175 | LIBSSH2_DISCONNECT_FUNC(php_ssh2_disconnect_cb) 176 | { 177 | php_ssh2_session_data *data; 178 | zval args[3]; 179 | 180 | if (!abstract || !*abstract) { 181 | return; 182 | } 183 | data = (php_ssh2_session_data*)*abstract; 184 | if (!data->disconnect_cb) { 185 | return; 186 | } 187 | 188 | ZVAL_LONG(&args[0], reason); 189 | ZVAL_STRINGL(&args[1], message, message_len); 190 | ZVAL_STRINGL(&args[2], language, language_len); 191 | 192 | zval retval; 193 | if (FAILURE == call_user_function(NULL, NULL, data->disconnect_cb, &retval, 3, args)) { 194 | php_error_docref(NULL, E_WARNING, "Failure calling disconnect callback"); 195 | } 196 | 197 | if (!Z_ISUNDEF(retval)) { 198 | zval_ptr_dtor(&retval); 199 | } 200 | } 201 | /* }}} */ 202 | 203 | 204 | 205 | /* ***************** 206 | * Userspace API * 207 | ***************** */ 208 | 209 | /* {{{ php_ssh2_set_callback 210 | * Try to set a method if it's passed in with the hash table 211 | */ 212 | static int php_ssh2_set_callback(LIBSSH2_SESSION *session, HashTable *ht, char *callback, int callback_len, int callback_type, php_ssh2_session_data *data) 213 | { 214 | zval *handler, *copyval; 215 | void *internal_handler; 216 | zend_string *callback_zstring; 217 | 218 | callback_zstring = zend_string_init(callback, callback_len, 0); 219 | if ((handler = zend_hash_find(ht, callback_zstring)) == NULL) { 220 | zend_string_release(callback_zstring); 221 | return 0; 222 | } 223 | zend_string_release(callback_zstring); 224 | 225 | if (!zend_is_callable(handler, 0, NULL)) { 226 | return -1; 227 | } 228 | 229 | copyval = emalloc(sizeof(zval)); 230 | ZVAL_COPY(copyval, handler); 231 | 232 | switch (callback_type) { 233 | case LIBSSH2_CALLBACK_IGNORE: 234 | internal_handler = php_ssh2_ignore_cb; 235 | if (data->ignore_cb) { 236 | zval_ptr_dtor(data->ignore_cb); 237 | } 238 | data->ignore_cb = copyval; 239 | break; 240 | case LIBSSH2_CALLBACK_DEBUG: 241 | internal_handler = php_ssh2_debug_cb; 242 | if (data->debug_cb) { 243 | zval_ptr_dtor(data->debug_cb); 244 | } 245 | data->debug_cb = copyval; 246 | break; 247 | case LIBSSH2_CALLBACK_MACERROR: 248 | internal_handler = php_ssh2_macerror_cb; 249 | if (data->macerror_cb) { 250 | zval_ptr_dtor(data->macerror_cb); 251 | } 252 | data->macerror_cb = copyval; 253 | break; 254 | case LIBSSH2_CALLBACK_DISCONNECT: 255 | internal_handler = php_ssh2_disconnect_cb; 256 | if (data->disconnect_cb) { 257 | zval_ptr_dtor(data->disconnect_cb); 258 | } 259 | data->disconnect_cb = copyval; 260 | break; 261 | default: 262 | zval_ptr_dtor(copyval); 263 | return -1; 264 | } 265 | 266 | libssh2_session_callback_set(session, callback_type, internal_handler); 267 | 268 | return 0; 269 | } 270 | /* }}} */ 271 | 272 | /* {{{ php_ssh2_set_method 273 | * Try to set a method if it's passed in with the hash table 274 | */ 275 | static int php_ssh2_set_method(LIBSSH2_SESSION *session, HashTable *ht, char *method, int method_len, int method_type) 276 | { 277 | zval *value; 278 | zend_string *method_zstring; 279 | 280 | 281 | method_zstring = zend_string_init(method, method_len, 0); 282 | if ((value = zend_hash_find(ht, method_zstring)) == NULL) { 283 | zend_string_release(method_zstring); 284 | return 0; 285 | } 286 | zend_string_release(method_zstring); 287 | 288 | if ((Z_TYPE_P(value) != IS_STRING)) { 289 | return -1; 290 | } 291 | 292 | return libssh2_session_method_pref(session, method_type, Z_STRVAL_P(value)); 293 | } 294 | /* }}} */ 295 | 296 | /* {{{ php_ssh2_session_connect 297 | * Connect to an SSH server with requested methods 298 | */ 299 | LIBSSH2_SESSION *php_ssh2_session_connect(char *host, int port, zval *methods, zval *callbacks) 300 | { 301 | LIBSSH2_SESSION *session; 302 | int socket; 303 | php_ssh2_session_data *data; 304 | struct timeval tv; 305 | zend_string *hash_lookup_zstring; 306 | 307 | tv.tv_sec = FG(default_socket_timeout); 308 | tv.tv_usec = 0; 309 | 310 | socket = php_network_connect_socket_to_host(host, port, SOCK_STREAM, 0, &tv, NULL, NULL, NULL, 0, STREAM_SOCKOP_NONE); 311 | 312 | if (socket <= 0) { 313 | php_error_docref(NULL, E_WARNING, "Unable to connect to %s on port %d", host, port); 314 | return NULL; 315 | } 316 | 317 | data = ecalloc(1, sizeof(php_ssh2_session_data)); 318 | data->socket = socket; 319 | 320 | session = libssh2_session_init_ex(php_ssh2_alloc_cb, php_ssh2_free_cb, php_ssh2_realloc_cb, data); 321 | if (!session) { 322 | php_error_docref(NULL, E_WARNING, "Unable to initialize SSH2 session"); 323 | efree(data); 324 | closesocket(socket); 325 | return NULL; 326 | } 327 | libssh2_banner_set(session, LIBSSH2_SSH_DEFAULT_BANNER " PHP"); 328 | 329 | /* Override method preferences */ 330 | if (methods) { 331 | zval *container; 332 | 333 | if (php_ssh2_set_method(session, HASH_OF(methods), "kex", sizeof("kex") - 1, LIBSSH2_METHOD_KEX)) { 334 | php_error_docref(NULL, E_WARNING, "Failed overriding KEX method"); 335 | } 336 | if (php_ssh2_set_method(session, HASH_OF(methods), "hostkey", sizeof("hostkey") - 1, LIBSSH2_METHOD_HOSTKEY)) { 337 | php_error_docref(NULL, E_WARNING, "Failed overriding HOSTKEY method"); 338 | } 339 | 340 | hash_lookup_zstring = zend_string_init("client_to_server", sizeof("client_to_server") - 1, 0); 341 | if ((container = zend_hash_find(HASH_OF(methods), hash_lookup_zstring)) != NULL && Z_TYPE_P(container) == IS_ARRAY) { 342 | if (php_ssh2_set_method(session, HASH_OF(container), "crypt", sizeof("crypt") - 1, LIBSSH2_METHOD_CRYPT_CS)) { 343 | php_error_docref(NULL, E_WARNING, "Failed overriding client to server CRYPT method"); 344 | } 345 | if (php_ssh2_set_method(session, HASH_OF(container), "mac", sizeof("mac") - 1, LIBSSH2_METHOD_MAC_CS)) { 346 | php_error_docref(NULL, E_WARNING, "Failed overriding client to server MAC method"); 347 | } 348 | if (php_ssh2_set_method(session, HASH_OF(container), "comp", sizeof("comp") - 1, LIBSSH2_METHOD_COMP_CS)) { 349 | php_error_docref(NULL, E_WARNING, "Failed overriding client to server COMP method"); 350 | } 351 | if (php_ssh2_set_method(session, HASH_OF(container), "lang", sizeof("lang") - 1, LIBSSH2_METHOD_LANG_CS)) { 352 | php_error_docref(NULL, E_WARNING, "Failed overriding client to server LANG method"); 353 | } 354 | } 355 | zend_string_release(hash_lookup_zstring); 356 | 357 | hash_lookup_zstring = zend_string_init("server_to_client", sizeof("server_to_client") - 1, 0); 358 | if ((container = zend_hash_find(HASH_OF(methods), hash_lookup_zstring)) != NULL && Z_TYPE_P(container) == IS_ARRAY) { 359 | if (php_ssh2_set_method(session, HASH_OF(container), "crypt", sizeof("crypt") - 1, LIBSSH2_METHOD_CRYPT_SC)) { 360 | php_error_docref(NULL, E_WARNING, "Failed overriding server to client CRYPT method"); 361 | } 362 | if (php_ssh2_set_method(session, HASH_OF(container), "mac", sizeof("mac") - 1, LIBSSH2_METHOD_MAC_SC)) { 363 | php_error_docref(NULL, E_WARNING, "Failed overriding server to client MAC method"); 364 | } 365 | if (php_ssh2_set_method(session, HASH_OF(container), "comp", sizeof("comp") - 1, LIBSSH2_METHOD_COMP_SC)) { 366 | php_error_docref(NULL, E_WARNING, "Failed overriding server to client COMP method"); 367 | } 368 | if (php_ssh2_set_method(session, HASH_OF(container), "lang", sizeof("lang") - 1, LIBSSH2_METHOD_LANG_SC)) { 369 | php_error_docref(NULL, E_WARNING, "Failed overriding server to client LANG method"); 370 | } 371 | } 372 | zend_string_release(hash_lookup_zstring); 373 | 374 | } 375 | 376 | /* Register Callbacks */ 377 | if (callbacks) { 378 | /* ignore debug disconnect macerror */ 379 | 380 | if (php_ssh2_set_callback(session, HASH_OF(callbacks), "ignore", sizeof("ignore") - 1, LIBSSH2_CALLBACK_IGNORE, data)) { 381 | php_error_docref(NULL, E_WARNING, "Failed setting IGNORE callback"); 382 | } 383 | 384 | if (php_ssh2_set_callback(session, HASH_OF(callbacks), "debug", sizeof("debug") - 1, LIBSSH2_CALLBACK_DEBUG, data)) { 385 | php_error_docref(NULL, E_WARNING, "Failed setting DEBUG callback"); 386 | } 387 | 388 | if (php_ssh2_set_callback(session, HASH_OF(callbacks), "macerror", sizeof("macerror") - 1, LIBSSH2_CALLBACK_MACERROR, data)) { 389 | php_error_docref(NULL, E_WARNING, "Failed setting MACERROR callback"); 390 | } 391 | 392 | if (php_ssh2_set_callback(session, HASH_OF(callbacks), "disconnect", sizeof("disconnect") - 1, LIBSSH2_CALLBACK_DISCONNECT, data)) { 393 | php_error_docref(NULL, E_WARNING, "Failed setting DISCONNECT callback"); 394 | } 395 | } 396 | 397 | if (libssh2_session_startup(session, socket)) { 398 | int last_error = 0; 399 | char *error_msg = NULL; 400 | 401 | last_error = libssh2_session_last_error(session, &error_msg, NULL, 0); 402 | php_error_docref(NULL, E_WARNING, "Error starting up SSH connection(%d): %s", last_error, error_msg); 403 | closesocket(socket); 404 | libssh2_session_free(session); 405 | efree(data); 406 | return NULL; 407 | } 408 | 409 | return session; 410 | } 411 | /* }}} */ 412 | 413 | /* {{{ proto resource ssh2_connect(string host[, int port[, array methods[, array callbacks]]]) 414 | * Establish a connection to a remote SSH server and return a resource on success, false on error 415 | */ 416 | PHP_FUNCTION(ssh2_connect) 417 | { 418 | LIBSSH2_SESSION *session; 419 | zval *methods = NULL, *callbacks = NULL; 420 | char *host; 421 | zend_long port = PHP_SSH2_DEFAULT_PORT; 422 | size_t host_len; 423 | 424 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|la!a!", &host, &host_len, &port, &methods, &callbacks) == FAILURE) { 425 | return; 426 | } 427 | 428 | session = php_ssh2_session_connect(host, port, methods, callbacks); 429 | if (!session) { 430 | php_error_docref(NULL, E_WARNING, "Unable to connect to %s", host); 431 | RETURN_FALSE; 432 | } 433 | 434 | RETURN_RES(zend_register_resource(session, le_ssh2_session)); 435 | } 436 | /* }}} */ 437 | 438 | /* {{{ proto resource ssh2_disconnect(resource session) 439 | * close a connection to a remote SSH server and return a true on success, false on error. 440 | */ 441 | PHP_FUNCTION(ssh2_disconnect) 442 | { 443 | zval *zsession; 444 | 445 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zsession) == FAILURE) { 446 | RETURN_FALSE; 447 | } 448 | 449 | zend_list_close(Z_RES_P(zsession)); 450 | 451 | RETURN_TRUE; 452 | } 453 | /* }}} */ 454 | 455 | /* {{{ proto array ssh2_methods_negotiated(resource session) 456 | * Return list of negotiaed methods 457 | */ 458 | PHP_FUNCTION(ssh2_methods_negotiated) 459 | { 460 | LIBSSH2_SESSION *session; 461 | zval *zsession, endpoint; 462 | char *kex, *hostkey, *crypt_cs, *crypt_sc, *mac_cs, *mac_sc, *comp_cs, *comp_sc, *lang_cs, *lang_sc; 463 | 464 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zsession) == FAILURE) { 465 | return; 466 | } 467 | 468 | if ((session = (LIBSSH2_SESSION *)zend_fetch_resource(Z_RES_P(zsession), PHP_SSH2_SESSION_RES_NAME, le_ssh2_session)) == NULL) { 469 | RETURN_FALSE; 470 | } 471 | 472 | kex = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_KEX); 473 | hostkey = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_HOSTKEY); 474 | crypt_cs = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_CRYPT_CS); 475 | crypt_sc = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_CRYPT_SC); 476 | mac_cs = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_MAC_CS); 477 | mac_sc = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_MAC_SC); 478 | comp_cs = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_COMP_CS); 479 | comp_sc = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_COMP_SC); 480 | lang_cs = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_LANG_CS); 481 | lang_sc = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_LANG_SC); 482 | 483 | array_init(return_value); 484 | add_assoc_string(return_value, "kex", kex); 485 | add_assoc_string(return_value, "hostkey", hostkey); 486 | 487 | array_init(&endpoint); 488 | add_assoc_string(&endpoint, "crypt", crypt_cs); 489 | add_assoc_string(&endpoint, "mac", mac_cs); 490 | add_assoc_string(&endpoint, "comp", comp_cs); 491 | add_assoc_string(&endpoint, "lang", lang_cs); 492 | add_assoc_zval(return_value, "client_to_server", &endpoint); 493 | 494 | array_init(&endpoint); 495 | add_assoc_string(&endpoint, "crypt", crypt_sc); 496 | add_assoc_string(&endpoint, "mac", mac_sc); 497 | add_assoc_string(&endpoint, "comp", comp_sc); 498 | add_assoc_string(&endpoint, "lang", lang_sc); 499 | add_assoc_zval(return_value, "server_to_client", &endpoint); 500 | } 501 | /* }}} */ 502 | 503 | /* {{{ proto string ssh2_fingerprint(resource session[, int flags]) 504 | * Returns a server hostkey hash from an active session 505 | * Defaults to MD5 fingerprint encoded as ASCII hex values 506 | */ 507 | PHP_FUNCTION(ssh2_fingerprint) 508 | { 509 | LIBSSH2_SESSION *session; 510 | zval *zsession; 511 | const char *fingerprint; 512 | zend_long flags = 0; 513 | int i, fingerprint_len; 514 | 515 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|l", &zsession, &flags) == FAILURE) { 516 | return; 517 | } 518 | fingerprint_len = (flags & PHP_SSH2_FINGERPRINT_SHA1) ? SHA_DIGEST_LENGTH : MD5_DIGEST_LENGTH; 519 | 520 | if ((session = (LIBSSH2_SESSION *)zend_fetch_resource(Z_RES_P(zsession), PHP_SSH2_SESSION_RES_NAME, le_ssh2_session)) == NULL) { 521 | RETURN_FALSE; 522 | } 523 | 524 | fingerprint = (char*)libssh2_hostkey_hash(session, (flags & PHP_SSH2_FINGERPRINT_SHA1) ? LIBSSH2_HOSTKEY_HASH_SHA1 : LIBSSH2_HOSTKEY_HASH_MD5); 525 | if (!fingerprint) { 526 | php_error_docref(NULL, E_WARNING, "Unable to retrieve fingerprint from specified session"); 527 | RETURN_FALSE; 528 | } 529 | 530 | for(i = 0; i < fingerprint_len; i++) { 531 | if (fingerprint[i] != '\0') { 532 | goto fingerprint_good; 533 | } 534 | } 535 | php_error_docref(NULL, E_WARNING, "No fingerprint available using specified hash"); 536 | RETURN_NULL(); 537 | fingerprint_good: 538 | if (flags & PHP_SSH2_FINGERPRINT_RAW) { 539 | RETURN_STRINGL(fingerprint, fingerprint_len); 540 | } else { 541 | char *hexchars; 542 | 543 | hexchars = emalloc((fingerprint_len * 2) + 1); 544 | for(i = 0; i < fingerprint_len; i++) { 545 | snprintf(hexchars + (2 * i), 3, "%02X", (unsigned char)fingerprint[i]); 546 | } 547 | ZVAL_STRINGL(return_value, hexchars, 2 * fingerprint_len); 548 | efree(hexchars); 549 | } 550 | } 551 | /* }}} */ 552 | 553 | /* {{{ proto array ssh2_auth_none(resource session, string username) 554 | * Attempt "none" authentication, returns a list of allowed methods on failed authentication, 555 | * false on utter failure, or true on success 556 | */ 557 | PHP_FUNCTION(ssh2_auth_none) 558 | { 559 | LIBSSH2_SESSION *session; 560 | zval *zsession; 561 | char *username, *methods, *s, *p; 562 | size_t username_len; 563 | 564 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &zsession, &username, &username_len) == FAILURE) { 565 | return; 566 | } 567 | 568 | if ((session = (LIBSSH2_SESSION *)zend_fetch_resource(Z_RES_P(zsession), PHP_SSH2_SESSION_RES_NAME, le_ssh2_session)) == NULL) { 569 | RETURN_FALSE; 570 | } 571 | 572 | s = methods = libssh2_userauth_list(session, username, username_len); 573 | if (!methods) { 574 | /* Either bad failure, or unexpected success */ 575 | RETURN_BOOL(libssh2_userauth_authenticated(session)); 576 | } 577 | 578 | array_init(return_value); 579 | while ((p = strchr(s, ','))) { 580 | if ((p - s) > 0) { 581 | add_next_index_stringl(return_value, s, p - s); 582 | } 583 | s = p + 1; 584 | } 585 | if (strlen(s)) { 586 | add_next_index_string(return_value, s); 587 | } 588 | } 589 | /* }}} */ 590 | 591 | char *password_for_kbd_callback; 592 | 593 | static void kbd_callback(const char *name, int name_len, 594 | const char *instruction, int instruction_len, 595 | int num_prompts, 596 | const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts, 597 | LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses, 598 | void **abstract) 599 | { 600 | (void)name; 601 | (void)name_len; 602 | (void)instruction; 603 | (void)instruction_len; 604 | if (num_prompts == 1) { 605 | responses[0].text = estrdup(password_for_kbd_callback); 606 | responses[0].length = strlen(password_for_kbd_callback); 607 | } 608 | (void)prompts; 609 | (void)abstract; 610 | } 611 | 612 | /* {{{ proto bool ssh2_auth_password(resource session, string username, string password) 613 | * Authenticate over SSH using a plain password 614 | */ 615 | PHP_FUNCTION(ssh2_auth_password) 616 | { 617 | LIBSSH2_SESSION *session; 618 | zval *zsession; 619 | zend_string *username, *password; 620 | char *userauthlist; 621 | 622 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "rSS", &zsession, &username, &password) == FAILURE) { 623 | return; 624 | } 625 | 626 | SSH2_FETCH_NONAUTHENTICATED_SESSION(session, zsession); 627 | 628 | userauthlist = libssh2_userauth_list(session, username->val, username->len); 629 | 630 | if (userauthlist != NULL) { 631 | password_for_kbd_callback = password->val; 632 | if (strstr(userauthlist, "keyboard-interactive") != NULL) { 633 | if (libssh2_userauth_keyboard_interactive(session, username->val, &kbd_callback) == 0) { 634 | RETURN_TRUE; 635 | } 636 | } 637 | 638 | /* TODO: Support password change callback */ 639 | if (libssh2_userauth_password_ex(session, username->val, username->len, password->val, password->len, NULL)) { 640 | php_error_docref(NULL, E_WARNING, "Authentication failed for %s using password", username->val); 641 | RETURN_FALSE; 642 | } 643 | } 644 | 645 | RETURN_TRUE; 646 | } 647 | /* }}} */ 648 | 649 | /* {{{ proto bool ssh2_auth_pubkey_file(resource session, string username, string pubkeyfile, string privkeyfile[, string passphrase]) 650 | * Authenticate using a public key 651 | */ 652 | PHP_FUNCTION(ssh2_auth_pubkey_file) 653 | { 654 | LIBSSH2_SESSION *session; 655 | zval *zsession; 656 | zend_string *username, *pubkey, *privkey, *passphrase = NULL; 657 | #ifndef PHP_WIN32 658 | zend_string *newpath; 659 | struct passwd *pws; 660 | #endif 661 | 662 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "rSSS|S", &zsession, &username, &pubkey, &privkey, &passphrase) == FAILURE) { 663 | return; 664 | } 665 | 666 | if (php_check_open_basedir(ZSTR_VAL(pubkey)) || php_check_open_basedir(ZSTR_VAL(privkey))) { 667 | RETURN_FALSE; 668 | } 669 | 670 | SSH2_FETCH_NONAUTHENTICATED_SESSION(session, zsession); 671 | #ifndef PHP_WIN32 672 | /* Explode '~/paths' stopgap fix because libssh2 does not accept tilde for homedir 673 | This should be ifdef'ed when a fix is available to support older libssh2 versions*/ 674 | pws = getpwuid(geteuid()); 675 | if (ZSTR_LEN(pubkey) >= 2 && *(ZSTR_VAL(pubkey)) == '~' && *(ZSTR_VAL(pubkey)+1) == '/') { 676 | newpath = zend_string_alloc(strlen(pws->pw_dir) + ZSTR_LEN(pubkey), 0); 677 | strcpy(ZSTR_VAL(newpath), pws->pw_dir); 678 | strcat(ZSTR_VAL(newpath), ZSTR_VAL(pubkey)+1); 679 | zend_string_release(pubkey); 680 | pubkey = newpath; 681 | } 682 | if (ZSTR_LEN(privkey) >= 2 && *(ZSTR_VAL(privkey)) == '~' && *(ZSTR_VAL(privkey)+1) == '/') { 683 | newpath = zend_string_alloc(strlen(pws->pw_dir) + ZSTR_LEN(privkey), 0); 684 | strcpy(ZSTR_VAL(newpath), pws->pw_dir); 685 | strcat(ZSTR_VAL(newpath), ZSTR_VAL(privkey)+1); 686 | zend_string_release(privkey); 687 | privkey = newpath; 688 | } 689 | #endif 690 | 691 | /* TODO: Support passphrase callback */ 692 | if (libssh2_userauth_publickey_fromfile_ex(session, ZSTR_VAL(username), ZSTR_LEN(username), ZSTR_VAL(pubkey), ZSTR_VAL(privkey), passphrase ? ZSTR_VAL(passphrase) : NULL)) { 693 | char *buf; 694 | int len; 695 | libssh2_session_last_error(session, &buf, &len, 0); 696 | php_error_docref(NULL, E_WARNING, "Authentication failed for %s using public key: %s", ZSTR_VAL(username), buf); 697 | RETURN_FALSE; 698 | } 699 | 700 | RETURN_TRUE; 701 | } 702 | /* }}} */ 703 | 704 | 705 | /* {{{ proto bool ssh2_auth_pubkey(resource session, string username, string pubkey, string privkey[, string passphrase]) 706 | * Authenticate using a public key 707 | */ 708 | PHP_FUNCTION(ssh2_auth_pubkey) 709 | { 710 | LIBSSH2_SESSION *session; 711 | zval *zsession; 712 | zend_string *username, *pubkey, *privkey, *passphrase = NULL; 713 | 714 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "rSSS|S", &zsession, &username, &pubkey, &privkey, &passphrase) == FAILURE) { 715 | return; 716 | } 717 | 718 | SSH2_FETCH_NONAUTHENTICATED_SESSION(session, zsession); 719 | 720 | if (libssh2_userauth_publickey_frommemory(session, ZSTR_VAL(username), ZSTR_LEN(username), ZSTR_VAL(pubkey), ZSTR_LEN(pubkey), ZSTR_VAL(privkey), ZSTR_LEN(privkey), passphrase ? ZSTR_VAL(passphrase) : NULL)) { 721 | char *buf; 722 | int len; 723 | libssh2_session_last_error(session, &buf, &len, 0); 724 | php_error_docref(NULL, E_WARNING, "Authentication failed for %s using public key: %s", ZSTR_VAL(username), buf); 725 | RETURN_FALSE; 726 | } 727 | 728 | RETURN_TRUE; 729 | } 730 | /* }}} */ 731 | 732 | /* {{{ proto bool ssh2_auth_hostbased_file(resource session, string username, string hostname, string pubkeyfile, string privkeyfile[, string passphrase[, string local_username]]) 733 | * Authenticate using a hostkey 734 | */ 735 | PHP_FUNCTION(ssh2_auth_hostbased_file) 736 | { 737 | LIBSSH2_SESSION *session; 738 | zval *zsession; 739 | char *username, *hostname, *pubkey, *privkey, *passphrase = NULL, *local_username = NULL; 740 | size_t username_len, hostname_len, pubkey_len, privkey_len, passphrase_len, local_username_len; 741 | 742 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "rssss|s!s!", &zsession, &username, &username_len, 743 | &hostname, &hostname_len, 744 | &pubkey, &pubkey_len, 745 | &privkey, &privkey_len, 746 | &passphrase, &passphrase_len, 747 | &local_username, &local_username_len) == FAILURE) { 748 | return; 749 | } 750 | 751 | if (php_check_open_basedir(pubkey) || php_check_open_basedir(privkey)) { 752 | RETURN_FALSE; 753 | } 754 | 755 | SSH2_FETCH_NONAUTHENTICATED_SESSION(session, zsession); 756 | 757 | if (!local_username) { 758 | local_username = username; 759 | local_username_len = username_len; 760 | } 761 | 762 | /* TODO: Support passphrase callback */ 763 | if (libssh2_userauth_hostbased_fromfile_ex(session, username, username_len, pubkey, privkey, passphrase, hostname, hostname_len, local_username, local_username_len)) { 764 | php_error_docref(NULL, E_WARNING, "Authentication failed for %s using hostbased public key", username); 765 | RETURN_FALSE; 766 | } 767 | 768 | RETURN_TRUE; 769 | } 770 | /* }}} */ 771 | 772 | /* {{{ proto resource ssh2_forward_listen(resource session, int port[, string host[, long max_connections]]) 773 | * Bind a port on the remote server and listen for connections 774 | */ 775 | PHP_FUNCTION(ssh2_forward_listen) 776 | { 777 | zval *zsession; 778 | LIBSSH2_SESSION *session; 779 | LIBSSH2_LISTENER *listener; 780 | php_ssh2_listener_data *data; 781 | zend_long port; 782 | char *host = NULL; 783 | size_t host_len; 784 | zend_long max_connections = PHP_SSH2_LISTEN_MAX_QUEUED; 785 | 786 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl|sl", &zsession, &port, &host, &host_len, &max_connections) == FAILURE) { 787 | return; 788 | } 789 | 790 | SSH2_FETCH_AUTHENTICATED_SESSION(session, zsession); 791 | 792 | listener = libssh2_channel_forward_listen_ex(session, host, port, NULL, max_connections); 793 | 794 | if (!listener) { 795 | php_error_docref(NULL, E_WARNING, "Failure listening on remote port"); 796 | RETURN_FALSE; 797 | } 798 | 799 | data = emalloc(sizeof(php_ssh2_listener_data)); 800 | data->session = session; 801 | data->session_rsrc = Z_RES_P(zsession); 802 | Z_ADDREF_P(zsession); 803 | data->listener = listener; 804 | 805 | RETURN_RES(zend_register_resource(data, le_ssh2_listener)); 806 | } 807 | /* }}} */ 808 | 809 | /* {{{ proto stream ssh2_forward_accept(resource listener[, string &shost[, long &sport]]) 810 | * Accept a connection created by a listener 811 | */ 812 | PHP_FUNCTION(ssh2_forward_accept) 813 | { 814 | zval *zlistener; 815 | php_ssh2_listener_data *data; 816 | LIBSSH2_CHANNEL *channel; 817 | php_ssh2_channel_data *channel_data; 818 | php_stream *stream; 819 | 820 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zlistener) == FAILURE) { 821 | return; 822 | } 823 | 824 | if ((data = (php_ssh2_listener_data *)zend_fetch_resource(Z_RES_P(zlistener), PHP_SSH2_LISTENER_RES_NAME, le_ssh2_listener)) == NULL) { 825 | RETURN_FALSE; 826 | } 827 | 828 | channel = libssh2_channel_forward_accept(data->listener); 829 | 830 | if (!channel) { 831 | RETURN_FALSE; 832 | } 833 | 834 | channel_data = emalloc(sizeof(php_ssh2_channel_data)); 835 | channel_data->channel = channel; 836 | channel_data->streamid = 0; 837 | channel_data->is_blocking = 0; 838 | channel_data->session_rsrc = data->session_rsrc; 839 | channel_data->refcount = NULL; 840 | 841 | stream = php_stream_alloc(&php_ssh2_channel_stream_ops, channel_data, 0, "r+"); 842 | if (!stream) { 843 | php_error_docref(NULL, E_WARNING, "Failure allocating stream"); 844 | efree(channel_data); 845 | libssh2_channel_free(channel); 846 | RETURN_FALSE; 847 | } 848 | 849 | #if PHP_VERSION_ID < 70300 850 | GC_REFCOUNT(channel_data->session_rsrc)++; 851 | #else 852 | GC_ADDREF(channel_data->session_rsrc); 853 | #endif 854 | 855 | php_stream_to_zval(stream, return_value); 856 | } 857 | /* }}} */ 858 | 859 | /* {{{ proto int ssh2_poll(array &polldes[, int timeout]) 860 | * Poll the channels/listeners/streams for events 861 | * Returns number of descriptors which returned non-zero revents 862 | * Input array should be of the form: 863 | * array( 864 | * 0 => array( 865 | * [resource] => $channel,$listener, or $stream 866 | * [events] => SSH2_POLL* flags bitwise ORed together 867 | * ), 868 | * 1 => ... 869 | * ) 870 | * Each subarray will be populated with an revents element on return 871 | */ 872 | PHP_FUNCTION(ssh2_poll) 873 | { 874 | zval *zdesc, *subarray, **pollmap; 875 | zend_long timeout = PHP_SSH2_DEFAULT_POLL_TIMEOUT; 876 | LIBSSH2_POLLFD *pollfds; 877 | int numfds, i = 0, fds_ready; 878 | int le_stream = php_file_le_stream(); 879 | int le_pstream = php_file_le_pstream(); 880 | zend_string *hash_key_zstring; 881 | 882 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|l", &zdesc, &timeout) == FAILURE) { 883 | return; 884 | } 885 | 886 | numfds = zend_hash_num_elements(Z_ARRVAL_P(zdesc)); 887 | pollfds = safe_emalloc(sizeof(LIBSSH2_POLLFD), numfds, 0); 888 | pollmap = safe_emalloc(sizeof(zval**), numfds, 0); 889 | 890 | for(zend_hash_internal_pointer_reset(Z_ARRVAL_P(zdesc)); 891 | (subarray = zend_hash_get_current_data(Z_ARRVAL_P(zdesc))) != NULL; 892 | zend_hash_move_forward(Z_ARRVAL_P(zdesc))) { 893 | zval *tmpzval; 894 | int res_type = 0; 895 | void *res; 896 | 897 | if (Z_TYPE_P(subarray) != IS_ARRAY) { 898 | php_error_docref(NULL, E_WARNING, "Invalid element in poll array, not a sub array"); 899 | numfds--; 900 | continue; 901 | } 902 | 903 | hash_key_zstring = zend_string_init("events", sizeof("events") - 1, 0); 904 | if ((tmpzval = zend_hash_find(Z_ARRVAL_P(subarray), hash_key_zstring)) == NULL || Z_TYPE_P(tmpzval) != IS_LONG) { 905 | php_error_docref(NULL, E_WARNING, "Invalid data in subarray, no events element, or not a bitmask"); 906 | numfds--; 907 | zend_string_release(hash_key_zstring); 908 | continue; 909 | } 910 | zend_string_release(hash_key_zstring); 911 | 912 | pollfds[i].events = Z_LVAL_P(tmpzval); 913 | hash_key_zstring = zend_string_init("resource", sizeof("resource") - 1, 0); 914 | if ((tmpzval = zend_hash_find(Z_ARRVAL_P(subarray), hash_key_zstring)) == NULL || Z_TYPE_P(tmpzval) != IS_REFERENCE 915 | || (tmpzval = Z_REFVAL_P(tmpzval)) == NULL || Z_TYPE_P(tmpzval) != IS_RESOURCE) { 916 | php_error_docref(NULL, E_WARNING, "Invalid data in subarray, no resource element, or not of type resource"); 917 | numfds--; 918 | zend_string_release(hash_key_zstring); 919 | continue; 920 | } 921 | zend_string_release(hash_key_zstring); 922 | 923 | res_type = Z_RES_P(tmpzval)->type; 924 | res = zend_fetch_resource_ex(tmpzval, "Poll Resource", res_type); 925 | if (res_type == le_ssh2_listener) { 926 | pollfds[i].type = LIBSSH2_POLLFD_LISTENER; 927 | pollfds[i].fd.listener = ((php_ssh2_listener_data*)res)->listener; 928 | } else if ((res_type == le_stream || res_type == le_pstream) && 929 | ((php_stream*)res)->ops == &php_ssh2_channel_stream_ops) { 930 | pollfds[i].type = LIBSSH2_POLLFD_CHANNEL; 931 | pollfds[i].fd.channel = ((php_ssh2_channel_data*)(((php_stream*)res)->abstract))->channel; 932 | /* TODO: Add the ability to select against other stream types */ 933 | } else { 934 | php_error_docref(NULL, E_WARNING, "Invalid resource type in subarray: %s", zend_rsrc_list_get_rsrc_type(Z_RES_P(tmpzval))); 935 | numfds--; 936 | continue; 937 | } 938 | pollmap[i] = subarray; 939 | i++; 940 | } 941 | 942 | fds_ready = libssh2_poll(pollfds, numfds, timeout * 1000); 943 | 944 | for(i = 0; i < numfds; i++) { 945 | zval *subarray = pollmap[i]; 946 | 947 | if (Z_REFCOUNT_P(subarray) > 1) { 948 | /* Make a new copy of the subarray zval* */ 949 | zval new_subarray; 950 | ZVAL_DUP(&new_subarray, subarray); 951 | 952 | /* Decrement refcount prior to swapping in new allocation */ 953 | Z_DELREF_P(subarray); 954 | *pollmap[i] = new_subarray; 955 | } 956 | hash_key_zstring = zend_string_init("revents", sizeof("revents") - 1, 0); 957 | zend_hash_del(Z_ARRVAL_P(subarray), hash_key_zstring); 958 | zend_string_release(hash_key_zstring); 959 | 960 | add_assoc_long(subarray, "revents", pollfds[i].revents); 961 | 962 | } 963 | efree(pollmap); 964 | efree(pollfds); 965 | 966 | RETURN_LONG(fds_ready); 967 | } 968 | /* }}} */ 969 | 970 | /* *********************** 971 | * Publickey Subsystem * 972 | *********************** */ 973 | 974 | /* {{{ proto resource ssh2_publickey_init(resource connection) 975 | Initialize the publickey subsystem */ 976 | PHP_FUNCTION(ssh2_publickey_init) 977 | { 978 | zval *zsession; 979 | LIBSSH2_SESSION *session; 980 | LIBSSH2_PUBLICKEY *pkey; 981 | php_ssh2_pkey_subsys_data *data; 982 | 983 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zsession) == FAILURE) { 984 | return; 985 | } 986 | 987 | SSH2_FETCH_AUTHENTICATED_SESSION(session, zsession); 988 | 989 | pkey = libssh2_publickey_init(session); 990 | 991 | if (!pkey) { 992 | int last_error = 0; 993 | char *error_msg = NULL; 994 | 995 | last_error = libssh2_session_last_error(session, &error_msg, NULL, 0); 996 | php_error_docref(NULL, E_WARNING, "Unable to initialize publickey subsystem(%d) %s", last_error, error_msg); 997 | RETURN_FALSE; 998 | } 999 | 1000 | data = emalloc(sizeof(php_ssh2_pkey_subsys_data)); 1001 | data->session = session; 1002 | data->session_rsrc = Z_RES_P(zsession); 1003 | Z_ADDREF_P(zsession); 1004 | data->pkey = pkey; 1005 | 1006 | RETURN_RES(zend_register_resource(data, le_ssh2_pkey_subsys)); 1007 | } 1008 | /* }}} */ 1009 | 1010 | /* {{{ proto bool ssh2_publickey_add(resource pkey, string algoname, string blob[, bool overwrite=FALSE [,array attributes=NULL]]) 1011 | Add an additional publickey */ 1012 | PHP_FUNCTION(ssh2_publickey_add) 1013 | { 1014 | zval *zpkey_data, *zattrs = NULL; 1015 | php_ssh2_pkey_subsys_data *data; 1016 | char *algo, *blob; 1017 | size_t algo_len, blob_len; 1018 | unsigned long num_attrs = 0; 1019 | libssh2_publickey_attribute *attrs = NULL; 1020 | zend_bool overwrite = 0; 1021 | 1022 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "rss|ba", &zpkey_data, &algo, &algo_len, &blob, &blob_len, &overwrite, &zattrs) == FAILURE) { 1023 | return; 1024 | } 1025 | 1026 | 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) { 1027 | RETURN_FALSE; 1028 | } 1029 | 1030 | if (zattrs) { 1031 | HashPosition pos; 1032 | zval *attr_val; 1033 | unsigned long current_attr = 0; 1034 | 1035 | num_attrs = zend_hash_num_elements(Z_ARRVAL_P(zattrs)); 1036 | attrs = safe_emalloc(num_attrs, sizeof(libssh2_publickey_attribute), 0); 1037 | 1038 | for(zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(zattrs), &pos); 1039 | (attr_val = zend_hash_get_current_data_ex(Z_ARRVAL_P(zattrs), &pos)) != NULL; 1040 | zend_hash_move_forward_ex(Z_ARRVAL_P(zattrs), &pos)) { 1041 | zend_string *key; 1042 | int type; 1043 | zend_ulong idx; 1044 | zval copyval = *attr_val; 1045 | 1046 | type = zend_hash_get_current_key_ex(Z_ARRVAL_P(zattrs), &key, &idx, &pos); 1047 | if (type == HASH_KEY_NON_EXISTENT) { 1048 | /* All but impossible */ 1049 | break; 1050 | } 1051 | if (type == HASH_KEY_IS_LONG) { 1052 | /* Malformed, ignore */ 1053 | php_error_docref(NULL, E_WARNING, "Malformed attirbute array, contains numeric index"); 1054 | num_attrs--; 1055 | continue; 1056 | } 1057 | 1058 | if (!key || (key->len == 1 && key->val[0] == '*')) { 1059 | /* Empty key, ignore */ 1060 | php_error_docref(NULL, E_WARNING, "Empty attribute key"); 1061 | num_attrs--; 1062 | continue; 1063 | } 1064 | 1065 | zval_copy_ctor(©val); 1066 | // TODO Sean-Der 1067 | //Z_UNSET_ISREF_P(©val); 1068 | //Z_SET_REFCOUNT_P(©val, 1); 1069 | convert_to_string(©val); 1070 | 1071 | if (key->val[0] == '*') { 1072 | attrs[current_attr].mandatory = 1; 1073 | attrs[current_attr].name = key->val + 1; 1074 | attrs[current_attr].name_len = key->len - 2; 1075 | } else { 1076 | attrs[current_attr].mandatory = 0; 1077 | attrs[current_attr].name = key->val; 1078 | attrs[current_attr].name_len = key->len - 1; 1079 | } 1080 | attrs[current_attr].value_len = Z_STRLEN(copyval); 1081 | attrs[current_attr].value = Z_STRVAL(copyval); 1082 | 1083 | /* copyval deliberately not dtor'd, we're stealing the string */ 1084 | current_attr++; 1085 | } 1086 | } 1087 | 1088 | if (libssh2_publickey_add_ex(data->pkey, (unsigned char *) algo, algo_len, (unsigned char *) blob, blob_len, overwrite, num_attrs, attrs)) { 1089 | php_error_docref(NULL, E_WARNING, "Unable to add %s key", algo); 1090 | RETVAL_FALSE; 1091 | } else { 1092 | RETVAL_TRUE; 1093 | } 1094 | 1095 | if (attrs) { 1096 | unsigned long i; 1097 | 1098 | for(i = 0; i < num_attrs; i++) { 1099 | /* name doesn't need freeing */ 1100 | // TODO Sean-Der 1101 | //efree(attrs[i].value); 1102 | } 1103 | efree(attrs); 1104 | } 1105 | } 1106 | /* }}} */ 1107 | 1108 | /* {{{ proto bool ssh2_publickey_remove(resource pkey, string algoname, string blob) 1109 | Remove a publickey entry */ 1110 | PHP_FUNCTION(ssh2_publickey_remove) 1111 | { 1112 | zval *zpkey_data; 1113 | php_ssh2_pkey_subsys_data *data; 1114 | char *algo, *blob; 1115 | size_t algo_len, blob_len; 1116 | 1117 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "rss", &zpkey_data, &algo, &algo_len, &blob, &blob_len) == FAILURE) { 1118 | return; 1119 | } 1120 | 1121 | 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) { 1122 | RETURN_FALSE; 1123 | } 1124 | 1125 | if (libssh2_publickey_remove_ex(data->pkey, (unsigned char *) algo, algo_len, (unsigned char *) blob, blob_len)) { 1126 | php_error_docref(NULL, E_WARNING, "Unable to remove %s key", algo); 1127 | RETURN_FALSE; 1128 | } 1129 | 1130 | RETURN_TRUE; 1131 | } 1132 | /* }}} */ 1133 | 1134 | /* {{{ proto array ssh2_publickey_list(resource pkey) 1135 | List currently installed publickey entries */ 1136 | PHP_FUNCTION(ssh2_publickey_list) 1137 | { 1138 | zval *zpkey_data; 1139 | php_ssh2_pkey_subsys_data *data; 1140 | unsigned long num_keys, i; 1141 | libssh2_publickey_list *keys; 1142 | zend_string *hash_key_zstring; 1143 | 1144 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zpkey_data) == FAILURE) { 1145 | return; 1146 | } 1147 | 1148 | 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) { 1149 | RETURN_FALSE; 1150 | } 1151 | 1152 | if (libssh2_publickey_list_fetch(data->pkey, &num_keys, &keys)) { 1153 | php_error_docref(NULL, E_WARNING, "Unable to list keys on remote server"); 1154 | RETURN_FALSE; 1155 | } 1156 | 1157 | array_init(return_value); 1158 | for(i = 0; i < num_keys; i++) { 1159 | zval key, attrs; 1160 | unsigned long j; 1161 | 1162 | array_init(&key); 1163 | 1164 | add_assoc_stringl(&key, "name", (char *) keys[i].name, keys[i].name_len); 1165 | add_assoc_stringl(&key, "blob", (char *) keys[i].blob, keys[i].blob_len); 1166 | 1167 | array_init(&attrs); 1168 | for(j = 0; j < keys[i].num_attrs; j++) { 1169 | zval attr; 1170 | 1171 | ZVAL_STRINGL(&attr, keys[i].attrs[j].value, keys[i].attrs[j].value_len); 1172 | hash_key_zstring = zend_string_init(keys[i].attrs[j].name, keys[i].attrs[j].name_len, 0); 1173 | zend_hash_add(Z_ARRVAL_P(&attrs), hash_key_zstring, &attr); 1174 | zend_string_release(hash_key_zstring); 1175 | } 1176 | add_assoc_zval(&key, "attrs", &attrs); 1177 | 1178 | add_next_index_zval(return_value, &key); 1179 | } 1180 | 1181 | libssh2_publickey_list_free(data->pkey, keys); 1182 | } 1183 | /* }}} */ 1184 | 1185 | /* {{{ proto array ssh2_auth_agent(resource session, string username) 1186 | Authenticate using the ssh agent */ 1187 | PHP_FUNCTION(ssh2_auth_agent) 1188 | { 1189 | #ifdef PHP_SSH2_AGENT_AUTH 1190 | zval *zsession; 1191 | char *username; 1192 | size_t username_len; 1193 | 1194 | LIBSSH2_SESSION *session; 1195 | char *userauthlist; 1196 | LIBSSH2_AGENT *agent = NULL; 1197 | int rc; 1198 | struct libssh2_agent_publickey *identity, *prev_identity = NULL; 1199 | 1200 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &zsession, &username, &username_len) == FAILURE) { 1201 | return; 1202 | } 1203 | 1204 | SSH2_FETCH_NONAUTHENTICATED_SESSION(session, zsession); 1205 | 1206 | /* check what authentication methods are available */ 1207 | userauthlist = libssh2_userauth_list(session, username, username_len); 1208 | 1209 | if (userauthlist != NULL && strstr(userauthlist, "publickey") == NULL) { 1210 | php_error_docref(NULL, E_WARNING, "\"publickey\" authentication is not supported"); 1211 | RETURN_FALSE; 1212 | } 1213 | 1214 | /* Connect to the ssh-agent */ 1215 | agent = libssh2_agent_init(session); 1216 | 1217 | if (!agent) { 1218 | php_error_docref(NULL, E_WARNING, "Failure initializing ssh-agent support"); 1219 | RETURN_FALSE; 1220 | } 1221 | 1222 | if (libssh2_agent_connect(agent)) { 1223 | php_error_docref(NULL, E_WARNING, "Failure connecting to ssh-agent"); 1224 | libssh2_agent_free(agent); 1225 | RETURN_FALSE; 1226 | } 1227 | 1228 | if (libssh2_agent_list_identities(agent)) { 1229 | php_error_docref(NULL, E_WARNING, "Failure requesting identities to ssh-agent"); 1230 | libssh2_agent_disconnect(agent); 1231 | libssh2_agent_free(agent); 1232 | RETURN_FALSE; 1233 | } 1234 | 1235 | while (1) { 1236 | rc = libssh2_agent_get_identity(agent, &identity, prev_identity); 1237 | 1238 | if (rc == 1) { 1239 | php_error_docref(NULL, E_WARNING, "Couldn't continue authentication"); 1240 | libssh2_agent_disconnect(agent); 1241 | libssh2_agent_free(agent); 1242 | RETURN_FALSE; 1243 | } 1244 | 1245 | if (rc < 0) { 1246 | php_error_docref(NULL, E_WARNING, "Failure obtaining identity from ssh-agent support"); 1247 | libssh2_agent_disconnect(agent); 1248 | libssh2_agent_free(agent); 1249 | RETURN_FALSE; 1250 | } 1251 | 1252 | if (!libssh2_agent_userauth(agent, username, identity)) { 1253 | libssh2_agent_disconnect(agent); 1254 | libssh2_agent_free(agent); 1255 | RETURN_TRUE; 1256 | } 1257 | prev_identity = identity; 1258 | } 1259 | #else 1260 | 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"); 1261 | RETURN_FALSE; 1262 | #endif /* PHP_SSH2_AGENT_AUTH */ 1263 | } 1264 | /* }}} */ 1265 | 1266 | /* *********************** 1267 | * Module Housekeeping * 1268 | *********************** */ 1269 | 1270 | static void php_ssh2_session_dtor(zend_resource *rsrc) 1271 | { 1272 | LIBSSH2_SESSION *session = (LIBSSH2_SESSION*)rsrc->ptr; 1273 | php_ssh2_session_data **data = (php_ssh2_session_data**)libssh2_session_abstract(session); 1274 | 1275 | libssh2_session_disconnect(session, "PECL/ssh2 (http://pecl.php.net/packages/ssh2)"); 1276 | 1277 | if (*data) { 1278 | if ((*data)->ignore_cb) { 1279 | zval_ptr_dtor((*data)->ignore_cb); 1280 | } 1281 | if ((*data)->debug_cb) { 1282 | zval_ptr_dtor((*data)->debug_cb); 1283 | } 1284 | if ((*data)->macerror_cb) { 1285 | zval_ptr_dtor((*data)->macerror_cb); 1286 | } 1287 | if ((*data)->disconnect_cb) { 1288 | zval_ptr_dtor((*data)->disconnect_cb); 1289 | } 1290 | 1291 | closesocket((*data)->socket); 1292 | 1293 | efree(*data); 1294 | *data = NULL; 1295 | } 1296 | 1297 | libssh2_session_free(session); 1298 | } 1299 | 1300 | static void php_ssh2_listener_dtor(zend_resource *rsrc) 1301 | { 1302 | php_ssh2_listener_data *data = (php_ssh2_listener_data*)rsrc->ptr; 1303 | LIBSSH2_LISTENER *listener = data->listener; 1304 | 1305 | libssh2_channel_forward_cancel(listener); 1306 | zend_list_delete(data->session_rsrc); 1307 | efree(data); 1308 | } 1309 | 1310 | static void php_ssh2_pkey_subsys_dtor(zend_resource *rsrc) 1311 | { 1312 | php_ssh2_pkey_subsys_data *data = (php_ssh2_pkey_subsys_data*)rsrc->ptr; 1313 | LIBSSH2_PUBLICKEY *pkey = data->pkey; 1314 | 1315 | libssh2_publickey_shutdown(pkey); 1316 | zend_list_delete(data->session_rsrc); 1317 | efree(data); 1318 | } 1319 | 1320 | /* {{{ PHP_MINIT_FUNCTION 1321 | */ 1322 | PHP_MINIT_FUNCTION(ssh2) 1323 | { 1324 | le_ssh2_session = zend_register_list_destructors_ex(php_ssh2_session_dtor, NULL, PHP_SSH2_SESSION_RES_NAME, module_number); 1325 | le_ssh2_listener = zend_register_list_destructors_ex(php_ssh2_listener_dtor, NULL, PHP_SSH2_LISTENER_RES_NAME, module_number); 1326 | le_ssh2_sftp = zend_register_list_destructors_ex(php_ssh2_sftp_dtor, NULL, PHP_SSH2_SFTP_RES_NAME, module_number); 1327 | le_ssh2_pkey_subsys = zend_register_list_destructors_ex(php_ssh2_pkey_subsys_dtor, NULL, PHP_SSH2_PKEY_SUBSYS_RES_NAME, module_number); 1328 | 1329 | REGISTER_LONG_CONSTANT("SSH2_FINGERPRINT_MD5", PHP_SSH2_FINGERPRINT_MD5, CONST_CS | CONST_PERSISTENT); 1330 | REGISTER_LONG_CONSTANT("SSH2_FINGERPRINT_SHA1", PHP_SSH2_FINGERPRINT_SHA1, CONST_CS | CONST_PERSISTENT); 1331 | REGISTER_LONG_CONSTANT("SSH2_FINGERPRINT_HEX", PHP_SSH2_FINGERPRINT_HEX, CONST_CS | CONST_PERSISTENT); 1332 | REGISTER_LONG_CONSTANT("SSH2_FINGERPRINT_RAW", PHP_SSH2_FINGERPRINT_RAW, CONST_CS | CONST_PERSISTENT); 1333 | 1334 | REGISTER_LONG_CONSTANT("SSH2_TERM_UNIT_CHARS", PHP_SSH2_TERM_UNIT_CHARS, CONST_CS | CONST_PERSISTENT); 1335 | REGISTER_LONG_CONSTANT("SSH2_TERM_UNIT_PIXELS", PHP_SSH2_TERM_UNIT_PIXELS, CONST_CS | CONST_PERSISTENT); 1336 | 1337 | REGISTER_STRING_CONSTANT("SSH2_DEFAULT_TERMINAL", PHP_SSH2_DEFAULT_TERMINAL, CONST_CS | CONST_PERSISTENT); 1338 | REGISTER_LONG_CONSTANT("SSH2_DEFAULT_TERM_WIDTH", PHP_SSH2_DEFAULT_TERM_WIDTH, CONST_CS | CONST_PERSISTENT); 1339 | REGISTER_LONG_CONSTANT("SSH2_DEFAULT_TERM_HEIGHT", PHP_SSH2_DEFAULT_TERM_HEIGHT, CONST_CS | CONST_PERSISTENT); 1340 | REGISTER_LONG_CONSTANT("SSH2_DEFAULT_TERM_UNIT", PHP_SSH2_DEFAULT_TERM_UNIT, CONST_CS | CONST_PERSISTENT); 1341 | 1342 | REGISTER_LONG_CONSTANT("SSH2_STREAM_STDIO", 0, CONST_CS | CONST_PERSISTENT); 1343 | REGISTER_LONG_CONSTANT("SSH2_STREAM_STDERR", SSH_EXTENDED_DATA_STDERR, CONST_CS | CONST_PERSISTENT); 1344 | 1345 | /* events/revents */ 1346 | REGISTER_LONG_CONSTANT("SSH2_POLLIN", LIBSSH2_POLLFD_POLLIN, CONST_CS | CONST_PERSISTENT); 1347 | REGISTER_LONG_CONSTANT("SSH2_POLLEXT", LIBSSH2_POLLFD_POLLEXT, CONST_CS | CONST_PERSISTENT); 1348 | REGISTER_LONG_CONSTANT("SSH2_POLLOUT", LIBSSH2_POLLFD_POLLOUT, CONST_CS | CONST_PERSISTENT); 1349 | 1350 | /* revents only */ 1351 | REGISTER_LONG_CONSTANT("SSH2_POLLERR", LIBSSH2_POLLFD_POLLERR, CONST_CS | CONST_PERSISTENT); 1352 | REGISTER_LONG_CONSTANT("SSH2_POLLHUP", LIBSSH2_POLLFD_POLLHUP, CONST_CS | CONST_PERSISTENT); 1353 | REGISTER_LONG_CONSTANT("SSH2_POLLNVAL", LIBSSH2_POLLFD_POLLNVAL, CONST_CS | CONST_PERSISTENT); 1354 | REGISTER_LONG_CONSTANT("SSH2_POLL_SESSION_CLOSED", LIBSSH2_POLLFD_SESSION_CLOSED, CONST_CS | CONST_PERSISTENT); 1355 | REGISTER_LONG_CONSTANT("SSH2_POLL_CHANNEL_CLOSED", LIBSSH2_POLLFD_CHANNEL_CLOSED, CONST_CS | CONST_PERSISTENT); 1356 | REGISTER_LONG_CONSTANT("SSH2_POLL_LISTENER_CLOSED", LIBSSH2_POLLFD_LISTENER_CLOSED, CONST_CS | CONST_PERSISTENT); 1357 | 1358 | return (php_register_url_stream_wrapper("ssh2.shell", &php_ssh2_stream_wrapper_shell) == SUCCESS && 1359 | php_register_url_stream_wrapper("ssh2.exec", &php_ssh2_stream_wrapper_exec) == SUCCESS && 1360 | php_register_url_stream_wrapper("ssh2.tunnel", &php_ssh2_stream_wrapper_tunnel) == SUCCESS && 1361 | php_register_url_stream_wrapper("ssh2.scp", &php_ssh2_stream_wrapper_scp) == SUCCESS && 1362 | php_register_url_stream_wrapper("ssh2.sftp", &php_ssh2_sftp_wrapper) == SUCCESS) ? SUCCESS : FAILURE; 1363 | } 1364 | /* }}} */ 1365 | 1366 | /* {{{ PHP_MSHUTDOWN_FUNCTION 1367 | */ 1368 | PHP_MSHUTDOWN_FUNCTION(ssh2) 1369 | { 1370 | return (php_unregister_url_stream_wrapper("ssh2.shell") == SUCCESS && 1371 | php_unregister_url_stream_wrapper("ssh2.exec") == SUCCESS && 1372 | php_unregister_url_stream_wrapper("ssh2.tunnel") == SUCCESS && 1373 | php_unregister_url_stream_wrapper("ssh2.scp") == SUCCESS && 1374 | php_unregister_url_stream_wrapper("ssh2.sftp") == SUCCESS) ? SUCCESS : FAILURE; 1375 | } 1376 | /* }}} */ 1377 | 1378 | /* {{{ PHP_MINFO_FUNCTION 1379 | */ 1380 | PHP_MINFO_FUNCTION(ssh2) 1381 | { 1382 | php_info_print_table_start(); 1383 | php_info_print_table_header(2, "SSH2 support", "enabled"); 1384 | php_info_print_table_row(2, "extension version", PHP_SSH2_VERSION); 1385 | php_info_print_table_row(2, "libssh2 version", LIBSSH2_VERSION); 1386 | php_info_print_table_row(2, "banner", LIBSSH2_SSH_BANNER); 1387 | php_info_print_table_end(); 1388 | } 1389 | /* }}} */ 1390 | 1391 | /* {{{ ZEND_BEGIN_ARG_INFO 1392 | */ 1393 | ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_connect, 0, 0, 1) 1394 | ZEND_ARG_INFO(0, host) 1395 | ZEND_ARG_INFO(0, port) 1396 | ZEND_ARG_INFO(0, methods) 1397 | ZEND_ARG_INFO(0, callbacks) 1398 | ZEND_END_ARG_INFO() 1399 | 1400 | ZEND_BEGIN_ARG_INFO(arginfo_ssh2_disconnect, 1) 1401 | ZEND_ARG_INFO(0, session) 1402 | ZEND_END_ARG_INFO() 1403 | 1404 | ZEND_BEGIN_ARG_INFO(arginfo_ssh2_methods_negotiated, 1) 1405 | ZEND_ARG_INFO(0, session) 1406 | ZEND_END_ARG_INFO() 1407 | 1408 | ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_fingerprint, 0, 0, 1) 1409 | ZEND_ARG_INFO(0, session) 1410 | ZEND_ARG_INFO(0, flags) 1411 | ZEND_END_ARG_INFO() 1412 | 1413 | ZEND_BEGIN_ARG_INFO(arginfo_ssh2_auth_none, 2) 1414 | ZEND_ARG_INFO(0, session) 1415 | ZEND_ARG_INFO(0, username) 1416 | ZEND_END_ARG_INFO() 1417 | 1418 | ZEND_BEGIN_ARG_INFO(arginfo_ssh2_auth_password, 3) 1419 | ZEND_ARG_INFO(0, session) 1420 | ZEND_ARG_INFO(0, username) 1421 | ZEND_ARG_INFO(0, password) 1422 | ZEND_END_ARG_INFO() 1423 | 1424 | ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_auth_pubkey_file, 0, 0, 4) 1425 | ZEND_ARG_INFO(0, session) 1426 | ZEND_ARG_INFO(0, username) 1427 | ZEND_ARG_INFO(0, pubkeyfile) 1428 | ZEND_ARG_INFO(0, privkeyfile) 1429 | ZEND_ARG_INFO(0, passphrase) 1430 | ZEND_END_ARG_INFO() 1431 | 1432 | ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_auth_pubkey, 0, 0, 4) 1433 | ZEND_ARG_INFO(0, session) 1434 | ZEND_ARG_INFO(0, username) 1435 | ZEND_ARG_INFO(0, pubkey) 1436 | ZEND_ARG_INFO(0, privkey) 1437 | ZEND_ARG_INFO(0, passphrase) 1438 | ZEND_END_ARG_INFO() 1439 | 1440 | ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_auth_hostbased_file, 0, 0, 5) 1441 | ZEND_ARG_INFO(0, session) 1442 | ZEND_ARG_INFO(0, username) 1443 | ZEND_ARG_INFO(0, hostname) 1444 | ZEND_ARG_INFO(0, pubkeyfile) 1445 | ZEND_ARG_INFO(0, privkeyfile) 1446 | ZEND_ARG_INFO(0, passphrase) 1447 | ZEND_ARG_INFO(0, local_username) 1448 | ZEND_END_ARG_INFO() 1449 | 1450 | ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_forward_listen, 0, 0, 2) 1451 | ZEND_ARG_INFO(0, session) 1452 | ZEND_ARG_INFO(0, port) 1453 | ZEND_ARG_INFO(0, host) 1454 | ZEND_ARG_INFO(0, max_connections) 1455 | ZEND_END_ARG_INFO() 1456 | 1457 | ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_forward_accept, 0, 0, 1) 1458 | ZEND_ARG_INFO(0, listener) 1459 | ZEND_ARG_INFO(1, host) 1460 | ZEND_ARG_INFO(0, port) 1461 | ZEND_END_ARG_INFO() 1462 | 1463 | ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_shell, 0, 0, 1) 1464 | ZEND_ARG_INFO(0, session) 1465 | ZEND_ARG_INFO(0, termtype) 1466 | ZEND_ARG_INFO(0, env) 1467 | ZEND_ARG_INFO(0, width) 1468 | ZEND_ARG_INFO(0, height) 1469 | ZEND_ARG_INFO(0, width_height_type) 1470 | ZEND_END_ARG_INFO() 1471 | 1472 | ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_shell_resize, 0, 0, 3) 1473 | ZEND_ARG_INFO(0, session) 1474 | ZEND_ARG_INFO(0, width) 1475 | ZEND_ARG_INFO(0, height) 1476 | ZEND_END_ARG_INFO() 1477 | 1478 | ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_exec, 0, 0, 2) 1479 | ZEND_ARG_INFO(0, session) 1480 | ZEND_ARG_INFO(0, command) 1481 | ZEND_ARG_INFO(0, pty) 1482 | ZEND_ARG_INFO(0, env) 1483 | ZEND_ARG_INFO(0, width) 1484 | ZEND_ARG_INFO(0, height) 1485 | ZEND_ARG_INFO(0, width_height_type) 1486 | ZEND_END_ARG_INFO() 1487 | 1488 | ZEND_BEGIN_ARG_INFO(arginfo_ssh2_tunnel, 3) 1489 | ZEND_ARG_INFO(0, session) 1490 | ZEND_ARG_INFO(0, host) 1491 | ZEND_ARG_INFO(0, port) 1492 | ZEND_END_ARG_INFO() 1493 | 1494 | ZEND_BEGIN_ARG_INFO(arginfo_ssh2_scp_recv, 3) 1495 | ZEND_ARG_INFO(0, session) 1496 | ZEND_ARG_INFO(0, remote_file) 1497 | ZEND_ARG_INFO(0, local_file) 1498 | ZEND_END_ARG_INFO() 1499 | 1500 | ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_scp_send, 0, 0, 3) 1501 | ZEND_ARG_INFO(0, session) 1502 | ZEND_ARG_INFO(0, local_file) 1503 | ZEND_ARG_INFO(0, remote_file) 1504 | ZEND_ARG_INFO(0, create_mode) 1505 | ZEND_END_ARG_INFO() 1506 | 1507 | ZEND_BEGIN_ARG_INFO(arginfo_ssh2_fetch_stream, 2) 1508 | ZEND_ARG_INFO(0, channel) 1509 | ZEND_ARG_INFO(0, streamid) 1510 | ZEND_END_ARG_INFO() 1511 | 1512 | ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_poll, 0, 0, 1) 1513 | ZEND_ARG_INFO(1, polldes) 1514 | ZEND_ARG_INFO(0, timeout) 1515 | ZEND_END_ARG_INFO() 1516 | 1517 | ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_send_eof, 0, 0, 1) 1518 | ZEND_ARG_INFO(0, channel) 1519 | ZEND_END_ARG_INFO() 1520 | 1521 | ZEND_BEGIN_ARG_INFO(arginfo_ssh2_sftp, 1) 1522 | ZEND_ARG_INFO(0, session) 1523 | ZEND_END_ARG_INFO() 1524 | 1525 | ZEND_BEGIN_ARG_INFO(arginfo_ssh2_sftp_rename, 3) 1526 | ZEND_ARG_INFO(0, sftp) 1527 | ZEND_ARG_INFO(0, from) 1528 | ZEND_ARG_INFO(0, to) 1529 | ZEND_END_ARG_INFO() 1530 | 1531 | ZEND_BEGIN_ARG_INFO(arginfo_ssh2_sftp_unlink, 2) 1532 | ZEND_ARG_INFO(0, sftp) 1533 | ZEND_ARG_INFO(0, filename) 1534 | ZEND_END_ARG_INFO() 1535 | 1536 | ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_sftp_mkdir, 0, 0, 2) 1537 | ZEND_ARG_INFO(0, sftp) 1538 | ZEND_ARG_INFO(0, dirname) 1539 | ZEND_ARG_INFO(0, mode) 1540 | ZEND_ARG_INFO(0, recursive) 1541 | ZEND_END_ARG_INFO() 1542 | 1543 | ZEND_BEGIN_ARG_INFO(arginfo_ssh2_sftp_rmdir, 2) 1544 | ZEND_ARG_INFO(0, sftp) 1545 | ZEND_ARG_INFO(0, dirname) 1546 | ZEND_END_ARG_INFO() 1547 | 1548 | ZEND_BEGIN_ARG_INFO(arginfo_ssh2_sftp_chmod, 3) 1549 | ZEND_ARG_INFO(0, sftp) 1550 | ZEND_ARG_INFO(0, filename) 1551 | ZEND_ARG_INFO(0, mode) 1552 | ZEND_END_ARG_INFO() 1553 | 1554 | ZEND_BEGIN_ARG_INFO(arginfo_ssh2_sftp_stat, 2) 1555 | ZEND_ARG_INFO(0, sftp) 1556 | ZEND_ARG_INFO(0, path) 1557 | ZEND_END_ARG_INFO() 1558 | 1559 | ZEND_BEGIN_ARG_INFO(arginfo_ssh2_sftp_lstat, 2) 1560 | ZEND_ARG_INFO(0, sftp) 1561 | ZEND_ARG_INFO(0, path) 1562 | ZEND_END_ARG_INFO() 1563 | 1564 | ZEND_BEGIN_ARG_INFO(arginfo_ssh2_sftp_symlink, 3) 1565 | ZEND_ARG_INFO(0, sftp) 1566 | ZEND_ARG_INFO(0, target) 1567 | ZEND_ARG_INFO(0, link) 1568 | ZEND_END_ARG_INFO() 1569 | 1570 | ZEND_BEGIN_ARG_INFO(arginfo_ssh2_sftp_readlink, 2) 1571 | ZEND_ARG_INFO(0, sftp) 1572 | ZEND_ARG_INFO(0, link) 1573 | ZEND_END_ARG_INFO() 1574 | 1575 | ZEND_BEGIN_ARG_INFO(arginfo_ssh2_sftp_realpath, 2) 1576 | ZEND_ARG_INFO(0, sftp) 1577 | ZEND_ARG_INFO(0, filename) 1578 | ZEND_END_ARG_INFO() 1579 | 1580 | ZEND_BEGIN_ARG_INFO(arginfo_ssh2_publickey_init, 1) 1581 | ZEND_ARG_INFO(0, session) 1582 | ZEND_END_ARG_INFO() 1583 | 1584 | ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_publickey_add, 0, 0, 3) 1585 | ZEND_ARG_INFO(0, pkey) 1586 | ZEND_ARG_INFO(0, algoname) 1587 | ZEND_ARG_INFO(0, blob) 1588 | ZEND_ARG_INFO(0, overwrite) 1589 | ZEND_ARG_INFO(0, attributes) 1590 | ZEND_END_ARG_INFO() 1591 | 1592 | ZEND_BEGIN_ARG_INFO(arginfo_ssh2_publickey_remove, 3) 1593 | ZEND_ARG_INFO(0, pkey) 1594 | ZEND_ARG_INFO(0, algoname) 1595 | ZEND_ARG_INFO(0, blob) 1596 | ZEND_END_ARG_INFO() 1597 | 1598 | ZEND_BEGIN_ARG_INFO(arginfo_ssh2_publickey_list, 1) 1599 | ZEND_ARG_INFO(0, pkey) 1600 | ZEND_END_ARG_INFO() 1601 | 1602 | ZEND_BEGIN_ARG_INFO(arginfo_ssh2_auth_agent, 2) 1603 | ZEND_ARG_INFO(0, session) 1604 | ZEND_ARG_INFO(0, username) 1605 | ZEND_END_ARG_INFO() 1606 | /* }}} */ 1607 | 1608 | /* {{{ ssh2_functions[] 1609 | */ 1610 | zend_function_entry ssh2_functions[] = { 1611 | PHP_FE(ssh2_connect, arginfo_ssh2_connect) 1612 | PHP_FE(ssh2_disconnect, arginfo_ssh2_disconnect) 1613 | PHP_FE(ssh2_methods_negotiated, arginfo_ssh2_methods_negotiated) 1614 | PHP_FE(ssh2_fingerprint, arginfo_ssh2_fingerprint) 1615 | 1616 | PHP_FE(ssh2_auth_none, arginfo_ssh2_auth_none) 1617 | PHP_FE(ssh2_auth_password, arginfo_ssh2_auth_password) 1618 | PHP_FE(ssh2_auth_pubkey_file, arginfo_ssh2_auth_pubkey_file) 1619 | PHP_FE(ssh2_auth_pubkey, arginfo_ssh2_auth_pubkey) 1620 | PHP_FE(ssh2_auth_hostbased_file, arginfo_ssh2_auth_hostbased_file) 1621 | 1622 | PHP_FE(ssh2_forward_listen, arginfo_ssh2_forward_listen) 1623 | PHP_FE(ssh2_forward_accept, arginfo_ssh2_forward_accept) 1624 | 1625 | /* Stream Stuff */ 1626 | PHP_FE(ssh2_shell, arginfo_ssh2_shell) 1627 | PHP_FE(ssh2_exec, arginfo_ssh2_exec) 1628 | PHP_FE(ssh2_tunnel, arginfo_ssh2_tunnel) 1629 | PHP_FE(ssh2_scp_recv, arginfo_ssh2_scp_recv) 1630 | PHP_FE(ssh2_scp_send, arginfo_ssh2_scp_send) 1631 | PHP_FE(ssh2_fetch_stream, arginfo_ssh2_fetch_stream) 1632 | PHP_FE(ssh2_poll, arginfo_ssh2_poll) 1633 | PHP_FE(ssh2_send_eof, arginfo_ssh2_send_eof) 1634 | PHP_FE(ssh2_shell_resize, arginfo_ssh2_shell_resize) 1635 | 1636 | /* SFTP Stuff */ 1637 | PHP_FE(ssh2_sftp, arginfo_ssh2_sftp) 1638 | 1639 | /* SFTP Wrapper Ops */ 1640 | PHP_FE(ssh2_sftp_rename, arginfo_ssh2_sftp_rename) 1641 | PHP_FE(ssh2_sftp_unlink, arginfo_ssh2_sftp_unlink) 1642 | PHP_FE(ssh2_sftp_mkdir, arginfo_ssh2_sftp_mkdir) 1643 | PHP_FE(ssh2_sftp_rmdir, arginfo_ssh2_sftp_rmdir) 1644 | PHP_FE(ssh2_sftp_chmod, arginfo_ssh2_sftp_chmod) 1645 | PHP_FE(ssh2_sftp_stat, arginfo_ssh2_sftp_stat) 1646 | PHP_FE(ssh2_sftp_lstat, arginfo_ssh2_sftp_lstat) 1647 | PHP_FE(ssh2_sftp_symlink, arginfo_ssh2_sftp_symlink) 1648 | PHP_FE(ssh2_sftp_readlink, arginfo_ssh2_sftp_readlink) 1649 | PHP_FE(ssh2_sftp_realpath, arginfo_ssh2_sftp_realpath) 1650 | 1651 | /* Publickey subsystem */ 1652 | PHP_FE(ssh2_publickey_init, arginfo_ssh2_publickey_init) 1653 | PHP_FE(ssh2_publickey_add, arginfo_ssh2_publickey_add) 1654 | PHP_FE(ssh2_publickey_remove, arginfo_ssh2_publickey_remove) 1655 | PHP_FE(ssh2_publickey_list, arginfo_ssh2_publickey_list) 1656 | 1657 | PHP_FE(ssh2_auth_agent, arginfo_ssh2_auth_agent) 1658 | 1659 | PHP_FE_END 1660 | }; 1661 | /* }}} */ 1662 | 1663 | /* {{{ ssh2_module_entry 1664 | */ 1665 | zend_module_entry ssh2_module_entry = { 1666 | STANDARD_MODULE_HEADER, 1667 | "ssh2", 1668 | ssh2_functions, 1669 | PHP_MINIT(ssh2), 1670 | PHP_MSHUTDOWN(ssh2), 1671 | NULL, /* RINIT */ 1672 | NULL, /* RSHUTDOWN */ 1673 | PHP_MINFO(ssh2), 1674 | PHP_SSH2_VERSION, 1675 | STANDARD_MODULE_PROPERTIES 1676 | }; 1677 | /* }}} */ 1678 | 1679 | #ifdef COMPILE_DL_SSH2 1680 | ZEND_GET_MODULE(ssh2) 1681 | #endif 1682 | 1683 | /* 1684 | * Local variables: 1685 | * tab-width: 4 1686 | * c-basic-offset: 4 1687 | * End: 1688 | * vim600: noet sw=4 ts=4 fdm=marker 1689 | * vim<600: noet sw=4 ts=4 1690 | */ 1691 | --------------------------------------------------------------------------------