├── 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 |
--------------------------------------------------------------------------------