[
  {
    "path": ".github/workflows/linux.yml",
    "content": "name: linux\n\non:\n  push:\n    branches:\n    - master\n  pull_request:\n    branches:\n    - master\n\njobs:\n  build:\n    strategy:\n      fail-fast: false\n      matrix:\n        os: [ubuntu-22.04, ubuntu-24.04]\n        rust_toolchain: [stable, beta]\n    runs-on: ${{ matrix.os }}\n    steps:\n    - uses: actions/checkout@v1\n    - name: Install Rust\n      uses: dtolnay/rust-toolchain@master\n      with:\n        toolchain: ${{ matrix.rust_toolchain }}\n    - name: Build and test\n      run: |\n        rustc -V\n        cargo -V\n        cargo build\n        tests/run_integration_tests.sh\n        rustdoc --test README.md -L target\n        cargo run --manifest-path systest/Cargo.toml\n"
  },
  {
    "path": ".github/workflows/macos.yml",
    "content": "name: macOS\n\non:\n  push:\n    branches:\n    - master\n  pull_request:\n    branches:\n    - master\n\njobs:\n  build:\n    strategy:\n      fail-fast: false\n      matrix:\n        os: [macos-14, macos-15, macos-26]\n    runs-on: ${{ matrix.os }}\n    steps:\n    - uses: actions/checkout@v1\n    - name: Install Rust\n      uses: dtolnay/rust-toolchain@stable\n    - name: Build and test\n      run: |\n        rustc -V\n        cargo -V\n        cargo build\n        tests/run_integration_tests.sh\n        rustdoc --test README.md -L target\n        cargo run --manifest-path systest/Cargo.toml\n"
  },
  {
    "path": ".github/workflows/windows.yml",
    "content": "name: Windows\n\non:\n  push:\n    branches:\n    - master\n  pull_request:\n    branches:\n    - master\n\njobs:\n  build:\n    strategy:\n      fail-fast: false\n      matrix:\n        os: [windows-2022, windows-2025]\n        env:\n        - TARGET: x86_64-pc-windows-msvc\n        - TARGET: i686-pc-windows-msvc\n    runs-on: ${{ matrix.os }}\n    steps:\n    - uses: actions/checkout@v1\n    - name: Install Rust\n      uses: dtolnay/rust-toolchain@stable\n      with:\n        target: ${{ matrix.env.TARGET }}\n    - name: Build and test\n      env:\n        TARGET: ${{ matrix.env.TARGET }}\n      run: |\n        rustc -V\n        cargo -V\n        cargo test --no-run --target %TARGET%\n        cargo run --manifest-path systest/Cargo.toml --target %TARGET%\n        cargo test --no-run --target %TARGET% --features openssl-on-win32,vendored-openssl\n      shell: cmd\n"
  },
  {
    "path": ".gitignore",
    "content": ".*.sw*\n/target/\nlibssh2-sys/target/\n/Cargo.lock\n/tests/sshd\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"libssh2-sys/libssh2\"]\n\tpath = libssh2-sys/libssh2\n\turl = https://github.com/libssh2/libssh2\n\tbranch = master\n"
  },
  {
    "path": ".mailmap",
    "content": "<ga29smith@gmail.com> <gabriel.smith@precisionot.com>\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: rust\nsudo: required\n\nmatrix:\n  include:\n    - rust: 1.36.0\n    - rust: stable\n    - rust: beta\n    - rust: nightly\n\n#    - name: \"master doc to gh-pages\"\n#     rust: nightly\n#     script:\n#       - cargo doc --no-deps\n#       - cargo doc --no-deps --manifest-path libssh2-sys/Cargo.toml\n#     deploy:\n#       provider: script\n#       script: curl -LsSf https://git.io/fhJ8n | rustc - && (cd target/doc && ../../rust_out)\n#       skip_cleanup: true\n#       on:\n#         branch: master\n\nscript:\n  - cargo build\n  - tests/run_integration_tests.sh\n  - rustdoc --test README.md -L target\n  - cargo run --manifest-path systest/Cargo.toml\n\nnotifications:\n  email:\n    on_success: never\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[package]\nname = \"ssh2\"\nversion = \"0.9.5\"\nauthors = [\"Alex Crichton <alex@alexcrichton.com>\", \"Wez Furlong <wez@wezfurlong.org>\", \"Matteo Bigoi <bigo@crisidev.org>\"]\nlicense = \"MIT OR Apache-2.0\"\nkeywords = [\"ssh\"]\nreadme = \"README.md\"\nrepository = \"https://github.com/alexcrichton/ssh2-rs\"\nhomepage = \"https://github.com/alexcrichton/ssh2-rs\"\ndocumentation = \"https://docs.rs/ssh2\"\ndescription = \"\"\"\nBindings to libssh2 for interacting with SSH servers and executing remote\ncommands, forwarding local ports, etc.\n\"\"\"\n\n[features]\nvendored-openssl = [\"libssh2-sys/vendored-openssl\"]\nopenssl-on-win32 = [\"libssh2-sys/openssl-on-win32\"]\n\n[dependencies]\nbitflags = \"2\"\nlibc = \"0.2\"\nlibssh2-sys = { path = \"libssh2-sys\", version = \"0.3.1\" }\nparking_lot = \"0.12\"\n\n[dev-dependencies]\ntempfile = \"3\"\n\n[workspace]\nmembers = ['systest']\n"
  },
  {
    "path": "LICENSE-APACHE",
    "content": "                              Apache License\n                        Version 2.0, January 2004\n                     http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n   \"License\" shall mean the terms and conditions for use, reproduction,\n   and distribution as defined by Sections 1 through 9 of this document.\n\n   \"Licensor\" shall mean the copyright owner or entity authorized by\n   the copyright owner that is granting the License.\n\n   \"Legal Entity\" shall mean the union of the acting entity and all\n   other entities that control, are controlled by, or are under common\n   control with that entity. For the purposes of this definition,\n   \"control\" means (i) the power, direct or indirect, to cause the\n   direction or management of such entity, whether by contract or\n   otherwise, or (ii) ownership of fifty percent (50%) or more of the\n   outstanding shares, or (iii) beneficial ownership of such entity.\n\n   \"You\" (or \"Your\") shall mean an individual or Legal Entity\n   exercising permissions granted by this License.\n\n   \"Source\" form shall mean the preferred form for making modifications,\n   including but not limited to software source code, documentation\n   source, and configuration files.\n\n   \"Object\" form shall mean any form resulting from mechanical\n   transformation or translation of a Source form, including but\n   not limited to compiled object code, generated documentation,\n   and conversions to other media types.\n\n   \"Work\" shall mean the work of authorship, whether in Source or\n   Object form, made available under the License, as indicated by a\n   copyright notice that is included in or attached to the work\n   (an example is provided in the Appendix below).\n\n   \"Derivative Works\" shall mean any work, whether in Source or Object\n   form, that is based on (or derived from) the Work and for which the\n   editorial revisions, annotations, elaborations, or other modifications\n   represent, as a whole, an original work of authorship. For the purposes\n   of this License, Derivative Works shall not include works that remain\n   separable from, or merely link (or bind by name) to the interfaces of,\n   the Work and Derivative Works thereof.\n\n   \"Contribution\" shall mean any work of authorship, including\n   the original version of the Work and any modifications or additions\n   to that Work or Derivative Works thereof, that is intentionally\n   submitted to Licensor for inclusion in the Work by the copyright owner\n   or by an individual or Legal Entity authorized to submit on behalf of\n   the copyright owner. For the purposes of this definition, \"submitted\"\n   means any form of electronic, verbal, or written communication sent\n   to the Licensor or its representatives, including but not limited to\n   communication on electronic mailing lists, source code control systems,\n   and issue tracking systems that are managed by, or on behalf of, the\n   Licensor for the purpose of discussing and improving the Work, but\n   excluding communication that is conspicuously marked or otherwise\n   designated in writing by the copyright owner as \"Not a Contribution.\"\n\n   \"Contributor\" shall mean Licensor and any individual or Legal Entity\n   on behalf of whom a Contribution has been received by Licensor and\n   subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   copyright license to reproduce, prepare Derivative Works of,\n   publicly display, publicly perform, sublicense, and distribute the\n   Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   (except as stated in this section) patent license to make, have made,\n   use, offer to sell, sell, import, and otherwise transfer the Work,\n   where such license applies only to those patent claims licensable\n   by such Contributor that are necessarily infringed by their\n   Contribution(s) alone or by combination of their Contribution(s)\n   with the Work to which such Contribution(s) was submitted. If You\n   institute patent litigation against any entity (including a\n   cross-claim or counterclaim in a lawsuit) alleging that the Work\n   or a Contribution incorporated within the Work constitutes direct\n   or contributory patent infringement, then any patent licenses\n   granted to You under this License for that Work shall terminate\n   as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the\n   Work or Derivative Works thereof in any medium, with or without\n   modifications, and in Source or Object form, provided that You\n   meet the following conditions:\n\n   (a) You must give any other recipients of the Work or\n       Derivative Works a copy of this License; and\n\n   (b) You must cause any modified files to carry prominent notices\n       stating that You changed the files; and\n\n   (c) You must retain, in the Source form of any Derivative Works\n       that You distribute, all copyright, patent, trademark, and\n       attribution notices from the Source form of the Work,\n       excluding those notices that do not pertain to any part of\n       the Derivative Works; and\n\n   (d) If the Work includes a \"NOTICE\" text file as part of its\n       distribution, then any Derivative Works that You distribute must\n       include a readable copy of the attribution notices contained\n       within such NOTICE file, excluding those notices that do not\n       pertain to any part of the Derivative Works, in at least one\n       of the following places: within a NOTICE text file distributed\n       as part of the Derivative Works; within the Source form or\n       documentation, if provided along with the Derivative Works; or,\n       within a display generated by the Derivative Works, if and\n       wherever such third-party notices normally appear. The contents\n       of the NOTICE file are for informational purposes only and\n       do not modify the License. You may add Your own attribution\n       notices within Derivative Works that You distribute, alongside\n       or as an addendum to the NOTICE text from the Work, provided\n       that such additional attribution notices cannot be construed\n       as modifying the License.\n\n   You may add Your own copyright statement to Your modifications and\n   may provide additional or different license terms and conditions\n   for use, reproduction, or distribution of Your modifications, or\n   for any such Derivative Works as a whole, provided Your use,\n   reproduction, and distribution of the Work otherwise complies with\n   the conditions stated in this License.\n\n5. Submission of Contributions. Unless You explicitly state otherwise,\n   any Contribution intentionally submitted for inclusion in the Work\n   by You to the Licensor shall be under the terms and conditions of\n   this License, without any additional terms or conditions.\n   Notwithstanding the above, nothing herein shall supersede or modify\n   the terms of any separate license agreement you may have executed\n   with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade\n   names, trademarks, service marks, or product names of the Licensor,\n   except as required for reasonable and customary use in describing the\n   origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or\n   agreed to in writing, Licensor provides the Work (and each\n   Contributor provides its Contributions) on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n   implied, including, without limitation, any warranties or conditions\n   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n   PARTICULAR PURPOSE. You are solely responsible for determining the\n   appropriateness of using or redistributing the Work and assume any\n   risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory,\n   whether in tort (including negligence), contract, or otherwise,\n   unless required by applicable law (such as deliberate and grossly\n   negligent acts) or agreed to in writing, shall any Contributor be\n   liable to You for damages, including any direct, indirect, special,\n   incidental, or consequential damages of any character arising as a\n   result of this License or out of the use or inability to use the\n   Work (including but not limited to damages for loss of goodwill,\n   work stoppage, computer failure or malfunction, or any and all\n   other commercial damages or losses), even if such Contributor\n   has been advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability. While redistributing\n   the Work or Derivative Works thereof, You may choose to offer,\n   and charge a fee for, acceptance of support, warranty, indemnity,\n   or other liability obligations and/or rights consistent with this\n   License. However, in accepting such obligations, You may act only\n   on Your own behalf and on Your sole responsibility, not on behalf\n   of any other Contributor, and only if You agree to indemnify,\n   defend, and hold each Contributor harmless for any liability\n   incurred by, or claims asserted against, such Contributor by reason\n   of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work.\n\n   To apply the Apache License to your work, attach the following\n   boilerplate notice, with the fields enclosed by brackets \"[]\"\n   replaced with your own identifying information. (Don't include\n   the brackets!)  The text should be enclosed in the appropriate\n   comment syntax for the file format. We also recommend that a\n   file or class name and description of purpose be included on the\n   same \"printed page\" as the copyright notice for easier\n   identification within third-party archives.\n\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n\thttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "LICENSE-MIT",
    "content": "Copyright (c) 2014 Alex Crichton\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# ssh2-rs\n\n[![Build Status](https://github.com/alexcrichton/ssh2-rs/workflows/linux/badge.svg)](https://github.com/alexcrichton/ssh2-rs/actions?workflow=linux)\n[![Build Status](https://github.com/alexcrichton/ssh2-rs/workflows/Windows/badge.svg)](https://github.com/alexcrichton/ssh2-rs/actions?workflow=Windows)\n[![Build Status](https://github.com/alexcrichton/ssh2-rs/workflows/macOS/badge.svg)](https://github.com/alexcrichton/ssh2-rs/actions?workflow=macOS)\n\n[Documentation](https://docs.rs/ssh2)\n\nRust bindings to libssh2, an ssh client library.\n\n## Usage\n\n```toml\n# Cargo.toml\n[dependencies]\nssh2 = \"0.9\"\n```\n\n## Building on OSX 10.10+\n\nThis library depends on OpenSSL. To get OpenSSL working follow the\n[`openssl` crate's instructions](https://github.com/sfackler/rust-openssl#macos).\n\nStarting with version `0.4` of `ssh2`, you can enable the `vendored-openssl` feature\nto have `libssh2` built against a statically built version of openssl as [described\nhere](https://docs.rs/openssl/0.10.24/openssl/#vendored)\n\n# License\n\nThis project is licensed under either of\n\n * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or\n   http://www.apache.org/licenses/LICENSE-2.0)\n * MIT license ([LICENSE-MIT](LICENSE-MIT) or\n   http://opensource.org/licenses/MIT)\n\nat your option.\n\n### Contribution\n\nUnless you explicitly state otherwise, any contribution intentionally submitted\nfor inclusion in this project by you, as defined in the Apache-2.0 license,\nshall be dual licensed as above, without any additional terms or conditions.\n"
  },
  {
    "path": "libssh2-sys/.gitattributes",
    "content": "# apparently libssh2 has trouble with CRLF, but of course only on windows\n* text eol=lf\n"
  },
  {
    "path": "libssh2-sys/Cargo.toml",
    "content": "[package]\nname = \"libssh2-sys\"\nversion = \"0.3.1\"\nauthors = [\"Alex Crichton <alex@alexcrichton.com>\", \"Wez Furlong <wez@wezfurlong.org>\", \"Matteo Bigoi <bigo@crisidev.org>\"]\nlinks = \"ssh2\"\nbuild = \"build.rs\"\nlicense = \"MIT OR Apache-2.0\"\nrepository = \"https://github.com/alexcrichton/ssh2-rs\"\ndocumentation = \"https://docs.rs/libssh2-sys\"\ndescription = \"Native bindings to the libssh2 library\"\n\n[lib]\nname = \"libssh2_sys\"\npath = \"lib.rs\"\n\n[features]\nvendored-openssl = [\"openssl-sys/vendored\"]\nzlib-ng-compat = [\"libz-sys/zlib-ng\"]\nopenssl-on-win32 = [\"openssl-sys\"]\n\n[dependencies]\nlibc = \"0.2\"\nlibz-sys = { version = \"1.1.0\", default-features = false, features = [\"libc\"] }\n\n[target.\"cfg(unix)\".dependencies]\nopenssl-sys = \"0.9.35\"\n\n[target.\"cfg(windows)\".dependencies]\nopenssl-sys = { version=\"0.9.35\", optional = true }\n\n[build-dependencies]\npkg-config = \"0.3.11\"\ncc = \"1.0.25\"\n\n[target.'cfg(target_env = \"msvc\")'.build-dependencies]\nvcpkg = \"0.2\"\n"
  },
  {
    "path": "libssh2-sys/LICENSE-APACHE",
    "content": "                              Apache License\n                        Version 2.0, January 2004\n                     http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n   \"License\" shall mean the terms and conditions for use, reproduction,\n   and distribution as defined by Sections 1 through 9 of this document.\n\n   \"Licensor\" shall mean the copyright owner or entity authorized by\n   the copyright owner that is granting the License.\n\n   \"Legal Entity\" shall mean the union of the acting entity and all\n   other entities that control, are controlled by, or are under common\n   control with that entity. For the purposes of this definition,\n   \"control\" means (i) the power, direct or indirect, to cause the\n   direction or management of such entity, whether by contract or\n   otherwise, or (ii) ownership of fifty percent (50%) or more of the\n   outstanding shares, or (iii) beneficial ownership of such entity.\n\n   \"You\" (or \"Your\") shall mean an individual or Legal Entity\n   exercising permissions granted by this License.\n\n   \"Source\" form shall mean the preferred form for making modifications,\n   including but not limited to software source code, documentation\n   source, and configuration files.\n\n   \"Object\" form shall mean any form resulting from mechanical\n   transformation or translation of a Source form, including but\n   not limited to compiled object code, generated documentation,\n   and conversions to other media types.\n\n   \"Work\" shall mean the work of authorship, whether in Source or\n   Object form, made available under the License, as indicated by a\n   copyright notice that is included in or attached to the work\n   (an example is provided in the Appendix below).\n\n   \"Derivative Works\" shall mean any work, whether in Source or Object\n   form, that is based on (or derived from) the Work and for which the\n   editorial revisions, annotations, elaborations, or other modifications\n   represent, as a whole, an original work of authorship. For the purposes\n   of this License, Derivative Works shall not include works that remain\n   separable from, or merely link (or bind by name) to the interfaces of,\n   the Work and Derivative Works thereof.\n\n   \"Contribution\" shall mean any work of authorship, including\n   the original version of the Work and any modifications or additions\n   to that Work or Derivative Works thereof, that is intentionally\n   submitted to Licensor for inclusion in the Work by the copyright owner\n   or by an individual or Legal Entity authorized to submit on behalf of\n   the copyright owner. For the purposes of this definition, \"submitted\"\n   means any form of electronic, verbal, or written communication sent\n   to the Licensor or its representatives, including but not limited to\n   communication on electronic mailing lists, source code control systems,\n   and issue tracking systems that are managed by, or on behalf of, the\n   Licensor for the purpose of discussing and improving the Work, but\n   excluding communication that is conspicuously marked or otherwise\n   designated in writing by the copyright owner as \"Not a Contribution.\"\n\n   \"Contributor\" shall mean Licensor and any individual or Legal Entity\n   on behalf of whom a Contribution has been received by Licensor and\n   subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   copyright license to reproduce, prepare Derivative Works of,\n   publicly display, publicly perform, sublicense, and distribute the\n   Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   (except as stated in this section) patent license to make, have made,\n   use, offer to sell, sell, import, and otherwise transfer the Work,\n   where such license applies only to those patent claims licensable\n   by such Contributor that are necessarily infringed by their\n   Contribution(s) alone or by combination of their Contribution(s)\n   with the Work to which such Contribution(s) was submitted. If You\n   institute patent litigation against any entity (including a\n   cross-claim or counterclaim in a lawsuit) alleging that the Work\n   or a Contribution incorporated within the Work constitutes direct\n   or contributory patent infringement, then any patent licenses\n   granted to You under this License for that Work shall terminate\n   as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the\n   Work or Derivative Works thereof in any medium, with or without\n   modifications, and in Source or Object form, provided that You\n   meet the following conditions:\n\n   (a) You must give any other recipients of the Work or\n       Derivative Works a copy of this License; and\n\n   (b) You must cause any modified files to carry prominent notices\n       stating that You changed the files; and\n\n   (c) You must retain, in the Source form of any Derivative Works\n       that You distribute, all copyright, patent, trademark, and\n       attribution notices from the Source form of the Work,\n       excluding those notices that do not pertain to any part of\n       the Derivative Works; and\n\n   (d) If the Work includes a \"NOTICE\" text file as part of its\n       distribution, then any Derivative Works that You distribute must\n       include a readable copy of the attribution notices contained\n       within such NOTICE file, excluding those notices that do not\n       pertain to any part of the Derivative Works, in at least one\n       of the following places: within a NOTICE text file distributed\n       as part of the Derivative Works; within the Source form or\n       documentation, if provided along with the Derivative Works; or,\n       within a display generated by the Derivative Works, if and\n       wherever such third-party notices normally appear. The contents\n       of the NOTICE file are for informational purposes only and\n       do not modify the License. You may add Your own attribution\n       notices within Derivative Works that You distribute, alongside\n       or as an addendum to the NOTICE text from the Work, provided\n       that such additional attribution notices cannot be construed\n       as modifying the License.\n\n   You may add Your own copyright statement to Your modifications and\n   may provide additional or different license terms and conditions\n   for use, reproduction, or distribution of Your modifications, or\n   for any such Derivative Works as a whole, provided Your use,\n   reproduction, and distribution of the Work otherwise complies with\n   the conditions stated in this License.\n\n5. Submission of Contributions. Unless You explicitly state otherwise,\n   any Contribution intentionally submitted for inclusion in the Work\n   by You to the Licensor shall be under the terms and conditions of\n   this License, without any additional terms or conditions.\n   Notwithstanding the above, nothing herein shall supersede or modify\n   the terms of any separate license agreement you may have executed\n   with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade\n   names, trademarks, service marks, or product names of the Licensor,\n   except as required for reasonable and customary use in describing the\n   origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or\n   agreed to in writing, Licensor provides the Work (and each\n   Contributor provides its Contributions) on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n   implied, including, without limitation, any warranties or conditions\n   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n   PARTICULAR PURPOSE. You are solely responsible for determining the\n   appropriateness of using or redistributing the Work and assume any\n   risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory,\n   whether in tort (including negligence), contract, or otherwise,\n   unless required by applicable law (such as deliberate and grossly\n   negligent acts) or agreed to in writing, shall any Contributor be\n   liable to You for damages, including any direct, indirect, special,\n   incidental, or consequential damages of any character arising as a\n   result of this License or out of the use or inability to use the\n   Work (including but not limited to damages for loss of goodwill,\n   work stoppage, computer failure or malfunction, or any and all\n   other commercial damages or losses), even if such Contributor\n   has been advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability. While redistributing\n   the Work or Derivative Works thereof, You may choose to offer,\n   and charge a fee for, acceptance of support, warranty, indemnity,\n   or other liability obligations and/or rights consistent with this\n   License. However, in accepting such obligations, You may act only\n   on Your own behalf and on Your sole responsibility, not on behalf\n   of any other Contributor, and only if You agree to indemnify,\n   defend, and hold each Contributor harmless for any liability\n   incurred by, or claims asserted against, such Contributor by reason\n   of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work.\n\n   To apply the Apache License to your work, attach the following\n   boilerplate notice, with the fields enclosed by brackets \"[]\"\n   replaced with your own identifying information. (Don't include\n   the brackets!)  The text should be enclosed in the appropriate\n   comment syntax for the file format. We also recommend that a\n   file or class name and description of purpose be included on the\n   same \"printed page\" as the copyright notice for easier\n   identification within third-party archives.\n\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n\thttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "libssh2-sys/LICENSE-MIT",
    "content": "Copyright (c) 2014 Alex Crichton\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "libssh2-sys/build.rs",
    "content": "extern crate cc;\nextern crate pkg_config;\n\n#[cfg(target_env = \"msvc\")]\nextern crate vcpkg;\n\nuse std::env;\nuse std::fs;\nuse std::path::{Path, PathBuf};\nuse std::process::Command;\n\nfn main() {\n    let zlib_ng_compat = env::var(\"CARGO_FEATURE_ZLIB_NG_COMPAT\").is_ok();\n\n    if !zlib_ng_compat && try_vcpkg() {\n        return;\n    }\n\n    // The system copy of libssh2 is not used by default because it\n    // can lead to having two copies of libssl loaded at once.\n    // See https://github.com/alexcrichton/ssh2-rs/pull/88\n    println!(\"cargo:rerun-if-env-changed=LIBSSH2_SYS_USE_PKG_CONFIG\");\n    if env::var(\"LIBSSH2_SYS_USE_PKG_CONFIG\").is_ok() {\n        if zlib_ng_compat {\n            panic!(\"LIBSSH2_SYS_USE_PKG_CONFIG set, but cannot use zlib-ng-compat with system libssh2\");\n        }\n        if let Ok(lib) = pkg_config::find_library(\"libssh2\") {\n            for path in &lib.include_paths {\n                println!(\"cargo:include={}\", path.display());\n            }\n            return;\n        }\n    }\n\n    if !Path::new(\"libssh2/.git\").exists() {\n        let _ = Command::new(\"git\")\n            .args(&[\"submodule\", \"update\", \"--init\"])\n            .status();\n    }\n\n    let target = env::var(\"TARGET\").unwrap();\n    let profile = env::var(\"PROFILE\").unwrap();\n    let dst = PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    let mut cfg = cc::Build::new();\n\n    let include = dst.join(\"include\");\n    println!(\"cargo:include={}\", include.display());\n    println!(\"cargo:root={}\", dst.display());\n    let build = dst.join(\"build\");\n    cfg.out_dir(&build);\n    fs::create_dir_all(&build).unwrap();\n    fs::create_dir_all(&include).unwrap();\n\n    fs::copy(\"libssh2/include/libssh2.h\", include.join(\"libssh2.h\")).unwrap();\n    fs::copy(\n        \"libssh2/include/libssh2_publickey.h\",\n        include.join(\"libssh2_publickey.h\"),\n    )\n    .unwrap();\n    fs::copy(\n        \"libssh2/include/libssh2_sftp.h\",\n        include.join(\"libssh2_sftp.h\"),\n    )\n    .unwrap();\n\n    cfg.file(\"libssh2/src/agent.c\")\n        .file(\"libssh2/src/bcrypt_pbkdf.c\")\n        .file(\"libssh2/src/blowfish.c\")\n        .file(\"libssh2/src/chacha.c\")\n        .file(\"libssh2/src/channel.c\")\n        .file(\"libssh2/src/cipher-chachapoly.c\")\n        .file(\"libssh2/src/comp.c\")\n        .file(\"libssh2/src/crypt.c\")\n        .file(\"libssh2/src/crypto.c\")\n        .file(\"libssh2/src/global.c\")\n        .file(\"libssh2/src/hostkey.c\")\n        .file(\"libssh2/src/keepalive.c\")\n        .file(\"libssh2/src/kex.c\")\n        .file(\"libssh2/src/knownhost.c\")\n        .file(\"libssh2/src/mac.c\")\n        .file(\"libssh2/src/misc.c\")\n        .file(\"libssh2/src/packet.c\")\n        .file(\"libssh2/src/pem.c\")\n        .file(\"libssh2/src/poly1305.c\")\n        .file(\"libssh2/src/publickey.c\")\n        .file(\"libssh2/src/scp.c\")\n        .file(\"libssh2/src/session.c\")\n        .file(\"libssh2/src/sftp.c\")\n        .file(\"libssh2/src/transport.c\")\n        .file(\"libssh2/src/userauth.c\")\n        .file(\"libssh2/src/userauth_kbd_packet.c\")\n        .include(&include)\n        .include(\"libssh2/src\");\n\n    cfg.define(\"HAVE_LONGLONG\", None);\n\n    if target.contains(\"windows\") {\n        cfg.include(\"libssh2/win32\");\n        cfg.define(\"LIBSSH2_WIN32\", None);\n        cfg.file(\"libssh2/src/agent_win.c\");\n\n        if env::var_os(\"CARGO_FEATURE_OPENSSL_ON_WIN32\").is_some() {\n            cfg.define(\"LIBSSH2_OPENSSL\", None);\n            cfg.define(\"HAVE_EVP_AES_128_CTR\", None);\n            let lib_prefix = if target.contains(\"windows-msvc\") {\n                \"lib\"\n            } else {\n                \"\"\n            };\n            println!(\"cargo:rustc-link-lib=static={lib_prefix}ssl\");\n            println!(\"cargo:rustc-link-lib=static={lib_prefix}crypto\");\n            println!(\"cargo:rustc-link-lib=advapi32\");\n        } else {\n            cfg.define(\"LIBSSH2_WINCNG\", None);\n        }\n    } else {\n        cfg.flag(\"-fvisibility=hidden\");\n        cfg.define(\"HAVE_SNPRINTF\", None);\n        cfg.define(\"HAVE_UNISTD_H\", None);\n        cfg.define(\"HAVE_INTTYPES_H\", None);\n        cfg.define(\"HAVE_STDLIB_H\", None);\n        cfg.define(\"HAVE_SYS_SELECT_H\", None);\n        cfg.define(\"HAVE_SYS_SOCKET_H\", None);\n        cfg.define(\"HAVE_SYS_IOCTL_H\", None);\n        cfg.define(\"HAVE_SYS_TIME_H\", None);\n        cfg.define(\"HAVE_SYS_UN_H\", None);\n        cfg.define(\"HAVE_O_NONBLOCK\", None);\n        cfg.define(\"LIBSSH2_OPENSSL\", None);\n        cfg.define(\"HAVE_LIBCRYPT32\", None);\n        cfg.define(\"HAVE_EVP_AES_128_CTR\", None);\n        cfg.define(\"HAVE_POLL\", None);\n        cfg.define(\"HAVE_GETTIMEOFDAY\", None);\n\n        // Create `libssh2_config.h`\n        let config = fs::read_to_string(\"libssh2/src/libssh2_config_cmake.h.in\").unwrap();\n        let config = config\n            .lines()\n            .filter(|l| !l.contains(\"#cmakedefine\"))\n            .collect::<Vec<_>>()\n            .join(\"\\n\");\n        fs::write(build.join(\"libssh2_config.h\"), &config).unwrap();\n        cfg.include(&build);\n    }\n\n    /* Enable newer diffie-hellman-group-exchange-sha1 syntax */\n    cfg.define(\"LIBSSH2_DH_GEX_NEW\", None);\n\n    cfg.define(\"LIBSSH2_HAVE_ZLIB\", None);\n\n    if profile.contains(\"debug\") {\n        cfg.define(\"LIBSSH2DEBUG\", None);\n    }\n\n    println!(\"cargo:rerun-if-env-changed=DEP_Z_INCLUDE\");\n    if let Some(path) = env::var_os(\"DEP_Z_INCLUDE\") {\n        cfg.include(path);\n    }\n\n    println!(\"cargo:rerun-if-env-changed=DEP_OPENSSL_INCLUDE\");\n    if let Some(path) = env::var_os(\"DEP_OPENSSL_INCLUDE\") {\n        if let Some(path) = env::split_paths(&path).next() {\n            if let Some(path) = path.to_str() {\n                if path.len() > 0 {\n                    cfg.include(path);\n                }\n            }\n        }\n    }\n\n    let libssh2h = fs::read_to_string(\"libssh2/include/libssh2.h\").unwrap();\n    let version_line = libssh2h\n        .lines()\n        .find(|l| l.contains(\"LIBSSH2_VERSION\"))\n        .unwrap();\n    let version = &version_line[version_line.find('\"').unwrap() + 1..version_line.len() - 1];\n\n    let pkgconfig = dst.join(\"lib/pkgconfig\");\n    fs::create_dir_all(&pkgconfig).unwrap();\n    fs::write(\n        pkgconfig.join(\"libssh2.pc\"),\n        fs::read_to_string(\"libssh2/libssh2.pc.in\")\n            .unwrap()\n            .replace(\"@prefix@\", dst.to_str().unwrap())\n            .replace(\"@exec_prefix@\", \"\")\n            .replace(\"@libdir@\", dst.join(\"lib\").to_str().unwrap())\n            .replace(\"@includedir@\", include.to_str().unwrap())\n            .replace(\"@LIBS@\", \"\")\n            .replace(\"@LIBSREQUIRED@\", \"\")\n            .replace(\"@LIBSSH2VER@\", version),\n    )\n    .unwrap();\n\n    cfg.warnings(false);\n    cfg.compile(\"ssh2\");\n\n    if target.contains(\"windows\") {\n        println!(\"cargo:rustc-link-lib=bcrypt\");\n        println!(\"cargo:rustc-link-lib=crypt32\");\n        println!(\"cargo:rustc-link-lib=user32\");\n        println!(\"cargo:rustc-link-lib=ntdll\");\n    }\n}\n\n#[cfg(not(target_env = \"msvc\"))]\nfn try_vcpkg() -> bool {\n    false\n}\n\n#[cfg(target_env = \"msvc\")]\nfn try_vcpkg() -> bool {\n    vcpkg::Config::new()\n        .emit_includes(true)\n        .probe(\"libssh2\")\n        .map(|_| {\n            // found libssh2 which depends on openssl and zlib\n            vcpkg::Config::new()\n                .lib_name(\"libssl\")\n                .lib_name(\"libcrypto\")\n                .probe(\"openssl\")\n                .or_else(|_| {\n                    // openssl 1.1 was not found, try openssl 1.0\n                    vcpkg::Config::new()\n                        .lib_name(\"libeay32\")\n                        .lib_name(\"ssleay32\")\n                        .probe(\"openssl\")\n                })\n                .expect(\n                    \"configured libssh2 from vcpkg but could not \\\n                     find openssl libraries that it depends on\",\n                );\n\n            vcpkg::Config::new()\n                .lib_names(\"zlib\", \"zlib1\")\n                .probe(\"zlib\")\n                .expect(\n                    \"configured libssh2 from vcpkg but could not \\\n                     find the zlib library that it depends on\",\n                );\n\n            println!(\"cargo:rustc-link-lib=crypt32\");\n            println!(\"cargo:rustc-link-lib=gdi32\");\n            println!(\"cargo:rustc-link-lib=user32\");\n        })\n        .is_ok()\n}\n"
  },
  {
    "path": "libssh2-sys/lib.rs",
    "content": "#![doc(html_root_url = \"http://alexcrichton.com/ssh2-rs\")]\n#![allow(bad_style)]\n#![allow(unused_extern_crates)]\n\nextern crate libc;\n\nextern crate libz_sys;\n#[cfg(unix)]\nextern crate openssl_sys;\n\nuse libc::ssize_t;\nuse libc::{c_char, c_int, c_long, c_uchar, c_uint, c_ulong, c_void, size_t};\n\npub const SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT: c_int = 1;\npub const SSH_DISCONNECT_PROTOCOL_ERROR: c_int = 2;\npub const SSH_DISCONNECT_KEY_EXCHANGE_FAILED: c_int = 3;\npub const SSH_DISCONNECT_RESERVED: c_int = 4;\npub const SSH_DISCONNECT_MAC_ERROR: c_int = 5;\npub const SSH_DISCONNECT_COMPRESSION_ERROR: c_int = 6;\npub const SSH_DISCONNECT_SERVICE_NOT_AVAILABLE: c_int = 7;\npub const SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED: c_int = 8;\npub const SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE: c_int = 9;\npub const SSH_DISCONNECT_CONNECTION_LOST: c_int = 10;\npub const SSH_DISCONNECT_BY_APPLICATION: c_int = 11;\npub const SSH_DISCONNECT_TOO_MANY_CONNECTIONS: c_int = 12;\npub const SSH_DISCONNECT_AUTH_CANCELLED_BY_USER: c_int = 13;\npub const SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE: c_int = 14;\npub const SSH_DISCONNECT_ILLEGAL_USER_NAME: c_int = 15;\n\npub const LIBSSH2_FLAG_SIGPIPE: c_int = 1;\npub const LIBSSH2_FLAG_COMPRESS: c_int = 2;\n\npub const LIBSSH2_HOSTKEY_TYPE_UNKNOWN: c_int = 0;\npub const LIBSSH2_HOSTKEY_TYPE_RSA: c_int = 1;\npub const LIBSSH2_HOSTKEY_TYPE_DSS: c_int = 2;\npub const LIBSSH2_HOSTKEY_TYPE_ECDSA_256: c_int = 3;\npub const LIBSSH2_HOSTKEY_TYPE_ECDSA_384: c_int = 4;\npub const LIBSSH2_HOSTKEY_TYPE_ECDSA_521: c_int = 5;\npub const LIBSSH2_HOSTKEY_TYPE_ED25519: c_int = 6;\n\npub const LIBSSH2_METHOD_KEX: c_int = 0;\npub const LIBSSH2_METHOD_HOSTKEY: c_int = 1;\npub const LIBSSH2_METHOD_CRYPT_CS: c_int = 2;\npub const LIBSSH2_METHOD_CRYPT_SC: c_int = 3;\npub const LIBSSH2_METHOD_MAC_CS: c_int = 4;\npub const LIBSSH2_METHOD_MAC_SC: c_int = 5;\npub const LIBSSH2_METHOD_COMP_CS: c_int = 6;\npub const LIBSSH2_METHOD_COMP_SC: c_int = 7;\npub const LIBSSH2_METHOD_LANG_CS: c_int = 8;\npub const LIBSSH2_METHOD_LANG_SC: c_int = 9;\npub const LIBSSH2_METHOD_SIGN_ALGO: c_int = 10;\n\npub const LIBSSH2_CHANNEL_PACKET_DEFAULT: c_uint = 32768;\npub const LIBSSH2_CHANNEL_WINDOW_DEFAULT: c_uint = 2 * 1024 * 1024;\n\npub const LIBSSH2_ERROR_BANNER_RECV: c_int = -2;\npub const LIBSSH2_ERROR_BANNER_SEND: c_int = -3;\npub const LIBSSH2_ERROR_INVALID_MAC: c_int = -4;\npub const LIBSSH2_ERROR_KEX_FAILURE: c_int = -5;\npub const LIBSSH2_ERROR_ALLOC: c_int = -6;\npub const LIBSSH2_ERROR_SOCKET_SEND: c_int = -7;\npub const LIBSSH2_ERROR_KEY_EXCHANGE_FAILURE: c_int = -8;\npub const LIBSSH2_ERROR_TIMEOUT: c_int = -9;\npub const LIBSSH2_ERROR_HOSTKEY_INIT: c_int = -10;\npub const LIBSSH2_ERROR_HOSTKEY_SIGN: c_int = -11;\npub const LIBSSH2_ERROR_DECRYPT: c_int = -12;\npub const LIBSSH2_ERROR_SOCKET_DISCONNECT: c_int = -13;\npub const LIBSSH2_ERROR_PROTO: c_int = -14;\npub const LIBSSH2_ERROR_PASSWORD_EXPIRED: c_int = -15;\npub const LIBSSH2_ERROR_FILE: c_int = -16;\npub const LIBSSH2_ERROR_METHOD_NONE: c_int = -17;\npub const LIBSSH2_ERROR_AUTHENTICATION_FAILED: c_int = -18;\npub const LIBSSH2_ERROR_PUBLICKEY_UNRECOGNIZED: c_int = LIBSSH2_ERROR_AUTHENTICATION_FAILED;\npub const LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED: c_int = -19;\npub const LIBSSH2_ERROR_CHANNEL_OUTOFORDER: c_int = -20;\npub const LIBSSH2_ERROR_CHANNEL_FAILURE: c_int = -21;\npub const LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED: c_int = -22;\npub const LIBSSH2_ERROR_CHANNEL_UNKNOWN: c_int = -23;\npub const LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED: c_int = -24;\npub const LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED: c_int = -25;\npub const LIBSSH2_ERROR_CHANNEL_CLOSED: c_int = -26;\npub const LIBSSH2_ERROR_CHANNEL_EOF_SENT: c_int = -27;\npub const LIBSSH2_ERROR_SCP_PROTOCOL: c_int = -28;\npub const LIBSSH2_ERROR_ZLIB: c_int = -29;\npub const LIBSSH2_ERROR_SOCKET_TIMEOUT: c_int = -30;\npub const LIBSSH2_ERROR_SFTP_PROTOCOL: c_int = -31;\npub const LIBSSH2_ERROR_REQUEST_DENIED: c_int = -32;\npub const LIBSSH2_ERROR_METHOD_NOT_SUPPORTED: c_int = -33;\npub const LIBSSH2_ERROR_INVAL: c_int = -34;\npub const LIBSSH2_ERROR_INVALID_POLL_TYPE: c_int = -35;\npub const LIBSSH2_ERROR_PUBLICKEY_PROTOCOL: c_int = -36;\npub const LIBSSH2_ERROR_EAGAIN: c_int = -37;\npub const LIBSSH2_ERROR_BUFFER_TOO_SMALL: c_int = -38;\npub const LIBSSH2_ERROR_BAD_USE: c_int = -39;\npub const LIBSSH2_ERROR_COMPRESS: c_int = -40;\npub const LIBSSH2_ERROR_OUT_OF_BOUNDARY: c_int = -41;\npub const LIBSSH2_ERROR_AGENT_PROTOCOL: c_int = -42;\npub const LIBSSH2_ERROR_SOCKET_RECV: c_int = -43;\npub const LIBSSH2_ERROR_ENCRYPT: c_int = -44;\npub const LIBSSH2_ERROR_BAD_SOCKET: c_int = -45;\npub const LIBSSH2_ERROR_KNOWN_HOSTS: c_int = -46;\npub const LIBSSH2_ERROR_CHANNEL_WINDOW_FULL: c_int = -47;\npub const LIBSSH2_ERROR_KEYFILE_AUTH_FAILED: c_int = -48;\npub const LIBSSH2_ERROR_RANDGEN: c_int = -49;\npub const LIBSSH2_ERROR_MISSING_USERAUTH_BANNER: c_int = -50;\npub const LIBSSH2_ERROR_ALGO_UNSUPPORTED: c_int = -51;\n\npub const LIBSSH2_FX_EOF: c_int = 1;\npub const LIBSSH2_FX_NO_SUCH_FILE: c_int = 2;\npub const LIBSSH2_FX_PERMISSION_DENIED: c_int = 3;\npub const LIBSSH2_FX_FAILURE: c_int = 4;\npub const LIBSSH2_FX_BAD_MESSAGE: c_int = 5;\npub const LIBSSH2_FX_NO_CONNECTION: c_int = 6;\npub const LIBSSH2_FX_CONNECTION_LOST: c_int = 7;\npub const LIBSSH2_FX_OP_UNSUPPORTED: c_int = 8;\npub const LIBSSH2_FX_INVALID_HANDLE: c_int = 9;\npub const LIBSSH2_FX_NO_SUCH_PATH: c_int = 10;\npub const LIBSSH2_FX_FILE_ALREADY_EXISTS: c_int = 11;\npub const LIBSSH2_FX_WRITE_PROTECT: c_int = 12;\npub const LIBSSH2_FX_NO_MEDIA: c_int = 13;\npub const LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM: c_int = 14;\npub const LIBSSH2_FX_QUOTA_EXCEEDED: c_int = 15;\npub const LIBSSH2_FX_UNKNOWN_PRINCIPAL: c_int = 16;\npub const LIBSSH2_FX_LOCK_CONFLICT: c_int = 17;\npub const LIBSSH2_FX_DIR_NOT_EMPTY: c_int = 18;\npub const LIBSSH2_FX_NOT_A_DIRECTORY: c_int = 19;\npub const LIBSSH2_FX_INVALID_FILENAME: c_int = 20;\npub const LIBSSH2_FX_LINK_LOOP: c_int = 21;\n\npub const LIBSSH2_HOSTKEY_HASH_MD5: c_int = 1;\npub const LIBSSH2_HOSTKEY_HASH_SHA1: c_int = 2;\npub const LIBSSH2_HOSTKEY_HASH_SHA256: c_int = 3;\n\npub const LIBSSH2_KNOWNHOST_FILE_OPENSSH: c_int = 1;\n\npub const LIBSSH2_KNOWNHOST_CHECK_MATCH: c_int = 0;\npub const LIBSSH2_KNOWNHOST_CHECK_MISMATCH: c_int = 1;\npub const LIBSSH2_KNOWNHOST_CHECK_NOTFOUND: c_int = 2;\npub const LIBSSH2_KNOWNHOST_CHECK_FAILURE: c_int = 3;\n\npub const LIBSSH2_KNOWNHOST_TYPE_PLAIN: c_int = 1;\npub const LIBSSH2_KNOWNHOST_TYPE_SHA1: c_int = 2;\npub const LIBSSH2_KNOWNHOST_TYPE_CUSTOM: c_int = 3;\npub const LIBSSH2_KNOWNHOST_KEYENC_RAW: c_int = 1 << 16;\npub const LIBSSH2_KNOWNHOST_KEYENC_BASE64: c_int = 2 << 16;\npub const LIBSSH2_KNOWNHOST_KEY_RSA1: c_int = 1 << 18;\npub const LIBSSH2_KNOWNHOST_KEY_SSHRSA: c_int = 2 << 18;\npub const LIBSSH2_KNOWNHOST_KEY_SSHDSS: c_int = 3 << 18;\npub const LIBSSH2_KNOWNHOST_KEY_ECDSA_256: c_int = 4 << 18;\npub const LIBSSH2_KNOWNHOST_KEY_ECDSA_384: c_int = 5 << 18;\npub const LIBSSH2_KNOWNHOST_KEY_ECDSA_521: c_int = 6 << 18;\npub const LIBSSH2_KNOWNHOST_KEY_ED25519: c_int = 7 << 18;\npub const LIBSSH2_KNOWNHOST_KEY_UNKNOWN: c_int = 15 << 18;\n\npub const LIBSSH2_FXF_READ: c_ulong = 0x00000001;\npub const LIBSSH2_FXF_WRITE: c_ulong = 0x00000002;\npub const LIBSSH2_FXF_APPEND: c_ulong = 0x00000004;\npub const LIBSSH2_FXF_CREAT: c_ulong = 0x00000008;\npub const LIBSSH2_FXF_TRUNC: c_ulong = 0x00000010;\npub const LIBSSH2_FXF_EXCL: c_ulong = 0x00000020;\n\npub const LIBSSH2_SFTP_OPENFILE: c_int = 0;\npub const LIBSSH2_SFTP_OPENDIR: c_int = 1;\n\npub const LIBSSH2_SFTP_ATTR_SIZE: c_ulong = 0x00000001;\npub const LIBSSH2_SFTP_ATTR_UIDGID: c_ulong = 0x00000002;\npub const LIBSSH2_SFTP_ATTR_PERMISSIONS: c_ulong = 0x00000004;\npub const LIBSSH2_SFTP_ATTR_ACMODTIME: c_ulong = 0x00000008;\npub const LIBSSH2_SFTP_ATTR_EXTENDED: c_ulong = 0x80000000;\n\npub const LIBSSH2_SFTP_STAT: c_int = 0;\npub const LIBSSH2_SFTP_LSTAT: c_int = 1;\npub const LIBSSH2_SFTP_SETSTAT: c_int = 2;\n\npub const LIBSSH2_SFTP_SYMLINK: c_int = 0;\npub const LIBSSH2_SFTP_READLINK: c_int = 1;\npub const LIBSSH2_SFTP_REALPATH: c_int = 2;\n\npub const LIBSSH2_SFTP_RENAME_OVERWRITE: c_long = 0x1;\npub const LIBSSH2_SFTP_RENAME_ATOMIC: c_long = 0x2;\npub const LIBSSH2_SFTP_RENAME_NATIVE: c_long = 0x4;\n\npub const LIBSSH2_INIT_NO_CRYPTO: c_int = 0x1;\n\npub const LIBSSH2_SFTP_S_IFMT: c_ulong = 0o170000;\npub const LIBSSH2_SFTP_S_IFIFO: c_ulong = 0o010000;\npub const LIBSSH2_SFTP_S_IFCHR: c_ulong = 0o020000;\npub const LIBSSH2_SFTP_S_IFDIR: c_ulong = 0o040000;\npub const LIBSSH2_SFTP_S_IFBLK: c_ulong = 0o060000;\npub const LIBSSH2_SFTP_S_IFREG: c_ulong = 0o100000;\npub const LIBSSH2_SFTP_S_IFLNK: c_ulong = 0o120000;\npub const LIBSSH2_SFTP_S_IFSOCK: c_ulong = 0o140000;\n\npub const LIBSSH2_CHANNEL_EXTENDED_DATA_NORMAL: c_int = 0;\npub const LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE: c_int = 1;\npub const LIBSSH2_CHANNEL_EXTENDED_DATA_MERGE: c_int = 2;\n\npub const LIBSSH2_SESSION_BLOCK_INBOUND: c_int = 1;\npub const LIBSSH2_SESSION_BLOCK_OUTBOUND: c_int = 2;\n\npub const  LIBSSH2_TRACE_TRANS : c_int = 1<<1;\npub const  LIBSSH2_TRACE_KEX   : c_int = 1<<2;\npub const  LIBSSH2_TRACE_AUTH  : c_int = 1<<3;\npub const  LIBSSH2_TRACE_CONN  : c_int = 1<<4;\npub const  LIBSSH2_TRACE_SCP   : c_int = 1<<5;\npub const  LIBSSH2_TRACE_SFTP  : c_int = 1<<6;\npub const  LIBSSH2_TRACE_ERROR : c_int = 1<<7;\npub const  LIBSSH2_TRACE_PUBLICKEY : c_int = 1<<8;\npub const  LIBSSH2_TRACE_SOCKET : c_int = 1<<9;\npub enum LIBSSH2_SESSION {}\npub enum LIBSSH2_AGENT {}\npub enum LIBSSH2_CHANNEL {}\npub enum LIBSSH2_LISTENER {}\npub enum LIBSSH2_KNOWNHOSTS {}\npub enum LIBSSH2_SFTP {}\npub enum LIBSSH2_SFTP_HANDLE {}\n\npub type libssh2_int64_t = i64;\npub type libssh2_uint64_t = u64;\n\n// libssh2_struct_stat is a typedef for libc::stat on all platforms, however,\n// Windows has a bunch of legacy around struct stat that makes things more\n// complicated to validate with systest.\n// The most reasonable looking solution to this is a newtype that derefs\n// to libc::stat.\n// We cannot use `pub struct libssh2_struct_stat(pub libc::stat)` because\n// that triggers a `no tuple structs in FFI` error.\n#[repr(C)]\npub struct libssh2_struct_stat(libc::stat);\n\nimpl std::ops::Deref for libssh2_struct_stat {\n    type Target = libc::stat;\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\n#[repr(C)]\npub struct libssh2_agent_publickey {\n    pub magic: c_uint,\n    pub node: *mut c_void,\n    pub blob: *mut c_uchar,\n    pub blob_len: size_t,\n    pub comment: *mut c_char,\n}\n\n#[repr(C)]\npub struct libssh2_knownhost {\n    pub magic: c_uint,\n    pub node: *mut c_void,\n    pub name: *mut c_char,\n    pub key: *mut c_char,\n    pub typemask: c_int,\n}\n\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct LIBSSH2_SFTP_ATTRIBUTES {\n    pub flags: c_ulong,\n    pub filesize: libssh2_uint64_t,\n    pub uid: c_ulong,\n    pub gid: c_ulong,\n    pub permissions: c_ulong,\n    pub atime: c_ulong,\n    pub mtime: c_ulong,\n}\n\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct LIBSSH2_SFTP_STATVFS {\n    pub f_bsize: libssh2_uint64_t,\n    pub f_frsize: libssh2_uint64_t,\n    pub f_blocks: libssh2_uint64_t,\n    pub f_bfree: libssh2_uint64_t,\n    pub f_bavail: libssh2_uint64_t,\n    pub f_files: libssh2_uint64_t,\n    pub f_ffree: libssh2_uint64_t,\n    pub f_favail: libssh2_uint64_t,\n    pub f_fsid: libssh2_uint64_t,\n    pub f_flag: libssh2_uint64_t,\n    pub f_namemax: libssh2_uint64_t,\n}\n\npub type LIBSSH2_ALLOC_FUNC = extern \"C\" fn(size_t, *mut *mut c_void) -> *mut c_void;\npub type LIBSSH2_FREE_FUNC = extern \"C\" fn(*mut c_void, *mut *mut c_void);\npub type LIBSSH2_REALLOC_FUNC = extern \"C\" fn(*mut c_void, size_t, *mut *mut c_void) -> *mut c_void;\npub type LIBSSH2_PASSWD_CHANGEREQ_FUNC = extern \"C\" fn(\n    sess: *mut LIBSSH2_SESSION,\n    newpw: *mut *mut c_char,\n    newpw_len: *mut c_int,\n    abstrakt: *mut *mut c_void,\n);\n\npub type LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC = extern \"C\" fn(\n    username: *const c_char,\n    username_len: c_int,\n    instruction: *const c_char,\n    instruction_len: c_int,\n    num_prompts: c_int,\n    prompts: *const LIBSSH2_USERAUTH_KBDINT_PROMPT,\n    responses: *mut LIBSSH2_USERAUTH_KBDINT_RESPONSE,\n    abstrakt: *mut *mut c_void,\n);\n\n#[repr(C)]\npub struct LIBSSH2_USERAUTH_KBDINT_PROMPT {\n    pub text: *mut c_uchar,\n    pub length: size_t,\n    pub echo: c_uchar,\n}\n\n#[repr(C)]\npub struct LIBSSH2_USERAUTH_KBDINT_RESPONSE {\n    pub text: *mut c_char,\n    pub length: c_uint,\n}\n\n#[cfg(unix)]\npub type libssh2_socket_t = c_int;\n#[cfg(all(windows, target_pointer_width = \"32\"))]\npub type libssh2_socket_t = u32;\n#[cfg(all(windows, target_pointer_width = \"64\"))]\npub type libssh2_socket_t = u64;\n\nextern \"C\" {\n    // misc\n    pub fn libssh2_init(flag: c_int) -> c_int;\n    pub fn libssh2_exit();\n    pub fn libssh2_free(sess: *mut LIBSSH2_SESSION, ptr: *mut c_void);\n    pub fn libssh2_hostkey_hash(session: *mut LIBSSH2_SESSION, hash_type: c_int) -> *const c_char;\n    pub fn libssh2_trace(session: *mut LIBSSH2_SESSION, bitmask: c_int) -> c_int;\n\n    // session\n    pub fn libssh2_session_init_ex(\n        alloc: Option<LIBSSH2_ALLOC_FUNC>,\n        free: Option<LIBSSH2_FREE_FUNC>,\n        realloc: Option<LIBSSH2_REALLOC_FUNC>,\n        abstrakt: *mut c_void,\n    ) -> *mut LIBSSH2_SESSION;\n    pub fn libssh2_session_abstract(session: *mut LIBSSH2_SESSION) -> *mut *mut c_void;\n    pub fn libssh2_session_free(sess: *mut LIBSSH2_SESSION) -> c_int;\n    pub fn libssh2_session_banner_get(sess: *mut LIBSSH2_SESSION) -> *const c_char;\n    pub fn libssh2_session_banner_set(sess: *mut LIBSSH2_SESSION, banner: *const c_char) -> c_int;\n    pub fn libssh2_session_disconnect_ex(\n        sess: *mut LIBSSH2_SESSION,\n        reason: c_int,\n        description: *const c_char,\n        lang: *const c_char,\n    ) -> c_int;\n    pub fn libssh2_session_flag(sess: *mut LIBSSH2_SESSION, flag: c_int, value: c_int) -> c_int;\n    pub fn libssh2_session_get_blocking(session: *mut LIBSSH2_SESSION) -> c_int;\n    pub fn libssh2_session_get_timeout(sess: *mut LIBSSH2_SESSION) -> c_long;\n    pub fn libssh2_session_hostkey(\n        sess: *mut LIBSSH2_SESSION,\n        len: *mut size_t,\n        kind: *mut c_int,\n    ) -> *const c_char;\n    pub fn libssh2_session_method_pref(\n        sess: *mut LIBSSH2_SESSION,\n        method_type: c_int,\n        prefs: *const c_char,\n    ) -> c_int;\n    pub fn libssh2_session_methods(sess: *mut LIBSSH2_SESSION, method_type: c_int)\n        -> *const c_char;\n    pub fn libssh2_session_set_blocking(session: *mut LIBSSH2_SESSION, blocking: c_int);\n    pub fn libssh2_session_set_timeout(session: *mut LIBSSH2_SESSION, timeout: c_long);\n    pub fn libssh2_session_supported_algs(\n        session: *mut LIBSSH2_SESSION,\n        method_type: c_int,\n        algs: *mut *mut *const c_char,\n    ) -> c_int;\n    pub fn libssh2_session_last_errno(sess: *mut LIBSSH2_SESSION) -> c_int;\n    pub fn libssh2_session_last_error(\n        sess: *mut LIBSSH2_SESSION,\n        msg: *mut *mut c_char,\n        len: *mut c_int,\n        want_buf: c_int,\n    ) -> c_int;\n    pub fn libssh2_session_handshake(sess: *mut LIBSSH2_SESSION, socket: libssh2_socket_t)\n        -> c_int;\n    pub fn libssh2_keepalive_config(\n        sess: *mut LIBSSH2_SESSION,\n        want_reply: c_int,\n        interval: c_uint,\n    );\n    pub fn libssh2_keepalive_send(sess: *mut LIBSSH2_SESSION, seconds_to_next: *mut c_int)\n        -> c_int;\n    pub fn libssh2_session_block_directions(sess: *mut LIBSSH2_SESSION) -> c_int;\n\n    // agent\n    pub fn libssh2_agent_init(sess: *mut LIBSSH2_SESSION) -> *mut LIBSSH2_AGENT;\n    pub fn libssh2_agent_free(agent: *mut LIBSSH2_AGENT);\n    pub fn libssh2_agent_connect(agent: *mut LIBSSH2_AGENT) -> c_int;\n    pub fn libssh2_agent_disconnect(agent: *mut LIBSSH2_AGENT) -> c_int;\n    pub fn libssh2_agent_list_identities(agent: *mut LIBSSH2_AGENT) -> c_int;\n    pub fn libssh2_agent_get_identity(\n        agent: *mut LIBSSH2_AGENT,\n        store: *mut *mut libssh2_agent_publickey,\n        prev: *mut libssh2_agent_publickey,\n    ) -> c_int;\n    pub fn libssh2_agent_userauth(\n        agent: *mut LIBSSH2_AGENT,\n        username: *const c_char,\n        identity: *mut libssh2_agent_publickey,\n    ) -> c_int;\n\n    // channels\n    pub fn libssh2_channel_free(chan: *mut LIBSSH2_CHANNEL) -> c_int;\n    pub fn libssh2_channel_close(chan: *mut LIBSSH2_CHANNEL) -> c_int;\n    pub fn libssh2_channel_wait_closed(chan: *mut LIBSSH2_CHANNEL) -> c_int;\n    pub fn libssh2_channel_wait_eof(chan: *mut LIBSSH2_CHANNEL) -> c_int;\n    pub fn libssh2_channel_eof(chan: *mut LIBSSH2_CHANNEL) -> c_int;\n    pub fn libssh2_channel_process_startup(\n        chan: *mut LIBSSH2_CHANNEL,\n        req: *const c_char,\n        req_len: c_uint,\n        msg: *const c_char,\n        msg_len: c_uint,\n    ) -> c_int;\n    pub fn libssh2_channel_flush_ex(chan: *mut LIBSSH2_CHANNEL, streamid: c_int) -> c_int;\n    pub fn libssh2_channel_write_ex(\n        chan: *mut LIBSSH2_CHANNEL,\n        stream_id: c_int,\n        buf: *const c_char,\n        buflen: size_t,\n    ) -> ssize_t;\n    pub fn libssh2_channel_get_exit_signal(\n        chan: *mut LIBSSH2_CHANNEL,\n        exitsignal: *mut *mut c_char,\n        exitsignal_len: *mut size_t,\n        errmsg: *mut *mut c_char,\n        errmsg_len: *mut size_t,\n        langtag: *mut *mut c_char,\n        langtag_len: *mut size_t,\n    ) -> c_int;\n    pub fn libssh2_channel_get_exit_status(chan: *mut LIBSSH2_CHANNEL) -> c_int;\n    pub fn libssh2_channel_open_ex(\n        sess: *mut LIBSSH2_SESSION,\n        channel_type: *const c_char,\n        channel_type_len: c_uint,\n        window_size: c_uint,\n        packet_size: c_uint,\n        message: *const c_char,\n        message_len: c_uint,\n    ) -> *mut LIBSSH2_CHANNEL;\n    pub fn libssh2_channel_read_ex(\n        chan: *mut LIBSSH2_CHANNEL,\n        stream_id: c_int,\n        buf: *mut c_char,\n        buflen: size_t,\n    ) -> ssize_t;\n    pub fn libssh2_channel_setenv_ex(\n        chan: *mut LIBSSH2_CHANNEL,\n        var: *const c_char,\n        varlen: c_uint,\n        val: *const c_char,\n        vallen: c_uint,\n    ) -> c_int;\n    pub fn libssh2_channel_send_eof(chan: *mut LIBSSH2_CHANNEL) -> c_int;\n    pub fn libssh2_channel_request_pty_ex(\n        chan: *mut LIBSSH2_CHANNEL,\n        term: *const c_char,\n        termlen: c_uint,\n        modes: *const c_char,\n        modeslen: c_uint,\n        width: c_int,\n        height: c_int,\n        width_px: c_int,\n        height_px: c_int,\n    ) -> c_int;\n    pub fn libssh2_channel_request_pty_size_ex(\n        chan: *mut LIBSSH2_CHANNEL,\n        width: c_int,\n        height: c_int,\n        width_px: c_int,\n        height_px: c_int,\n    ) -> c_int;\n    pub fn libssh2_channel_window_read_ex(\n        chan: *mut LIBSSH2_CHANNEL,\n        read_avail: *mut c_ulong,\n        window_size_initial: *mut c_ulong,\n    ) -> c_ulong;\n    pub fn libssh2_channel_window_write_ex(\n        chan: *mut LIBSSH2_CHANNEL,\n        window_size_initial: *mut c_ulong,\n    ) -> c_ulong;\n    pub fn libssh2_channel_receive_window_adjust2(\n        chan: *mut LIBSSH2_CHANNEL,\n        adjust: c_ulong,\n        force: c_uchar,\n        window: *mut c_uint,\n    ) -> c_int;\n    pub fn libssh2_channel_direct_tcpip_ex(\n        ses: *mut LIBSSH2_SESSION,\n        host: *const c_char,\n        port: c_int,\n        shost: *const c_char,\n        sport: c_int,\n    ) -> *mut LIBSSH2_CHANNEL;\n    pub fn libssh2_channel_direct_streamlocal_ex(\n        ses: *mut LIBSSH2_SESSION,\n        socket_path: *const c_char,\n        shost: *const c_char,\n        sport: c_int,\n    ) -> *mut LIBSSH2_CHANNEL;\n    pub fn libssh2_channel_forward_accept(listener: *mut LIBSSH2_LISTENER) -> *mut LIBSSH2_CHANNEL;\n    pub fn libssh2_channel_forward_cancel(listener: *mut LIBSSH2_LISTENER) -> c_int;\n    pub fn libssh2_channel_forward_listen_ex(\n        sess: *mut LIBSSH2_SESSION,\n        host: *const c_char,\n        port: c_int,\n        bound_port: *mut c_int,\n        queue_maxsize: c_int,\n    ) -> *mut LIBSSH2_LISTENER;\n    pub fn libssh2_channel_handle_extended_data2(\n        channel: *mut LIBSSH2_CHANNEL,\n        mode: c_int,\n    ) -> c_int;\n    pub fn libssh2_channel_request_auth_agent(channel: *mut LIBSSH2_CHANNEL) -> c_int;\n\n    // userauth\n    pub fn libssh2_userauth_banner(sess: *mut LIBSSH2_SESSION, banner: *mut *mut c_char) -> c_int;\n    pub fn libssh2_userauth_authenticated(sess: *mut LIBSSH2_SESSION) -> c_int;\n    pub fn libssh2_userauth_list(\n        sess: *mut LIBSSH2_SESSION,\n        username: *const c_char,\n        username_len: c_uint,\n    ) -> *mut c_char;\n    pub fn libssh2_userauth_hostbased_fromfile_ex(\n        sess: *mut LIBSSH2_SESSION,\n        username: *const c_char,\n        username_len: c_uint,\n        publickey: *const c_char,\n        privatekey: *const c_char,\n        passphrase: *const c_char,\n        hostname: *const c_char,\n        hostname_len: c_uint,\n        local_username: *const c_char,\n        local_len: c_uint,\n    ) -> c_int;\n    pub fn libssh2_userauth_publickey_fromfile_ex(\n        sess: *mut LIBSSH2_SESSION,\n        username: *const c_char,\n        username_len: c_uint,\n        publickey: *const c_char,\n        privatekey: *const c_char,\n        passphrase: *const c_char,\n    ) -> c_int;\n    pub fn libssh2_userauth_publickey_frommemory(\n        sess: *mut LIBSSH2_SESSION,\n        username: *const c_char,\n        username_len: size_t,\n        publickeydata: *const c_char,\n        publickeydata_len: size_t,\n        privatekeydata: *const c_char,\n        privatekeydata_len: size_t,\n        passphrase: *const c_char,\n    ) -> c_int;\n    pub fn libssh2_userauth_password_ex(\n        session: *mut LIBSSH2_SESSION,\n        username: *const c_char,\n        username_len: c_uint,\n        password: *const c_char,\n        password_len: c_uint,\n        password_change_cb: Option<LIBSSH2_PASSWD_CHANGEREQ_FUNC>,\n    ) -> c_int;\n    pub fn libssh2_userauth_keyboard_interactive_ex(\n        session: *mut LIBSSH2_SESSION,\n        username: *const c_char,\n        username_len: c_uint,\n        callback: Option<LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC>,\n    ) -> c_int;\n\n    // knownhost\n    pub fn libssh2_knownhost_free(hosts: *mut LIBSSH2_KNOWNHOSTS);\n    pub fn libssh2_knownhost_addc(\n        hosts: *mut LIBSSH2_KNOWNHOSTS,\n        host: *const c_char,\n        salt: *const c_char,\n        key: *const c_char,\n        keylen: size_t,\n        comment: *const c_char,\n        commentlen: size_t,\n        typemask: c_int,\n        store: *mut *mut libssh2_knownhost,\n    ) -> c_int;\n    pub fn libssh2_knownhost_check(\n        hosts: *mut LIBSSH2_KNOWNHOSTS,\n        host: *const c_char,\n        key: *const c_char,\n        keylen: size_t,\n        typemask: c_int,\n        knownhost: *mut *mut libssh2_knownhost,\n    ) -> c_int;\n    pub fn libssh2_knownhost_checkp(\n        hosts: *mut LIBSSH2_KNOWNHOSTS,\n        host: *const c_char,\n        port: c_int,\n        key: *const c_char,\n        keylen: size_t,\n        typemask: c_int,\n        knownhost: *mut *mut libssh2_knownhost,\n    ) -> c_int;\n    pub fn libssh2_knownhost_del(\n        hosts: *mut LIBSSH2_KNOWNHOSTS,\n        entry: *mut libssh2_knownhost,\n    ) -> c_int;\n    pub fn libssh2_knownhost_get(\n        hosts: *mut LIBSSH2_KNOWNHOSTS,\n        store: *mut *mut libssh2_knownhost,\n        prev: *mut libssh2_knownhost,\n    ) -> c_int;\n    pub fn libssh2_knownhost_readfile(\n        hosts: *mut LIBSSH2_KNOWNHOSTS,\n        filename: *const c_char,\n        kind: c_int,\n    ) -> c_int;\n    pub fn libssh2_knownhost_readline(\n        hosts: *mut LIBSSH2_KNOWNHOSTS,\n        line: *const c_char,\n        len: size_t,\n        kind: c_int,\n    ) -> c_int;\n    pub fn libssh2_knownhost_writefile(\n        hosts: *mut LIBSSH2_KNOWNHOSTS,\n        filename: *const c_char,\n        kind: c_int,\n    ) -> c_int;\n    pub fn libssh2_knownhost_writeline(\n        hosts: *mut LIBSSH2_KNOWNHOSTS,\n        known: *mut libssh2_knownhost,\n        buffer: *mut c_char,\n        buflen: size_t,\n        outlen: *mut size_t,\n        kind: c_int,\n    ) -> c_int;\n    pub fn libssh2_knownhost_init(sess: *mut LIBSSH2_SESSION) -> *mut LIBSSH2_KNOWNHOSTS;\n\n    // scp\n    #[deprecated(note = \"dangerously unsafe on windows, use libssh2_scp_recv2 instead\")]\n    pub fn libssh2_scp_recv(\n        sess: *mut LIBSSH2_SESSION,\n        path: *const c_char,\n        sb: *mut libc::stat,\n    ) -> *mut LIBSSH2_CHANNEL;\n\n    pub fn libssh2_scp_recv2(\n        sess: *mut LIBSSH2_SESSION,\n        path: *const c_char,\n        sb: *mut libssh2_struct_stat,\n    ) -> *mut LIBSSH2_CHANNEL;\n\n    pub fn libssh2_scp_send64(\n        sess: *mut LIBSSH2_SESSION,\n        path: *const c_char,\n        mode: c_int,\n        size: libssh2_int64_t,\n        mtime: libc::time_t,\n        atime: libc::time_t,\n    ) -> *mut LIBSSH2_CHANNEL;\n\n    // sftp\n    pub fn libssh2_sftp_init(sess: *mut LIBSSH2_SESSION) -> *mut LIBSSH2_SFTP;\n    pub fn libssh2_sftp_shutdown(sftp: *mut LIBSSH2_SFTP) -> c_int;\n    pub fn libssh2_sftp_last_error(sftp: *mut LIBSSH2_SFTP) -> c_ulong;\n    pub fn libssh2_sftp_open_ex(\n        sftp: *mut LIBSSH2_SFTP,\n        filename: *const c_char,\n        filename_len: c_uint,\n        flags: c_ulong,\n        mode: c_long,\n        open_type: c_int,\n    ) -> *mut LIBSSH2_SFTP_HANDLE;\n    pub fn libssh2_sftp_close_handle(handle: *mut LIBSSH2_SFTP_HANDLE) -> c_int;\n    pub fn libssh2_sftp_mkdir_ex(\n        sftp: *mut LIBSSH2_SFTP,\n        path: *const c_char,\n        path_len: c_uint,\n        mode: c_long,\n    ) -> c_int;\n    pub fn libssh2_sftp_fsync(handle: *mut LIBSSH2_SFTP_HANDLE) -> c_int;\n    pub fn libssh2_sftp_fstat_ex(\n        handle: *mut LIBSSH2_SFTP_HANDLE,\n        attrs: *mut LIBSSH2_SFTP_ATTRIBUTES,\n        setstat: c_int,\n    ) -> c_int;\n    pub fn libssh2_sftp_fstatvfs(\n        handle: *mut LIBSSH2_SFTP_HANDLE,\n        attrs: *mut LIBSSH2_SFTP_STATVFS,\n    ) -> c_int;\n    pub fn libssh2_sftp_stat_ex(\n        sftp: *mut LIBSSH2_SFTP,\n        path: *const c_char,\n        path_len: c_uint,\n        stat_type: c_int,\n        attrs: *mut LIBSSH2_SFTP_ATTRIBUTES,\n    ) -> c_int;\n    pub fn libssh2_sftp_read(\n        handle: *mut LIBSSH2_SFTP_HANDLE,\n        buf: *mut c_char,\n        len: size_t,\n    ) -> ssize_t;\n    pub fn libssh2_sftp_symlink_ex(\n        sftp: *mut LIBSSH2_SFTP,\n        path: *const c_char,\n        path_len: c_uint,\n        target: *mut c_char,\n        target_len: c_uint,\n        link_type: c_int,\n    ) -> c_int;\n    pub fn libssh2_sftp_rename_ex(\n        sftp: *mut LIBSSH2_SFTP,\n        src: *const c_char,\n        src_len: c_uint,\n        dst: *const c_char,\n        dst_len: c_uint,\n        flags: c_long,\n    ) -> c_int;\n    pub fn libssh2_sftp_rmdir_ex(\n        sftp: *mut LIBSSH2_SFTP,\n        path: *const c_char,\n        path_len: c_uint,\n    ) -> c_int;\n    pub fn libssh2_sftp_write(\n        handle: *mut LIBSSH2_SFTP_HANDLE,\n        buffer: *const c_char,\n        len: size_t,\n    ) -> ssize_t;\n    pub fn libssh2_sftp_tell64(handle: *mut LIBSSH2_SFTP_HANDLE) -> libssh2_uint64_t;\n    pub fn libssh2_sftp_seek64(handle: *mut LIBSSH2_SFTP_HANDLE, off: libssh2_uint64_t);\n    pub fn libssh2_sftp_readdir_ex(\n        handle: *mut LIBSSH2_SFTP_HANDLE,\n        buffer: *mut c_char,\n        buffer_len: size_t,\n        longentry: *mut c_char,\n        longentry_len: size_t,\n        attrs: *mut LIBSSH2_SFTP_ATTRIBUTES,\n    ) -> c_int;\n    pub fn libssh2_sftp_unlink_ex(\n        sftp: *mut LIBSSH2_SFTP,\n        filename: *const c_char,\n        filename_len: c_uint,\n    ) -> c_int;\n}\n\n#[test]\nfn smoke() {\n    unsafe { libssh2_init(0) };\n}\n\n#[doc(hidden)]\npub fn issue_14344_workaround() {}\n\npub fn init() {\n    use std::sync::Once;\n\n    static INIT: Once = Once::new();\n    INIT.call_once(|| unsafe {\n        platform_init();\n        assert_eq!(libc::atexit(shutdown), 0);\n    });\n    extern \"C\" fn shutdown() {\n        unsafe {\n            libssh2_exit();\n        }\n    }\n\n    #[cfg(unix)]\n    unsafe fn platform_init() {\n        // On Unix we want to funnel through openssl_sys to initialize OpenSSL,\n        // so be sure to tell libssh2 to not do its own thing as we've already\n        // taken care of it.\n        openssl_sys::init();\n        assert_eq!(libssh2_init(LIBSSH2_INIT_NO_CRYPTO), 0);\n    }\n\n    #[cfg(windows)]\n    unsafe fn platform_init() {\n        // On Windows we want to be sure to tell libssh2 to initialize\n        // everything, as we're not managing crypto elsewhere ourselves. Also to\n        // fix alexcrichton/git2-rs#202\n        assert_eq!(libssh2_init(0), 0);\n    }\n}\n"
  },
  {
    "path": "src/agent.rs",
    "content": "use parking_lot::{Mutex, MutexGuard};\nuse std::ffi::{CStr, CString};\nuse std::ptr::null_mut;\nuse std::slice;\nuse std::str;\nuse std::sync::Arc;\n\nuse {raw, Error, ErrorCode, SessionInner};\n\n/// A structure representing a connection to an SSH agent.\n///\n/// Agents can be used to authenticate a session.\npub struct Agent {\n    raw: *mut raw::LIBSSH2_AGENT,\n    sess: Arc<Mutex<SessionInner>>,\n}\n\n// Agent is both Send and Sync; the compiler can't see it because it\n// is pessimistic about the raw pointer.  We use Arc/Mutex to guard accessing\n// the raw pointer so we are safe for both.\nunsafe impl Send for Agent {}\nunsafe impl Sync for Agent {}\n\n/// A public key which is extracted from an SSH agent.\n#[derive(Debug, PartialEq, Eq)]\npub struct PublicKey {\n    blob: Vec<u8>,\n    comment: String,\n}\n\nimpl Agent {\n    pub(crate) fn from_raw_opt(\n        raw: *mut raw::LIBSSH2_AGENT,\n        err: Option<Error>,\n        sess: &Arc<Mutex<SessionInner>>,\n    ) -> Result<Self, Error> {\n        if raw.is_null() {\n            Err(err.unwrap_or_else(Error::unknown))\n        } else {\n            Ok(Self {\n                raw,\n                sess: Arc::clone(sess),\n            })\n        }\n    }\n\n    /// Connect to an ssh-agent running on the system.\n    pub fn connect(&mut self) -> Result<(), Error> {\n        let sess = self.sess.lock();\n        unsafe { sess.rc(raw::libssh2_agent_connect(self.raw)) }\n    }\n\n    /// Close a connection to an ssh-agent.\n    pub fn disconnect(&mut self) -> Result<(), Error> {\n        let sess = self.sess.lock();\n        unsafe { sess.rc(raw::libssh2_agent_disconnect(self.raw)) }\n    }\n\n    /// Request an ssh-agent to list of public keys, and stores them in the\n    /// internal collection of the handle.\n    ///\n    /// Call `identities` to get the public keys.\n    pub fn list_identities(&mut self) -> Result<(), Error> {\n        let sess = self.sess.lock();\n        unsafe { sess.rc(raw::libssh2_agent_list_identities(self.raw)) }\n    }\n\n    /// Get list of the identities of this agent.\n    pub fn identities(&self) -> Result<Vec<PublicKey>, Error> {\n        let sess = self.sess.lock();\n        let mut res = vec![];\n        let mut prev = null_mut();\n        let mut next = null_mut();\n        loop {\n            match unsafe { raw::libssh2_agent_get_identity(self.raw, &mut next, prev) } {\n                0 => {\n                    prev = next;\n                    res.push(unsafe { PublicKey::from_raw(next) });\n                }\n                1 => break,\n                rc => return Err(Error::from_session_error_raw(sess.raw, rc)),\n            }\n        }\n        Ok(res)\n    }\n\n    fn resolve_raw_identity(\n        &self,\n        sess: &MutexGuard<SessionInner>,\n        identity: &PublicKey,\n    ) -> Result<Option<*mut raw::libssh2_agent_publickey>, Error> {\n        let mut prev = null_mut();\n        let mut next = null_mut();\n        loop {\n            match unsafe { raw::libssh2_agent_get_identity(self.raw, &mut next, prev) } {\n                0 => {\n                    prev = next;\n                    let this_ident = unsafe { PublicKey::from_raw(next) };\n                    if this_ident == *identity {\n                        return Ok(Some(next));\n                    }\n                }\n                1 => break,\n                rc => return Err(Error::from_session_error_raw(sess.raw, rc)),\n            }\n        }\n        Ok(None)\n    }\n\n    /// Attempt public key authentication with the help of ssh-agent.\n    pub fn userauth(&self, username: &str, identity: &PublicKey) -> Result<(), Error> {\n        let username = CString::new(username)?;\n        let sess = self.sess.lock();\n        let raw_ident = self.resolve_raw_identity(&sess, identity)?.ok_or_else(|| {\n            Error::new(\n                ErrorCode::Session(raw::LIBSSH2_ERROR_BAD_USE),\n                \"Identity not found in agent\",\n            )\n        })?;\n        unsafe {\n            sess.rc(raw::libssh2_agent_userauth(\n                self.raw,\n                username.as_ptr(),\n                raw_ident,\n            ))\n        }\n    }\n}\n\nimpl Drop for Agent {\n    fn drop(&mut self) {\n        unsafe { raw::libssh2_agent_free(self.raw) }\n    }\n}\n\nimpl PublicKey {\n    unsafe fn from_raw(raw: *mut raw::libssh2_agent_publickey) -> Self {\n        let blob = slice::from_raw_parts_mut((*raw).blob, (*raw).blob_len as usize);\n        let comment = (*raw).comment;\n        let comment = if comment.is_null() {\n            String::new()\n        } else {\n            CStr::from_ptr(comment).to_string_lossy().into_owned()\n        };\n        Self {\n            blob: blob.to_vec(),\n            comment,\n        }\n    }\n\n    /// Return the data of this public key.\n    pub fn blob(&self) -> &[u8] {\n        &self.blob\n    }\n\n    /// Returns the comment in a printable format\n    pub fn comment(&self) -> &str {\n        &self.comment\n    }\n}\n"
  },
  {
    "path": "src/channel.rs",
    "content": "use libc::{c_char, c_int, c_uchar, c_uint, c_ulong, c_void, size_t};\nuse parking_lot::{Mutex, MutexGuard};\nuse std::cmp;\nuse std::ffi::CString;\nuse std::ptr::{null, null_mut};\nuse std::io;\nuse std::io::prelude::*;\nuse std::slice;\nuse std::sync::Arc;\n\nuse {raw, Error, ExtendedData, PtyModes, SessionInner};\n\nstruct ChannelInner {\n    unsafe_raw: *mut raw::LIBSSH2_CHANNEL,\n    sess: Arc<Mutex<SessionInner>>,\n    read_limit: Mutex<Option<u64>>,\n}\n\n// ChannelInner is both Send and Sync; the compiler can't see it because it\n// is pessimistic about the raw pointer.  We use Arc/Mutex to guard accessing\n// the raw pointer so we are safe for both.\nunsafe impl Send for ChannelInner {}\nunsafe impl Sync for ChannelInner {}\n\nstruct LockedChannel<'a> {\n    raw: *mut raw::LIBSSH2_CHANNEL,\n    sess: MutexGuard<'a, SessionInner>,\n}\n\n/// A channel represents a portion of an SSH connection on which data can be\n/// read and written.\n///\n/// Channels denote all of SCP uploads and downloads, shell sessions, remote\n/// process executions, and other general-purpose sessions. Each channel\n/// implements the `Reader` and `Writer` traits to send and receive data.\n/// Whether or not I/O operations are blocking is mandated by the `blocking`\n/// flag on a channel's corresponding `Session`.\n///\n/// You may clone a `Channel` to obtain another handle to the same underlying\n/// channel, but note that all clones will share the same underlying SSH\n/// session and will be subject to the same blocking behavior. For more details\n/// on the implications of cloning and blocking operations, refer to the\n/// `Session` documentation.\n#[derive(Clone)]\npub struct Channel {\n    channel_inner: Arc<ChannelInner>,\n}\n\nimpl Channel {\n    pub(crate) fn from_raw_opt(\n        raw: *mut raw::LIBSSH2_CHANNEL,\n        err: Option<Error>,\n        sess: &Arc<Mutex<SessionInner>>,\n    ) -> Result<Self, Error> {\n        if raw.is_null() {\n            Err(err.unwrap_or_else(Error::unknown))\n        } else {\n            Ok(Self {\n                channel_inner: Arc::new(ChannelInner {\n                    unsafe_raw: raw,\n                    sess: Arc::clone(sess),\n                    read_limit: Mutex::new(None),\n                }),\n            })\n        }\n    }\n\n    fn lock(&self) -> LockedChannel<'_> {\n        let sess = self.channel_inner.sess.lock();\n        LockedChannel {\n            sess,\n            raw: self.channel_inner.unsafe_raw,\n        }\n    }\n}\n\n/// A channel can have a number of streams, each identified by an id, each of\n/// which implements the `Read` and `Write` traits.\n///\n/// You may clone a `Stream` to obtain another handle to the same underlying\n/// stream, but note that all clones will share the same underlying SSH\n/// session and will be subject to the same blocking behavior. For more details\n/// on the implications of cloning and blocking operations, refer to the\n/// `Session` documentation.\n#[derive(Clone)]\npub struct Stream {\n    channel_inner: Arc<ChannelInner>,\n    id: i32,\n}\n\nstruct LockedStream<'a> {\n    raw: *mut raw::LIBSSH2_CHANNEL,\n    sess: MutexGuard<'a, SessionInner>,\n    id: i32,\n    read_limit: MutexGuard<'a, Option<u64>>,\n}\n\nimpl<'a> LockedStream<'a> {\n    pub fn eof(&self) -> bool {\n        *self.read_limit == Some(0) || unsafe { raw::libssh2_channel_eof(self.raw) != 0 }\n    }\n}\n\n/// Data received from when a program exits with a signal.\npub struct ExitSignal {\n    /// The exit signal received, if the program did not exit cleanly. Does not\n    /// contain a SIG prefix\n    pub exit_signal: Option<String>,\n    /// Error message provided by the remote server (if any)\n    pub error_message: Option<String>,\n    /// Language tag provided by the remote server (if any)\n    pub lang_tag: Option<String>,\n}\n\n/// Description of the read window as returned by `Channel::read_window`\n#[derive(Copy, Clone)]\npub struct ReadWindow {\n    /// The number of bytes which the remote end may send without overflowing\n    /// the window limit.\n    pub remaining: u32,\n    /// The number of bytes actually available to be read.\n    pub available: u32,\n    /// The window_size_initial as defined by the channel open request\n    pub window_size_initial: u32,\n}\n\n/// Description of the write window as returned by `Channel::write_window`\n#[derive(Copy, Clone)]\npub struct WriteWindow {\n    /// The number of bytes which may be safely written on the channel without\n    /// blocking.\n    pub remaining: u32,\n    /// The window_size_initial as defined by the channel open request\n    pub window_size_initial: u32,\n}\n\nimpl Channel {\n    /// Set an environment variable in the remote channel's process space.\n    ///\n    /// Note that this does not make sense for all channel types and may be\n    /// ignored by the server despite returning success.\n    pub fn setenv(&mut self, var: &str, val: &str) -> Result<(), Error> {\n        let var = CString::new(var)?;\n        let var = var.as_bytes();\n        let val = CString::new(val)?;\n        let val = val.as_bytes();\n        let locked = self.lock();\n        unsafe {\n            locked.sess.rc(raw::libssh2_channel_setenv_ex(\n                locked.raw,\n                var.as_ptr() as *const _,\n                var.len() as c_uint,\n                val.as_ptr() as *const _,\n                val.len() as c_uint,\n            ))\n        }\n    }\n\n    /// Request a PTY on an established channel.\n    ///\n    /// Note that this does not make sense for all channel types and may be\n    /// ignored by the server despite returning success.\n    ///\n    /// The dimensions argument is a tuple of (width, height, width_px,\n    /// height_px)\n    ///\n    /// The mode parameter is optional and specifies modes to apply to\n    /// the pty.  Use the `PtyModes` type construct these modes.\n    /// A contrived example of this is below:\n    ///\n    /// ```\n    /// let mut mode = ssh2::PtyModes::new();\n    /// // Set the interrupt character to CTRL-C (ASCII 3: ETX).\n    /// // This is typically the default, but we're showing how to\n    /// // set a relatable option for the sake of example!\n    /// mode.set_character(ssh2::PtyModeOpcode::VINTR, Some(3 as char));\n    /// ```\n    pub fn request_pty(\n        &mut self,\n        term: &str,\n        mode: Option<PtyModes>,\n        dim: Option<(u32, u32, u32, u32)>,\n    ) -> Result<(), Error> {\n        let term = CString::new(term)?;\n        let term = term.as_bytes();\n        let locked = self.lock();\n        let mode = mode.map(PtyModes::finish);\n        let mode = mode.as_ref().map(Vec::as_slice).unwrap_or(&[]);\n        locked.sess.rc(unsafe {\n            let (width, height, width_px, height_px) = dim.unwrap_or((80, 24, 0, 0));\n            raw::libssh2_channel_request_pty_ex(\n                locked.raw,\n                term.as_ptr() as *const _,\n                term.len() as c_uint,\n                mode.as_ptr() as *const _,\n                mode.len() as c_uint,\n                width as c_int,\n                height as c_int,\n                width_px as c_int,\n                height_px as c_int,\n            )\n        })\n    }\n\n    /// Request that the PTY size be changed to the specified size.\n    /// width and height are the number of character cells, and you\n    /// may optionally include the size specified in pixels.\n    pub fn request_pty_size(\n        &mut self,\n        width: u32,\n        height: u32,\n        width_px: Option<u32>,\n        height_px: Option<u32>,\n    ) -> Result<(), Error> {\n        let locked = self.lock();\n        let width_px = width_px.unwrap_or(0);\n        let height_px = height_px.unwrap_or(0);\n        locked.sess.rc(unsafe {\n            raw::libssh2_channel_request_pty_size_ex(\n                locked.raw,\n                width as c_int,\n                height as c_int,\n                width_px as c_int,\n                height_px as c_int,\n            )\n        })\n    }\n\n    /// Requests that the remote host start an authentication agent;\n    /// if successful requests to that agent will be forwarded from\n    /// the server back to the local authentication agent on the client side.\n    ///\n    /// Note that some hosts are configured to disallow agent forwarding,\n    /// and that even if enabled, there is a possibility that starting\n    /// the agent on the remote system can fail.\n    pub fn request_auth_agent_forwarding(&mut self) -> Result<(), Error> {\n        let locked = self.lock();\n        locked\n            .sess\n            .rc(unsafe { raw::libssh2_channel_request_auth_agent(locked.raw) })\n    }\n\n    /// Execute a command\n    ///\n    /// An execution is one of the standard process services defined by the SSH2\n    /// protocol.\n    ///\n    /// # Example\n    ///\n    /// ```no_run\n    /// # use std::io::prelude::*;\n    /// # use ssh2::Session;\n    /// # let session: Session = panic!();\n    /// let mut channel = session.channel_session().unwrap();\n    /// channel.exec(\"ls\").unwrap();\n    /// let mut s = String::new();\n    /// channel.read_to_string(&mut s).unwrap();\n    /// println!(\"{}\", s);\n    /// ```\n    pub fn exec(&mut self, command: &str) -> Result<(), Error> {\n        self.process_startup(\"exec\", Some(command))\n    }\n\n    /// Start a shell\n    ///\n    /// A shell is one of the standard process services defined by the SSH2\n    /// protocol.\n    pub fn shell(&mut self) -> Result<(), Error> {\n        self.process_startup(\"shell\", None)\n    }\n\n    /// Request a subsystem be started.\n    ///\n    /// A subsystem is one of the standard process services defined by the SSH2\n    /// protocol.\n    pub fn subsystem(&mut self, system: &str) -> Result<(), Error> {\n        self.process_startup(\"subsystem\", Some(system))\n    }\n\n    /// Initiate a request on a session type channel.\n    ///\n    /// The SSH2 protocol currently defines shell, exec, and subsystem as\n    /// standard process services.\n    pub fn process_startup(&mut self, request: &str, message: Option<&str>) -> Result<(), Error> {\n        let message = message.map(|s| CString::new(s)).transpose()?;\n        let (message, message_len) = message\n            .as_ref()\n            .map(|s| (s.as_ptr(), s.as_bytes().len()))\n            .unwrap_or((null(), 0));\n        let locked = self.lock();\n        unsafe {\n            let rc = raw::libssh2_channel_process_startup(\n                locked.raw,\n                request.as_ptr() as *const _,\n                request.len() as c_uint,\n                message,\n                message_len as c_uint,\n            );\n            locked.sess.rc(rc)\n        }\n    }\n\n    /// Get a handle to the stderr stream of this channel.\n    ///\n    /// The returned handle implements the `Read` and `Write` traits.\n    pub fn stderr(&self) -> Stream {\n        self.stream(::EXTENDED_DATA_STDERR)\n    }\n\n    /// Get a handle to a particular stream for this channel.\n    ///\n    /// The returned handle implements the `Read` and `Write` traits.\n    ///\n    /// Groups of substreams may be flushed by passing one of the following\n    /// constants and then calling `flush()`.\n    ///\n    /// * FLUSH_EXTENDED_DATA - Flush all extended data substreams\n    /// * FLUSH_ALL - Flush all substreams\n    pub fn stream(&self, stream_id: i32) -> Stream {\n        Stream {\n            channel_inner: Arc::clone(&self.channel_inner),\n            id: stream_id,\n        }\n    }\n\n    /// Change how extended data (such as stderr) is handled\n    pub fn handle_extended_data(&mut self, mode: ExtendedData) -> Result<(), Error> {\n        let locked = self.lock();\n        unsafe {\n            let rc = raw::libssh2_channel_handle_extended_data2(locked.raw, mode as c_int);\n            locked.sess.rc(rc)\n        }\n    }\n\n    /// Returns the exit code raised by the process running on the remote host\n    /// at the other end of the named channel.\n    ///\n    /// Note that the exit status may not be available if the remote end has not\n    /// yet set its status to closed.\n    pub fn exit_status(&self) -> Result<i32, Error> {\n        let locked = self.lock();\n        // Should really store existing error, call function, check for error\n        // after and restore previous error if no new one...but the only error\n        // condition right now is a NULL pointer check on self.raw, so let's\n        // assume that's not the case.\n        Ok(unsafe { raw::libssh2_channel_get_exit_status(locked.raw) })\n    }\n\n    /// Get the remote exit signal.\n    pub fn exit_signal(&self) -> Result<ExitSignal, Error> {\n        let locked = self.lock();\n        unsafe {\n            let mut sig = null_mut();\n            let mut siglen = 0;\n            let mut msg = null_mut();\n            let mut msglen = 0;\n            let mut lang = null_mut();\n            let mut langlen = 0;\n            let rc = raw::libssh2_channel_get_exit_signal(\n                locked.raw,\n                &mut sig,\n                &mut siglen,\n                &mut msg,\n                &mut msglen,\n                &mut lang,\n                &mut langlen,\n            );\n            locked.sess.rc(rc)?;\n            return Ok(ExitSignal {\n                exit_signal: convert(&locked, sig, siglen),\n                error_message: convert(&locked, msg, msglen),\n                lang_tag: convert(&locked, lang, langlen),\n            });\n        }\n\n        unsafe fn convert(locked: &LockedChannel, ptr: *mut c_char, len: size_t) -> Option<String> {\n            if ptr.is_null() {\n                return None;\n            }\n            let slice = slice::from_raw_parts(ptr as *const u8, len as usize);\n            let ret = slice.to_vec();\n            raw::libssh2_free(locked.sess.raw, ptr as *mut c_void);\n            String::from_utf8(ret).ok()\n        }\n    }\n\n    /// Check the status of the read window.\n    pub fn read_window(&self) -> ReadWindow {\n        let locked = self.lock();\n        unsafe {\n            let mut avail = 0;\n            let mut init = 0;\n            let remaining = raw::libssh2_channel_window_read_ex(locked.raw, &mut avail, &mut init);\n            ReadWindow {\n                remaining: remaining as u32,\n                available: avail as u32,\n                window_size_initial: init as u32,\n            }\n        }\n    }\n\n    /// Check the status of the write window.\n    pub fn write_window(&self) -> WriteWindow {\n        let locked = self.lock();\n        unsafe {\n            let mut init = 0;\n            let remaining = raw::libssh2_channel_window_write_ex(locked.raw, &mut init);\n            WriteWindow {\n                remaining: remaining as u32,\n                window_size_initial: init as u32,\n            }\n        }\n    }\n\n    /// Adjust the receive window for a channel by adjustment bytes.\n    ///\n    /// If the amount to be adjusted is less than the minimum adjustment and\n    /// force is false, the adjustment amount will be queued for a later packet.\n    ///\n    /// This function returns the new size of the receive window (as understood\n    /// by remote end) on success.\n    pub fn adjust_receive_window(&mut self, adjust: u64, force: bool) -> Result<u64, Error> {\n        let locked = self.lock();\n        let mut ret = 0;\n        let rc = unsafe {\n            raw::libssh2_channel_receive_window_adjust2(\n                locked.raw,\n                adjust as c_ulong,\n                force as c_uchar,\n                &mut ret,\n            )\n        };\n        locked.sess.rc(rc)?;\n        Ok(ret as u64)\n    }\n\n    /// Artificially limit the number of bytes that will be read from this\n    /// channel. Hack intended for use by scp_recv only.\n    #[doc(hidden)]\n    pub(crate) fn limit_read(&mut self, limit: u64) {\n        *self.channel_inner.read_limit.lock() = Some(limit);\n    }\n\n    /// Check if the remote host has sent an EOF status for the channel.\n    /// Take care: the EOF status is for the entire channel which can be confusing\n    /// because the reading from the channel reads only the stdout stream.\n    /// unread, buffered, stderr data will cause eof() to return false.\n    pub fn eof(&self) -> bool {\n        let locked = self.lock();\n        *self.channel_inner.read_limit.lock() == Some(0)\n            || unsafe { raw::libssh2_channel_eof(locked.raw) != 0 }\n    }\n\n    /// Tell the remote host that no further data will be sent on the specified\n    /// channel.\n    ///\n    /// Processes typically interpret this as a closed stdin descriptor.\n    pub fn send_eof(&mut self) -> Result<(), Error> {\n        let locked = self.lock();\n        unsafe { locked.sess.rc(raw::libssh2_channel_send_eof(locked.raw)) }\n    }\n\n    /// Wait for the remote end to send EOF.\n    /// Note that unread buffered stdout and stderr will cause this function\n    /// to return `Ok(())` without waiting.\n    /// You should call the eof() function after calling this to check the\n    /// status of the channel.\n    pub fn wait_eof(&mut self) -> Result<(), Error> {\n        let locked = self.lock();\n        unsafe { locked.sess.rc(raw::libssh2_channel_wait_eof(locked.raw)) }\n    }\n\n    /// Close an active data channel.\n    ///\n    /// In practice this means sending an SSH_MSG_CLOSE packet to the remote\n    /// host which serves as instruction that no further data will be sent to\n    /// it. The remote host may still send data back until it sends its own\n    /// close message in response.\n    ///\n    /// To wait for the remote end to close its connection as well, follow this\n    /// command with `wait_close`\n    pub fn close(&mut self) -> Result<(), Error> {\n        let locked = self.lock();\n        unsafe { locked.sess.rc(raw::libssh2_channel_close(locked.raw)) }\n    }\n\n    /// Enter a temporary blocking state until the remote host closes the named\n    /// channel.\n    ///\n    /// Typically sent after `close` in order to examine the exit status.\n    pub fn wait_close(&mut self) -> Result<(), Error> {\n        let locked = self.lock();\n        unsafe { locked.sess.rc(raw::libssh2_channel_wait_closed(locked.raw)) }\n    }\n}\n\nimpl Write for Channel {\n    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {\n        self.stream(0).write(buf)\n    }\n\n    fn flush(&mut self) -> io::Result<()> {\n        self.stream(0).flush()\n    }\n}\n\nimpl Read for Channel {\n    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {\n        self.stream(0).read(buf)\n    }\n}\n\nimpl Drop for ChannelInner {\n    fn drop(&mut self) {\n        unsafe {\n            let _ = raw::libssh2_channel_free(self.unsafe_raw);\n        }\n    }\n}\n\nimpl Stream {\n    fn lock(&self) -> LockedStream<'_> {\n        let sess = self.channel_inner.sess.lock();\n        LockedStream {\n            sess,\n            raw: self.channel_inner.unsafe_raw,\n            id: self.id,\n            read_limit: self.channel_inner.read_limit.lock(),\n        }\n    }\n}\n\nimpl Read for Stream {\n    fn read(&mut self, data: &mut [u8]) -> io::Result<usize> {\n        let mut locked = self.lock();\n        if locked.eof() {\n            return Ok(0);\n        }\n\n        let data = match locked.read_limit.as_mut() {\n            Some(amt) => {\n                let len = data.len();\n                &mut data[..cmp::min(*amt as usize, len)]\n            }\n            None => data,\n        };\n        let ret = unsafe {\n            let rc = raw::libssh2_channel_read_ex(\n                locked.raw,\n                locked.id as c_int,\n                data.as_mut_ptr() as *mut _,\n                data.len() as size_t,\n            );\n            locked.sess.rc(rc as c_int).map(|()| rc as usize)\n        };\n        match ret {\n            Ok(n) => {\n                if let Some(ref mut amt) = locked.read_limit.as_mut() {\n                    **amt -= n as u64;\n                }\n                Ok(n)\n            }\n            Err(e) => Err(e.into()),\n        }\n    }\n}\n\nimpl Write for Stream {\n    fn write(&mut self, data: &[u8]) -> io::Result<usize> {\n        let locked = self.lock();\n        unsafe {\n            let rc = raw::libssh2_channel_write_ex(\n                locked.raw,\n                locked.id as c_int,\n                data.as_ptr() as *mut _,\n                data.len() as size_t,\n            );\n            locked.sess.rc(rc as c_int).map(|()| rc as usize)\n        }\n        .map_err(Into::into)\n    }\n\n    fn flush(&mut self) -> io::Result<()> {\n        let locked = self.lock();\n        unsafe {\n            let rc = raw::libssh2_channel_flush_ex(locked.raw, locked.id as c_int);\n            locked.sess.rc(rc)\n        }\n        .map_err(Into::into)\n    }\n}\n"
  },
  {
    "path": "src/error.rs",
    "content": "use libc;\nuse std::borrow::Cow;\nuse std::error;\nuse std::ffi::NulError;\nuse std::fmt;\nuse std::io;\nuse std::ptr::null_mut;\nuse std::str;\n\nuse {raw, Session};\n\n/// An error code originating from a particular source.\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\npub enum ErrorCode {\n    /// Codes for errors that originate in libssh2.\n    /// Can be one of  `LIBSSH2_ERROR_*` constants.\n    Session(libc::c_int),\n\n    /// Codes for errors that originate in the SFTP subsystem.\n    /// Can be one of `LIBSSH2_FX_*` constants.\n    //\n    // TODO: This should be `c_ulong` instead of `c_int` because these constants\n    // are only returned by `libssh2_sftp_last_error()` which returns `c_ulong`.\n    SFTP(libc::c_int),\n}\n\nimpl fmt::Display for ErrorCode {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        write!(f, \"{:?}\", self)\n    }\n}\n\n/// Representation of an error that can occur within libssh2\n#[derive(Debug)]\n#[allow(missing_copy_implementations)]\npub struct Error {\n    code: ErrorCode,\n    msg: Cow<'static, str>,\n}\n\nimpl Error {\n    #[doc(hidden)]\n    pub fn last_session_error_raw(raw: *mut raw::LIBSSH2_SESSION) -> Option<Error> {\n        unsafe {\n            let mut msg = null_mut();\n            let rc = raw::libssh2_session_last_error(raw, &mut msg, null_mut(), 0);\n            if rc == 0 {\n                return None;\n            }\n\n            // The pointer stored in `msg` points to the internal buffer of\n            // LIBSSH2_SESSION, so the error message should be copied before\n            // it is overwritten by the next API call.\n            Some(Self {\n                code: ErrorCode::Session(rc),\n                msg: make_error_message(msg),\n            })\n        }\n    }\n\n    /// Given a libssh2 error return code, generate an Error object that\n    /// encapsulates that error code and the error reason.\n    /// The error reason is extracted from the Session and is used if the\n    /// session contains the same error code as that provided.\n    /// If the error code doesn't match then an approximation of the error\n    /// reason is used instead of the error message stored in the Session.\n    pub fn from_session_error(sess: &Session, rc: libc::c_int) -> Error {\n        Self::from_session_error_raw(&mut *sess.raw(), rc)\n    }\n\n    #[doc(hidden)]\n    pub fn from_session_error_raw(raw: *mut raw::LIBSSH2_SESSION, rc: libc::c_int) -> Error {\n        unsafe {\n            let mut msg = null_mut();\n            let res = raw::libssh2_session_last_error(raw, &mut msg, null_mut(), 0);\n            if res != rc {\n                return Self::from_errno(ErrorCode::Session(rc));\n            }\n\n            // The pointer stored in `msg` points to the internal buffer of\n            // LIBSSH2_SESSION, so the error message should be copied before\n            // it is overwritten by the next API call.\n            Self {\n                code: ErrorCode::Session(rc),\n                msg: make_error_message(msg),\n            }\n        }\n    }\n\n    /// Generate the last error that occurred for a `Session`.\n    ///\n    /// Returns `None` if there was no last error.\n    pub fn last_session_error(sess: &Session) -> Option<Error> {\n        Self::last_session_error_raw(&mut *sess.raw())\n    }\n\n    /// Create a new error for the given code and message\n    pub fn new(code: ErrorCode, msg: &'static str) -> Error {\n        Error {\n            code,\n            msg: Cow::Borrowed(msg),\n        }\n    }\n\n    /// Generate an error that represents EOF\n    pub fn eof() -> Error {\n        Error::new(\n            ErrorCode::Session(raw::LIBSSH2_ERROR_CHANNEL_EOF_SENT),\n            \"end of file\",\n        )\n    }\n\n    /// Generate an error for unknown failure\n    pub fn unknown() -> Error {\n        Error::new(\n            ErrorCode::Session(libc::c_int::min_value()),\n            \"no other error listed\",\n        )\n    }\n\n    /// Construct an error from an error code from libssh2\n    pub fn from_errno(code: ErrorCode) -> Error {\n        let msg = match code {\n            ErrorCode::Session(code) => match code {\n                raw::LIBSSH2_ERROR_BANNER_RECV => \"banner recv failure\",\n                raw::LIBSSH2_ERROR_BANNER_SEND => \"banner send failure\",\n                raw::LIBSSH2_ERROR_INVALID_MAC => \"invalid mac\",\n                raw::LIBSSH2_ERROR_KEX_FAILURE => \"kex failure\",\n                raw::LIBSSH2_ERROR_ALLOC => \"alloc failure\",\n                raw::LIBSSH2_ERROR_SOCKET_SEND => \"socket send failure\",\n                raw::LIBSSH2_ERROR_KEY_EXCHANGE_FAILURE => \"key exchange failure\",\n                raw::LIBSSH2_ERROR_TIMEOUT => \"timed out\",\n                raw::LIBSSH2_ERROR_HOSTKEY_INIT => \"hostkey init error\",\n                raw::LIBSSH2_ERROR_HOSTKEY_SIGN => \"hostkey sign error\",\n                raw::LIBSSH2_ERROR_DECRYPT => \"decrypt error\",\n                raw::LIBSSH2_ERROR_SOCKET_DISCONNECT => \"socket disconnected\",\n                raw::LIBSSH2_ERROR_PROTO => \"protocol error\",\n                raw::LIBSSH2_ERROR_PASSWORD_EXPIRED => \"password expired\",\n                raw::LIBSSH2_ERROR_FILE => \"file error\",\n                raw::LIBSSH2_ERROR_METHOD_NONE => \"bad method name\",\n                raw::LIBSSH2_ERROR_AUTHENTICATION_FAILED => \"authentication failed\",\n                raw::LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED => \"public key unverified\",\n                raw::LIBSSH2_ERROR_CHANNEL_OUTOFORDER => \"channel out of order\",\n                raw::LIBSSH2_ERROR_CHANNEL_FAILURE => \"channel failure\",\n                raw::LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED => \"request denied\",\n                raw::LIBSSH2_ERROR_CHANNEL_UNKNOWN => \"unknown channel error\",\n                raw::LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED => \"window exceeded\",\n                raw::LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED => \"packet exceeded\",\n                raw::LIBSSH2_ERROR_CHANNEL_CLOSED => \"closed channel\",\n                raw::LIBSSH2_ERROR_CHANNEL_EOF_SENT => \"eof sent\",\n                raw::LIBSSH2_ERROR_SCP_PROTOCOL => \"scp protocol error\",\n                raw::LIBSSH2_ERROR_ZLIB => \"zlib error\",\n                raw::LIBSSH2_ERROR_SOCKET_TIMEOUT => \"socket timeout\",\n                raw::LIBSSH2_ERROR_SFTP_PROTOCOL => \"sftp protocol error\",\n                raw::LIBSSH2_ERROR_REQUEST_DENIED => \"request denied\",\n                raw::LIBSSH2_ERROR_METHOD_NOT_SUPPORTED => \"method not supported\",\n                raw::LIBSSH2_ERROR_INVAL => \"invalid\",\n                raw::LIBSSH2_ERROR_INVALID_POLL_TYPE => \"invalid poll type\",\n                raw::LIBSSH2_ERROR_PUBLICKEY_PROTOCOL => \"public key protocol error\",\n                raw::LIBSSH2_ERROR_EAGAIN => \"operation would block\",\n                raw::LIBSSH2_ERROR_BUFFER_TOO_SMALL => \"buffer too small\",\n                raw::LIBSSH2_ERROR_BAD_USE => \"bad use error\",\n                raw::LIBSSH2_ERROR_COMPRESS => \"compression error\",\n                raw::LIBSSH2_ERROR_OUT_OF_BOUNDARY => \"out of bounds\",\n                raw::LIBSSH2_ERROR_AGENT_PROTOCOL => \"invalid agent protocol\",\n                raw::LIBSSH2_ERROR_SOCKET_RECV => \"error receiving on socket\",\n                raw::LIBSSH2_ERROR_ENCRYPT => \"bad encrypt\",\n                raw::LIBSSH2_ERROR_BAD_SOCKET => \"bad socket\",\n                raw::LIBSSH2_ERROR_KNOWN_HOSTS => \"known hosts error\",\n                raw::LIBSSH2_ERROR_CHANNEL_WINDOW_FULL => \"channel window full\",\n                raw::LIBSSH2_ERROR_KEYFILE_AUTH_FAILED => \"keyfile auth failed\",\n                raw::LIBSSH2_ERROR_RANDGEN => \"unable to get random bytes\",\n                raw::LIBSSH2_ERROR_MISSING_USERAUTH_BANNER => \"missing userauth banner\",\n                raw::LIBSSH2_ERROR_ALGO_UNSUPPORTED => \"algorithm unsupported\",\n                _ => \"unknown error\",\n            },\n            ErrorCode::SFTP(code) => match code {\n                raw::LIBSSH2_FX_EOF => \"end of file\",\n                raw::LIBSSH2_FX_NO_SUCH_FILE => \"no such file\",\n                raw::LIBSSH2_FX_PERMISSION_DENIED => \"permission denied\",\n                raw::LIBSSH2_FX_FAILURE => \"failure\",\n                raw::LIBSSH2_FX_BAD_MESSAGE => \"bad message\",\n                raw::LIBSSH2_FX_NO_CONNECTION => \"no connection\",\n                raw::LIBSSH2_FX_CONNECTION_LOST => \"connection lost\",\n                raw::LIBSSH2_FX_OP_UNSUPPORTED => \"operation unsupported\",\n                raw::LIBSSH2_FX_INVALID_HANDLE => \"invalid handle\",\n                raw::LIBSSH2_FX_NO_SUCH_PATH => \"no such path\",\n                raw::LIBSSH2_FX_FILE_ALREADY_EXISTS => \"file already exists\",\n                raw::LIBSSH2_FX_WRITE_PROTECT => \"file is write protected\",\n                raw::LIBSSH2_FX_NO_MEDIA => \"no media available\",\n                raw::LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM => \"no space on filesystem\",\n                raw::LIBSSH2_FX_QUOTA_EXCEEDED => \"quota exceeded\",\n                raw::LIBSSH2_FX_UNKNOWN_PRINCIPAL => \"unknown principal\",\n                raw::LIBSSH2_FX_LOCK_CONFLICT => \"lock conflict\",\n                raw::LIBSSH2_FX_DIR_NOT_EMPTY => \"directory not empty\",\n                raw::LIBSSH2_FX_NOT_A_DIRECTORY => \"not a directory\",\n                raw::LIBSSH2_FX_INVALID_FILENAME => \"invalid filename\",\n                raw::LIBSSH2_FX_LINK_LOOP => \"link loop\",\n                _ => \"unknown error\",\n            },\n        };\n        Error::new(code, msg)\n    }\n\n    /// Get the message corresponding to this error\n    pub fn message(&self) -> &str {\n        &*self.msg\n    }\n\n    /// Return the code for this error\n    pub fn code(&self) -> ErrorCode {\n        self.code\n    }\n}\n\nimpl From<Error> for io::Error {\n    fn from(err: Error) -> io::Error {\n        let kind = match err.code {\n            ErrorCode::Session(raw::LIBSSH2_ERROR_EAGAIN) => io::ErrorKind::WouldBlock,\n            ErrorCode::Session(raw::LIBSSH2_ERROR_TIMEOUT) => io::ErrorKind::TimedOut,\n            ErrorCode::SFTP(raw::LIBSSH2_FX_NO_SUCH_FILE)\n            | ErrorCode::SFTP(raw::LIBSSH2_FX_NO_SUCH_PATH) => io::ErrorKind::NotFound,\n            _ => io::ErrorKind::Other,\n        };\n        io::Error::new(kind, err.msg)\n    }\n}\n\nimpl fmt::Display for Error {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        write!(f, \"[{}] {}\", self.code, self.msg)\n    }\n}\n\nimpl error::Error for Error {\n    fn description(&self) -> &str {\n        self.message()\n    }\n}\n\nimpl From<NulError> for Error {\n    fn from(_: NulError) -> Error {\n        Error::new(\n            ErrorCode::Session(raw::LIBSSH2_ERROR_INVAL),\n            \"provided data contained a nul byte and could not be used \\\n             as as string\",\n        )\n    }\n}\n\nunsafe fn make_error_message(msg: *mut libc::c_char) -> Cow<'static, str> {\n    const FALLBACK: Cow<'_, str> = Cow::Borrowed(\"<failed to fetch the error message>\");\n    ::opt_bytes(&(), msg)\n        .and_then(|msg| {\n            str::from_utf8(msg)\n                .map(|msg| Cow::Owned(msg.to_owned()))\n                .ok()\n        })\n        .unwrap_or_else(|| FALLBACK)\n}\n"
  },
  {
    "path": "src/knownhosts.rs",
    "content": "use libc::{c_int, size_t};\nuse parking_lot::{Mutex, MutexGuard};\nuse std::ffi::CString;\nuse std::path::Path;\nuse std::ptr::null_mut;\nuse std::str;\nuse std::sync::Arc;\n\nuse util;\nuse {raw, CheckResult, Error, ErrorCode, KnownHostFileKind, SessionInner};\n\n/// A set of known hosts which can be used to verify the identity of a remote\n/// server.\n///\n/// # Example\n///\n/// ```no_run\n/// use std::env;\n/// use std::path::Path;\n/// use ssh2::{self, CheckResult, HostKeyType, KnownHostKeyFormat};\n/// use ssh2::KnownHostFileKind;\n///\n/// fn check_known_host(session: &ssh2::Session, host: &str) {\n///     let mut known_hosts = session.known_hosts().unwrap();\n///\n///     // Initialize the known hosts with a global known hosts file\n///     let file = Path::new(&env::var(\"HOME\").unwrap()).join(\".ssh/known_hosts\");\n///     known_hosts.read_file(&file, KnownHostFileKind::OpenSSH).unwrap();\n///\n///     // Now check to see if the seesion's host key is anywhere in the known\n///     // hosts file\n///     let (key, key_type) = session.host_key().unwrap();\n///     match known_hosts.check(host, key) {\n///         CheckResult::Match => return, // all good!\n///         CheckResult::NotFound => {}   // ok, we'll add it\n///         CheckResult::Mismatch => {\n///             panic!(\"host mismatch, man in the middle attack?!\")\n///         }\n///         CheckResult::Failure => panic!(\"failed to check the known hosts\"),\n///     }\n///\n///     println!(\"adding {} to the known hosts\", host);\n///\n///     known_hosts.add(host, key, host, key_type.into()).unwrap();\n///     known_hosts.write_file(&file, KnownHostFileKind::OpenSSH).unwrap();\n/// }\n/// ```\npub struct KnownHosts {\n    raw: *mut raw::LIBSSH2_KNOWNHOSTS,\n    sess: Arc<Mutex<SessionInner>>,\n}\n\n/// Structure representing a known host as part of a `KnownHosts` structure.\n#[derive(Debug, PartialEq, Eq)]\npub struct Host {\n    name: Option<String>,\n    key: String,\n}\n\nimpl KnownHosts {\n    pub(crate) fn from_raw_opt(\n        raw: *mut raw::LIBSSH2_KNOWNHOSTS,\n        err: Option<Error>,\n        sess: &Arc<Mutex<SessionInner>>,\n    ) -> Result<Self, Error> {\n        if raw.is_null() {\n            Err(err.unwrap_or_else(Error::unknown))\n        } else {\n            Ok(Self {\n                raw,\n                sess: Arc::clone(sess),\n            })\n        }\n    }\n\n    /// Reads a collection of known hosts from a specified file and adds them to\n    /// the collection of known hosts.\n    pub fn read_file(&mut self, file: &Path, kind: KnownHostFileKind) -> Result<u32, Error> {\n        let file = CString::new(util::path2bytes(file)?)?;\n        let sess = self.sess.lock();\n        let n = unsafe { raw::libssh2_knownhost_readfile(self.raw, file.as_ptr(), kind as c_int) };\n        if n < 0 {\n            sess.rc(n)?\n        }\n        Ok(n as u32)\n    }\n\n    /// Read a line as if it were from a known hosts file.\n    pub fn read_str(&mut self, s: &str, kind: KnownHostFileKind) -> Result<(), Error> {\n        let s = CString::new(s)?;\n        let s = s.as_bytes();\n        let sess = self.sess.lock();\n        sess.rc(unsafe {\n            raw::libssh2_knownhost_readline(\n                self.raw,\n                s.as_ptr() as *const _,\n                s.len() as size_t,\n                kind as c_int,\n            )\n        })\n    }\n\n    /// Writes all the known hosts to the specified file using the specified\n    /// file format.\n    pub fn write_file(&self, file: &Path, kind: KnownHostFileKind) -> Result<(), Error> {\n        let file = CString::new(util::path2bytes(file)?)?;\n        let sess = self.sess.lock();\n        let n = unsafe { raw::libssh2_knownhost_writefile(self.raw, file.as_ptr(), kind as c_int) };\n        sess.rc(n)\n    }\n\n    /// Converts a single known host to a single line of output for storage,\n    /// using the 'type' output format.\n    pub fn write_string(&self, host: &Host, kind: KnownHostFileKind) -> Result<String, Error> {\n        let mut v = Vec::with_capacity(128);\n        let sess = self.sess.lock();\n        let raw_host = self.resolve_to_raw_host(&sess, host)?.ok_or_else(|| {\n            Error::new(\n                ErrorCode::Session(raw::LIBSSH2_ERROR_BAD_USE),\n                \"Host is not in the set of known hosts\",\n            )\n        })?;\n        loop {\n            let mut outlen = 0;\n            unsafe {\n                let rc = raw::libssh2_knownhost_writeline(\n                    self.raw,\n                    raw_host,\n                    v.as_mut_ptr() as *mut _,\n                    v.capacity() as size_t,\n                    &mut outlen,\n                    kind as c_int,\n                );\n                if rc == raw::LIBSSH2_ERROR_BUFFER_TOO_SMALL {\n                    // + 1 for the trailing zero\n                    v.reserve(outlen as usize + 1);\n                } else {\n                    sess.rc(rc)?;\n                    v.set_len(outlen as usize);\n                    break;\n                }\n            }\n        }\n        Ok(String::from_utf8(v).unwrap())\n    }\n\n    /// Create an iterator over all of the known hosts in this structure.\n    pub fn iter(&self) -> Result<Vec<Host>, Error> {\n        self.hosts()\n    }\n\n    /// Retrieves the list of known hosts\n    pub fn hosts(&self) -> Result<Vec<Host>, Error> {\n        let mut next = null_mut();\n        let mut prev = null_mut();\n        let sess = self.sess.lock();\n        let mut hosts = vec![];\n\n        loop {\n            match unsafe { raw::libssh2_knownhost_get(self.raw, &mut next, prev) } {\n                0 => {\n                    prev = next;\n                    hosts.push(unsafe { Host::from_raw(next) });\n                }\n                1 => break,\n                rc => return Err(Error::from_session_error_raw(sess.raw, rc)),\n            }\n        }\n\n        Ok(hosts)\n    }\n\n    /// Given a Host object, find the matching raw node in the internal list.\n    /// The returned value is only valid while the session is locked.\n    fn resolve_to_raw_host(\n        &self,\n        sess: &MutexGuard<SessionInner>,\n        host: &Host,\n    ) -> Result<Option<*mut raw::libssh2_knownhost>, Error> {\n        let mut next = null_mut();\n        let mut prev = null_mut();\n\n        loop {\n            match unsafe { raw::libssh2_knownhost_get(self.raw, &mut next, prev) } {\n                0 => {\n                    prev = next;\n                    let current = unsafe { Host::from_raw(next) };\n                    if current == *host {\n                        return Ok(Some(next));\n                    }\n                }\n                1 => break,\n                rc => return Err(Error::from_session_error_raw(sess.raw, rc)),\n            }\n        }\n        Ok(None)\n    }\n\n    /// Delete a known host entry from the collection of known hosts.\n    pub fn remove(&self, host: &Host) -> Result<(), Error> {\n        let sess = self.sess.lock();\n\n        if let Some(raw_host) = self.resolve_to_raw_host(&sess, host)? {\n            return sess.rc(unsafe { raw::libssh2_knownhost_del(self.raw, raw_host) });\n        } else {\n            Ok(())\n        }\n    }\n\n    /// Checks a host and its associated key against the collection of known\n    /// hosts, and returns info back about the (partially) matched entry.\n    ///\n    /// The host name can be the IP numerical address of the host or the full\n    /// name. The key must be the raw data of the key.\n    pub fn check(&self, host: &str, key: &[u8]) -> CheckResult {\n        self.check_port_(host, -1, key)\n    }\n\n    /// Same as `check`, but takes a port as well.\n    pub fn check_port(&self, host: &str, port: u16, key: &[u8]) -> CheckResult {\n        self.check_port_(host, port as i32, key)\n    }\n\n    fn check_port_(&self, host: &str, port: i32, key: &[u8]) -> CheckResult {\n        let host = CString::new(host).unwrap();\n        let flags = raw::LIBSSH2_KNOWNHOST_TYPE_PLAIN | raw::LIBSSH2_KNOWNHOST_KEYENC_RAW;\n        unsafe {\n            let rc = raw::libssh2_knownhost_checkp(\n                self.raw,\n                host.as_ptr(),\n                port as c_int,\n                key.as_ptr() as *const _,\n                key.len() as size_t,\n                flags,\n                null_mut(),\n            );\n            match rc {\n                raw::LIBSSH2_KNOWNHOST_CHECK_MATCH => CheckResult::Match,\n                raw::LIBSSH2_KNOWNHOST_CHECK_MISMATCH => CheckResult::Mismatch,\n                raw::LIBSSH2_KNOWNHOST_CHECK_NOTFOUND => CheckResult::NotFound,\n                _ => CheckResult::Failure,\n            }\n        }\n    }\n\n    /// Adds a known host to the collection of known hosts.\n    ///\n    /// The host is the host name in plain text. The host name can be the IP\n    /// numerical address of the host or the full name. If you want to add a key\n    /// for a specific port number for the given host, you must provide the host\n    /// name like `\"[host]:port\"` with the actual characters `[` and `]` enclosing\n    /// the host name and a colon separating the host part from the port number.\n    /// For example: `\"[host.example.com]:222\"`.\n    ///\n    /// The key provided must be the raw key for the host.\n    pub fn add(\n        &mut self,\n        host: &str,\n        key: &[u8],\n        comment: &str,\n        fmt: ::KnownHostKeyFormat,\n    ) -> Result<(), Error> {\n        let host = CString::new(host)?;\n        let comment = CString::new(comment)?;\n        let flags =\n            raw::LIBSSH2_KNOWNHOST_TYPE_PLAIN | raw::LIBSSH2_KNOWNHOST_KEYENC_RAW | (fmt as c_int);\n        let sess = self.sess.lock();\n        unsafe {\n            let rc = raw::libssh2_knownhost_addc(\n                self.raw,\n                host.as_ptr() as *mut _,\n                null_mut(),\n                key.as_ptr() as *mut _,\n                key.len() as size_t,\n                comment.as_ptr() as *const _,\n                comment.as_bytes().len() as size_t,\n                flags,\n                null_mut(),\n            );\n            sess.rc(rc)\n        }\n    }\n}\n\nimpl Drop for KnownHosts {\n    fn drop(&mut self) {\n        let _sess = self.sess.lock();\n        unsafe { raw::libssh2_knownhost_free(self.raw) }\n    }\n}\n\nimpl Host {\n    /// This is `None` if no plain text host name exists.\n    pub fn name(&self) -> Option<&str> {\n        self.name.as_ref().map(String::as_str)\n    }\n\n    /// Returns the key in base64/printable format\n    pub fn key(&self) -> &str {\n        &self.key\n    }\n\n    unsafe fn from_raw(raw: *mut raw::libssh2_knownhost) -> Self {\n        let name = ::opt_bytes(&raw, (*raw).name).and_then(|s| String::from_utf8(s.to_vec()).ok());\n        let key = ::opt_bytes(&raw, (*raw).key).unwrap();\n        let key = String::from_utf8(key.to_vec()).unwrap();\n        Self { name, key }\n    }\n}\n"
  },
  {
    "path": "src/lib.rs",
    "content": "//! Rust bindings to libssh2, an SSH client library.\n//!\n//! This library intends to provide a safe interface to the libssh2 library. It\n//! will build the library if it's not available on the local system, and\n//! otherwise link to an installed copy.\n//!\n//! Note that libssh2 only supports SSH *clients*, not SSH *servers*.\n//! Additionally it only supports protocol v2, not protocol v1.\n//!\n//! # Examples\n//!\n//! ## Inspecting ssh-agent\n//!\n//! ```no_run\n//! use ssh2::Session;\n//!\n//! // Almost all APIs require a `Session` to be available\n//! let sess = Session::new().unwrap();\n//! let mut agent = sess.agent().unwrap();\n//!\n//! // Connect the agent and request a list of identities\n//! agent.connect().unwrap();\n//! agent.list_identities().unwrap();\n//!\n//! for identity in agent.identities().unwrap() {\n//!     println!(\"{}\", identity.comment());\n//!     let pubkey = identity.blob();\n//! }\n//! ```\n//!\n//! ## Authenticating with ssh-agent\n//!\n//! ```no_run\n//! use std::net::TcpStream;\n//! use ssh2::Session;\n//!\n//! // Connect to the local SSH server\n//! let tcp = TcpStream::connect(\"127.0.0.1:22\").unwrap();\n//! let mut sess = Session::new().unwrap();\n//! sess.set_tcp_stream(tcp);\n//! sess.handshake().unwrap();\n//!\n//! // Try to authenticate with the first identity in the agent.\n//! sess.userauth_agent(\"username\").unwrap();\n//!\n//! // Make sure we succeeded\n//! assert!(sess.authenticated());\n//! ```\n//!\n//! ## Authenticating with a password\n//!\n//! ```no_run\n//! use std::net::TcpStream;\n//! use ssh2::Session;\n//!\n//! // Connect to the local SSH server\n//! let tcp = TcpStream::connect(\"127.0.0.1:22\").unwrap();\n//! let mut sess = Session::new().unwrap();\n//! sess.set_tcp_stream(tcp);\n//! sess.handshake().unwrap();\n//!\n//! sess.userauth_password(\"username\", \"password\").unwrap();\n//! assert!(sess.authenticated());\n//! ```\n//!\n//! ## Run a command\n//!\n//! ```no_run\n//! use std::io::prelude::*;\n//! use std::net::TcpStream;\n//! use ssh2::Session;\n//!\n//! // Connect to the local SSH server\n//! let tcp = TcpStream::connect(\"127.0.0.1:22\").unwrap();\n//! let mut sess = Session::new().unwrap();\n//! sess.set_tcp_stream(tcp);\n//! sess.handshake().unwrap();\n//! sess.userauth_agent(\"username\").unwrap();\n//!\n//! let mut channel = sess.channel_session().unwrap();\n//! channel.exec(\"ls\").unwrap();\n//! let mut s = String::new();\n//! channel.read_to_string(&mut s).unwrap();\n//! println!(\"{}\", s);\n//! channel.wait_close().unwrap();\n//! println!(\"{}\", channel.exit_status().unwrap());\n//! ```\n//!\n//! ## Upload a file\n//!\n//! ```no_run\n//! use std::io::prelude::*;\n//! use std::net::TcpStream;\n//! use std::path::Path;\n//! use ssh2::Session;\n//!\n//! // Connect to the local SSH server\n//! let tcp = TcpStream::connect(\"127.0.0.1:22\").unwrap();\n//! let mut sess = Session::new().unwrap();\n//! sess.set_tcp_stream(tcp);\n//! sess.handshake().unwrap();\n//! sess.userauth_agent(\"username\").unwrap();\n//!\n//! // Write the file\n//! let mut remote_file = sess.scp_send(Path::new(\"remote\"),\n//!                                     0o644, 10, None).unwrap();\n//! remote_file.write(b\"1234567890\").unwrap();\n//! // Close the channel and wait for the whole content to be transferred\n//! remote_file.send_eof().unwrap();\n//! remote_file.wait_eof().unwrap();\n//! remote_file.close().unwrap();\n//! remote_file.wait_close().unwrap();\n//! ```\n//!\n//! ## Download a file\n//!\n//! ```no_run\n//! use std::io::prelude::*;\n//! use std::net::TcpStream;\n//! use std::path::Path;\n//! use ssh2::Session;\n//!\n//! // Connect to the local SSH server\n//! let tcp = TcpStream::connect(\"127.0.0.1:22\").unwrap();\n//! let mut sess = Session::new().unwrap();\n//! sess.set_tcp_stream(tcp);\n//! sess.handshake().unwrap();\n//! sess.userauth_agent(\"username\").unwrap();\n//!\n//! let (mut remote_file, stat) = sess.scp_recv(Path::new(\"remote\")).unwrap();\n//! println!(\"remote file size: {}\", stat.size());\n//! let mut contents = Vec::new();\n//! remote_file.read_to_end(&mut contents).unwrap();\n//!\n//! // Close the channel and wait for the whole content to be tranferred\n//! remote_file.send_eof().unwrap();\n//! remote_file.wait_eof().unwrap();\n//! remote_file.close().unwrap();\n//! remote_file.wait_close().unwrap();\n//! ```\n//!\n//! ## Execute a Netconf XML payload\n//!\n//! ```no_run\n//! use ssh2::{Channel, Session};\n//! use std::error::Error;\n//! use std::io::prelude::*;\n//! use std::net::TcpStream;\n//!\n//! const HELLO: &str = \"<hello xmlns=\\\"urn:ietf:params:xml:ns:netconf:base:1.0\\\">\n//!   <capabilities>\n//!     <capability>urn:ietf:params:netconf:base:1.1</capability>\n//!   </capabilities>\n//! </hello>\n//! ]]>]]>\";\n//!\n//! const PAYLOAD: &str = \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\n//!     <rpc xmlns=\\\"urn:ietf:params:xml:ns:netconf:base:1.1\\\" message-id=\\\"2\\\">\n//!     <cli xmlns=\\\"http://cisco.com/ns/yang/cisco-nx-os-device\\\"><mode>EXEC</mode><cmdline>show version</cmdline></cli>\n//! </rpc>\";\n//!\n//! fn read(channel: &mut Channel) -> Result<String, Box<dyn Error>> {\n//!     let mut result = String::new();\n//!     loop {\n//!         // If you plan to use this, be aware that reading 1 byte at a time is terribly\n//!         // inefficient and should be optimized for your usecase. This is just an example.\n//!         let mut buffer = [1u8; 1];\n//!         let bytes_read = channel.read(&mut buffer[..])?;\n//!         let s = String::from_utf8_lossy(&buffer[..bytes_read]);\n//!         result.push_str(&s);\n//!         if result.ends_with(\"]]>]]>\") {\n//!             println!(\"Found netconf 1.0 terminator, breaking read loop\");\n//!             break;\n//!         }\n//!         if result.ends_with(\"##\") {\n//!             println!(\"Found netconf 1.1 terminator, breaking read loop\");\n//!             break;\n//!         }\n//!         if bytes_read == 0 || channel.eof() {\n//!             println!(\"Buffer is empty, SSH channel read terminated\");\n//!             break;\n//!         }\n//!     }\n//!     Ok(result)\n//! }\n//!\n//! fn main() -> Result<(), Box<dyn Error>> {\n//!     let tcp = TcpStream::connect(\"127.0.0.1:830\")?;\n//!     let mut sess = Session::new()?;\n//!     sess.set_tcp_stream(tcp);\n//!     sess.handshake().unwrap();\n//!     sess.userauth_password(\"user\", \"pass\")?;\n//!\n//!     let mut channel = sess.channel_session()?;\n//!     channel.subsystem(\"netconf\")?;\n//!     let result = read(&mut channel)?;\n//!     println!(\"Result from connection:\\n{}\", result);\n//!\n//!     let payload = format!(\"{}\\n#{}\\n{}\\n##\\n\", HELLO, PAYLOAD.len(), PAYLOAD);\n//!     let a = channel.write(payload.as_bytes())?;\n//!     println!(\"Written {} bytes payload\", a);\n//!     let result = read(&mut channel)?;\n//!     println!(\"Result from payload execution:\\n{}\", result);\n//!\n//!     channel.send_eof()?;\n//!     channel.wait_eof()?;\n//!     channel.close()?;\n//!     channel.wait_close()?;\n//!     Ok(())\n//! }\n//! ```\n\n#![doc(html_root_url = \"https://docs.rs/ssh2\")]\n#![allow(trivial_numeric_casts)]\n#![deny(missing_docs, unused_results)]\n#![cfg_attr(test, deny(warnings))]\n\nextern crate libc;\nextern crate libssh2_sys as raw;\n#[macro_use]\nextern crate bitflags;\nextern crate parking_lot;\n\nuse std::ffi::CStr;\n\npub use agent::{Agent, PublicKey};\npub use channel::{Channel, ExitSignal, ReadWindow, Stream, WriteWindow};\npub use error::{Error, ErrorCode};\npub use knownhosts::{Host, KnownHosts};\npub use listener::Listener;\nuse session::SessionInner;\npub use session::{BlockDirections, KeyboardInteractivePrompt, Prompt, ScpFileStat, Session, TraceFlags};\npub use sftp::{File, FileStat, FileType, OpenType};\npub use sftp::{OpenFlags, RenameFlags, Sftp};\npub use DisconnectCode::{AuthCancelledByUser, TooManyConnections};\npub use DisconnectCode::{ByApplication, ConnectionLost, HostKeyNotVerifiable};\npub use DisconnectCode::{CompressionError, KeyExchangeFailed, MacError, Reserved};\npub use DisconnectCode::{HostNotAllowedToConnect, ProtocolError};\npub use DisconnectCode::{IllegalUserName, NoMoreAuthMethodsAvailable};\npub use DisconnectCode::{ProtocolVersionNotSupported, ServiceNotAvailable};\n\nmod agent;\nmod channel;\nmod error;\nmod knownhosts;\nmod listener;\nmod session;\nmod sftp;\nmod util;\n\n/// Initialize the libssh2 library.\n///\n/// This is optional, it is lazily invoked.\npub fn init() {\n    raw::init();\n}\n\nunsafe fn opt_bytes<'a, T>(_: &'a T, c: *const libc::c_char) -> Option<&'a [u8]> {\n    if c.is_null() {\n        None\n    } else {\n        Some(CStr::from_ptr(c).to_bytes())\n    }\n}\n\n#[allow(missing_docs)]\n#[derive(Copy, Clone)]\npub enum DisconnectCode {\n    HostNotAllowedToConnect = raw::SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT as isize,\n    ProtocolError = raw::SSH_DISCONNECT_PROTOCOL_ERROR as isize,\n    KeyExchangeFailed = raw::SSH_DISCONNECT_KEY_EXCHANGE_FAILED as isize,\n    Reserved = raw::SSH_DISCONNECT_RESERVED as isize,\n    MacError = raw::SSH_DISCONNECT_MAC_ERROR as isize,\n    CompressionError = raw::SSH_DISCONNECT_COMPRESSION_ERROR as isize,\n    ServiceNotAvailable = raw::SSH_DISCONNECT_SERVICE_NOT_AVAILABLE as isize,\n    ProtocolVersionNotSupported = raw::SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED as isize,\n    HostKeyNotVerifiable = raw::SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE as isize,\n    ConnectionLost = raw::SSH_DISCONNECT_CONNECTION_LOST as isize,\n    ByApplication = raw::SSH_DISCONNECT_BY_APPLICATION as isize,\n    TooManyConnections = raw::SSH_DISCONNECT_TOO_MANY_CONNECTIONS as isize,\n    AuthCancelledByUser = raw::SSH_DISCONNECT_AUTH_CANCELLED_BY_USER as isize,\n    NoMoreAuthMethodsAvailable = raw::SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE as isize,\n    IllegalUserName = raw::SSH_DISCONNECT_ILLEGAL_USER_NAME as isize,\n}\n\n#[allow(missing_docs)]\n#[derive(Copy, Clone, Debug)]\npub enum HostKeyType {\n    Unknown = raw::LIBSSH2_HOSTKEY_TYPE_UNKNOWN as isize,\n    Rsa = raw::LIBSSH2_HOSTKEY_TYPE_RSA as isize,\n    Dss = raw::LIBSSH2_HOSTKEY_TYPE_DSS as isize,\n    Ecdsa256 = raw::LIBSSH2_HOSTKEY_TYPE_ECDSA_256 as isize,\n    Ecdsa384 = raw::LIBSSH2_HOSTKEY_TYPE_ECDSA_384 as isize,\n    Ecdsa521 = raw::LIBSSH2_HOSTKEY_TYPE_ECDSA_521 as isize,\n    Ed25519 = raw::LIBSSH2_HOSTKEY_TYPE_ED25519 as isize,\n}\n\n#[allow(missing_docs)]\n#[derive(Copy, Clone)]\npub enum MethodType {\n    Kex = raw::LIBSSH2_METHOD_KEX as isize,\n    HostKey = raw::LIBSSH2_METHOD_HOSTKEY as isize,\n    CryptCs = raw::LIBSSH2_METHOD_CRYPT_CS as isize,\n    CryptSc = raw::LIBSSH2_METHOD_CRYPT_SC as isize,\n    MacCs = raw::LIBSSH2_METHOD_MAC_CS as isize,\n    MacSc = raw::LIBSSH2_METHOD_MAC_SC as isize,\n    CompCs = raw::LIBSSH2_METHOD_COMP_CS as isize,\n    CompSc = raw::LIBSSH2_METHOD_COMP_SC as isize,\n    LangCs = raw::LIBSSH2_METHOD_LANG_CS as isize,\n    LangSc = raw::LIBSSH2_METHOD_LANG_SC as isize,\n    SignAlgo = raw::LIBSSH2_METHOD_SIGN_ALGO as isize,\n}\n\n/// When passed to `Channel::flush_stream`, flushes all extended data\n/// substreams.\npub static FLUSH_EXTENDED_DATA: i32 = -1;\n/// When passed to `Channel::flush_stream`, flushes all substream.\npub static FLUSH_ALL: i32 = -2;\n/// Stream ID of the stderr channel for stream-related methods on `Channel`\npub static EXTENDED_DATA_STDERR: i32 = 1;\n\n#[allow(missing_docs)]\n#[derive(Copy, Clone, Debug)]\npub enum HashType {\n    Md5 = raw::LIBSSH2_HOSTKEY_HASH_MD5 as isize,\n    Sha1 = raw::LIBSSH2_HOSTKEY_HASH_SHA1 as isize,\n    Sha256 = raw::LIBSSH2_HOSTKEY_HASH_SHA256 as isize,\n}\n\n#[allow(missing_docs)]\n#[derive(Copy, Clone, Debug)]\npub enum KnownHostFileKind {\n    OpenSSH = raw::LIBSSH2_KNOWNHOST_FILE_OPENSSH as isize,\n}\n\n/// Possible results of a call to `KnownHosts::check`\n#[derive(Copy, Clone, Debug)]\npub enum CheckResult {\n    /// Hosts and keys match\n    Match = raw::LIBSSH2_KNOWNHOST_CHECK_MATCH as isize,\n    /// Host was found, but the keys didn't match!\n    Mismatch = raw::LIBSSH2_KNOWNHOST_CHECK_MISMATCH as isize,\n    /// No host match was found\n    NotFound = raw::LIBSSH2_KNOWNHOST_CHECK_NOTFOUND as isize,\n    /// Something prevented the check to be made\n    Failure = raw::LIBSSH2_KNOWNHOST_CHECK_FAILURE as isize,\n}\n\n#[allow(missing_docs)]\n#[derive(Copy, Clone, Debug)]\npub enum KnownHostKeyFormat {\n    Unknown = raw::LIBSSH2_KNOWNHOST_KEY_UNKNOWN as isize,\n    Rsa1 = raw::LIBSSH2_KNOWNHOST_KEY_RSA1 as isize,\n    SshRsa = raw::LIBSSH2_KNOWNHOST_KEY_SSHRSA as isize,\n    SshDss = raw::LIBSSH2_KNOWNHOST_KEY_SSHDSS as isize,\n    Ecdsa256 = raw::LIBSSH2_KNOWNHOST_KEY_ECDSA_256 as isize,\n    Ecdsa384 = raw::LIBSSH2_KNOWNHOST_KEY_ECDSA_384 as isize,\n    Ecdsa521 = raw::LIBSSH2_KNOWNHOST_KEY_ECDSA_521 as isize,\n    Ed25519 = raw::LIBSSH2_KNOWNHOST_KEY_ED25519 as isize,\n}\n\nimpl From<HostKeyType> for KnownHostKeyFormat {\n    fn from(host_type: HostKeyType) -> KnownHostKeyFormat {\n        match host_type {\n            HostKeyType::Unknown => KnownHostKeyFormat::Unknown,\n            HostKeyType::Rsa => KnownHostKeyFormat::SshRsa,\n            HostKeyType::Dss => KnownHostKeyFormat::SshDss,\n            HostKeyType::Ecdsa256 => KnownHostKeyFormat::Ecdsa256,\n            HostKeyType::Ecdsa384 => KnownHostKeyFormat::Ecdsa384,\n            HostKeyType::Ecdsa521 => KnownHostKeyFormat::Ecdsa521,\n            HostKeyType::Ed25519 => KnownHostKeyFormat::Ed25519,\n        }\n    }\n}\n\n/// How to handle extended data streams, such as stderr\n#[derive(Copy, Clone, Debug)]\npub enum ExtendedData {\n    /// Queue extended data for eventual reading\n    Normal = raw::LIBSSH2_CHANNEL_EXTENDED_DATA_NORMAL as isize,\n    /// Treat extended data and ordinary data the same. Merge all substreams such that calls to\n    /// read will pull from all substreams on a first-in/first-out basis.\n    Merge = raw::LIBSSH2_CHANNEL_EXTENDED_DATA_MERGE as isize,\n    /// Discard all extended data as it arrives.\n    Ignore = raw::LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE as isize,\n}\n\n/// The modes described in <https://tools.ietf.org/html/rfc4250#section-4.5.2>\n#[allow(non_camel_case_types)]\n#[derive(Debug, Clone, Copy, Eq, PartialEq)]\npub enum PtyModeOpcode {\n    /// Indicates end of options.\n    TTY_OP_END = 0,\n    /// Interrupt character; 255 if none.  Similarly for the other characters.  Not all of these characters are supported on all systems.\n    VINTR = 1,\n    /// The quit character (sends SIGQUIT signal on POSIX systems).\n    VQUIT = 2,\n    /// Erase the character to left of the cursor.\n    VERASE = 3,\n    /// Kill the current input line.\n    VKILL = 4,\n    /// End-of-file character (sends EOF from the terminal).\n    VEOF = 5,\n    /// End-of-line character in addition to carriage return and/or linefeed.\n    VEOL = 6,\n    /// Additional end-of-line character.\n    VEOL2 = 7,\n    /// Continues paused output (normally control-Q).\n    VSTART = 8,\n    /// Pauses output (normally control-S).\n    VSTOP = 9,\n    /// Suspends the current program.\n    VSUSP = 10,\n    /// Another suspend character.\n    VDSUSP = 11,\n    /// Reprints the current input line.\n    VREPRINT = 12,\n    /// Erases a word left of cursor.\n    VWERASE = 13,\n    /// Enter the next character typed literally, even if it is a special character\n    VLNEXT = 14,\n    /// Character to flush output.\n    VFLUSH = 15,\n    /// Switch to a different shell layer.\n    VSWTCH = 16,\n    /// Prints system status line (load, command, pid, etc).\n    VSTATUS = 17,\n    /// Toggles the flushing of terminal output.\n    VDISCARD = 18,\n    /// The ignore parity flag.  The parameter SHOULD be 0 if this flag is FALSE, and 1 if it is TRUE.\n    IGNPAR = 30,\n    /// Mark parity and framing errors.\n    PARMRK = 31,\n    /// Enable checking of parity errors.\n    INPCK = 32,\n    /// Strip 8th bit off characters.\n    ISTRIP = 33,\n    /// Map NL into CR on input.\n    INLCR = 34,\n    /// Ignore CR on input.\n    IGNCR = 35,\n    /// Map CR to NL on input.\n    ICRNL = 36,\n    /// Translate uppercase characters to lowercase.\n    IUCLC = 37,\n    /// Enable output flow control.\n    IXON = 38,\n    /// Any char will restart after stop.\n    IXANY = 39,\n    /// Enable input flow control.\n    IXOFF = 49,\n    /// Ring bell on input queue full.\n    IMAXBEL = 41,\n    /// Enable signals INTR, QUIT, [D]SUSP.\n    ISIG = 50,\n    /// Canonicalize input lines.\n    ICANON = 51,\n\n    /// Enable input and output of uppercase characters by preceding their lowercase equivalents with \"\\\".\n    XCASE = 52,\n    /// Enable echoing.\n    ECHO = 53,\n    /// Visually erase chars.\n    ECHOE = 54,\n    /// Kill character discards current line.\n    ECHOK = 55,\n    /// Echo NL even if ECHO is off.\n    ECHONL = 56,\n    /// Don't flush after interrupt.\n    NOFLSH = 57,\n    /// Stop background jobs from output.\n    TOSTOP = 58,\n    /// Enable extensions.\n    IEXTEN = 59,\n    /// Echo control characters as ^(Char).\n    ECHOCTL = 60,\n    /// Visual erase for line kill.\n    ECHOKE = 61,\n    /// Retype pending input.\n    PENDIN = 62,\n    /// Enable output processing.\n    OPOST = 70,\n    /// Convert lowercase to uppercase.\n    OLCUC = 71,\n    /// Map NL to CR-NL.\n    ONLCR = 72,\n    /// Translate carriage return to newline (output).\n    OCRNL = 73,\n    /// Translate newline to carriage return-newline (output).\n    ONOCR = 74,\n    /// Newline performs a carriage return (output).\n    ONLRET = 75,\n    /// 7 bit mode.\n    CS7 = 90,\n    /// 8 bit mode.\n    CS8 = 91,\n    /// Parity enable.\n    PARENB = 92,\n    /// Odd parity, else even.\n    PARODD = 93,\n\n    /// Specifies the input baud rate in bits per second.\n    TTY_OP_ISPEED = 128,\n    /// Specifies the output baud rate in bits per second.\n    TTY_OP_OSPEED = 129,\n}\n\n/// An opcode for setting a Pty terminal mode\n#[derive(Debug, Clone, Copy, Eq, PartialEq)]\npub enum ExtensiblePtyModeOpcode {\n    /// Use one of the modes specified by RFC 4250\n    Mode(PtyModeOpcode),\n    /// Use a mode not reflected by RFC 4250\n    Extended(u8),\n}\n\nimpl From<PtyModeOpcode> for ExtensiblePtyModeOpcode {\n    fn from(op: PtyModeOpcode) -> ExtensiblePtyModeOpcode {\n        ExtensiblePtyModeOpcode::Mode(op)\n    }\n}\n\nimpl From<u8> for ExtensiblePtyModeOpcode {\n    fn from(op: u8) -> ExtensiblePtyModeOpcode {\n        ExtensiblePtyModeOpcode::Extended(op)\n    }\n}\n\nimpl ExtensiblePtyModeOpcode {\n    fn as_opcode(&self) -> u8 {\n        match self {\n            ExtensiblePtyModeOpcode::Mode(m) => *m as u8,\n            ExtensiblePtyModeOpcode::Extended(op) => *op,\n        }\n    }\n}\n\n/// Encodes modes for Pty allocation requests.\n/// The modes documented in <https://tools.ietf.org/html/rfc4250#section-4.5>\n/// are supported.\n#[derive(Debug, Clone)]\npub struct PtyModes {\n    data: Vec<u8>,\n}\n\nimpl PtyModes {\n    /// Construct a PtyModes instance so that you can specify values for\n    /// various modes\n    pub fn new() -> Self {\n        Self { data: vec![] }\n    }\n\n    /// Set a mode to an arbitrary u32 value\n    pub fn set_u32<O: Into<ExtensiblePtyModeOpcode>>(&mut self, option: O, value: u32) {\n        let data = [\n            option.into().as_opcode(),\n            ((value >> 24) & 0xff) as u8,\n            ((value >> 16) & 0xff) as u8,\n            ((value >> 8) & 0xff) as u8,\n            (value & 0xff) as u8,\n        ];\n        self.data.extend_from_slice(&data);\n    }\n\n    /// Set a mode to a boolean value\n    pub fn set_boolean<O: Into<ExtensiblePtyModeOpcode>>(&mut self, option: O, value: bool) {\n        self.set_u32(option, if value { 1 } else { 0 })\n    }\n\n    /// Set a mode to a character value.\n    /// If the character is None it is set to 255 to indicate that it\n    /// is disabled.\n    /// While this interface and the protocol accept unicode characters\n    /// of up to 32 bits in width, these options likely only work for\n    /// characters in the 7-bit ascii range.\n    pub fn set_character<O: Into<ExtensiblePtyModeOpcode>>(&mut self, option: O, c: Option<char>) {\n        self.set_u32(option, c.map(|c| c as u32).unwrap_or(255))\n    }\n\n    /// Finish accumulating modes and return the encoded\n    /// byte stream suitable for use in the ssh2 protocol\n    pub fn finish(mut self) -> Vec<u8> {\n        self.data.push(PtyModeOpcode::TTY_OP_END as u8);\n        self.data\n    }\n}\n"
  },
  {
    "path": "src/listener.rs",
    "content": "use parking_lot::Mutex;\nuse std::sync::Arc;\nuse {raw, Channel, Error, SessionInner};\n\n/// A listener represents a forwarding port from the remote server.\n///\n/// New channels can be accepted from a listener which represent connections on\n/// the remote server's port.\npub struct Listener {\n    raw: *mut raw::LIBSSH2_LISTENER,\n    sess: Arc<Mutex<SessionInner>>,\n}\n\n// Listener is both Send and Sync; the compiler can't see it because it\n// is pessimistic about the raw pointer.  We use Arc/Mutex to guard accessing\n// the raw pointer so we are safe for both.\nunsafe impl Send for Listener {}\nunsafe impl Sync for Listener {}\n\nimpl Listener {\n    /// Accept a queued connection from this listener.\n    pub fn accept(&mut self) -> Result<Channel, Error> {\n        let sess = self.sess.lock();\n        unsafe {\n            let chan = raw::libssh2_channel_forward_accept(self.raw);\n            let err = sess.last_error();\n            Channel::from_raw_opt(chan, err, &self.sess)\n        }\n    }\n\n    pub(crate) fn from_raw_opt(\n        raw: *mut raw::LIBSSH2_LISTENER,\n        err: Option<Error>,\n        sess: &Arc<Mutex<SessionInner>>,\n    ) -> Result<Self, Error> {\n        if raw.is_null() {\n            Err(err.unwrap_or_else(Error::unknown))\n        } else {\n            Ok(Self {\n                raw,\n                sess: Arc::clone(sess),\n            })\n        }\n    }\n}\n\nimpl Drop for Listener {\n    fn drop(&mut self) {\n        let _sess = self.sess.lock();\n        unsafe {\n            let _ = raw::libssh2_channel_forward_cancel(self.raw);\n        }\n    }\n}\n"
  },
  {
    "path": "src/session.rs",
    "content": "// Usings for openssl function userauth_pubkey_memory()\n#[cfg(any(unix, feature = \"vendored-openssl\", feature = \"openssl-on-win32\"))]\nuse libc::size_t;\nuse libc::{self, c_char, c_int, c_long, c_uint, c_void};\nuse parking_lot::{MappedMutexGuard, Mutex, MutexGuard};\nuse std::borrow::Cow;\nuse std::ffi::CString;\nuse std::ptr::{null, null_mut};\nuse std::mem;\n#[cfg(unix)]\nuse std::os::unix::io::{AsRawFd, RawFd};\n#[cfg(windows)]\nuse std::os::windows::io::{AsRawSocket, RawSocket};\nuse std::path::Path;\nuse std::slice;\nuse std::str;\nuse std::sync::Arc;\n\nuse util;\nuse {raw, ByApplication, DisconnectCode, Error, ErrorCode, HostKeyType};\nuse {Agent, Channel, HashType, KnownHosts, Listener, MethodType, Sftp};\n\nbitflags! {\n    /// Flags which can be used with the session trace method to set\n    /// the trace level.\n    #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]\n    pub struct TraceFlags: c_int {\n        /// Authentication debugging\n        const AUTH      = raw::LIBSSH2_TRACE_AUTH;\n        /// Connection layer debugging\n        const CONN      = raw::LIBSSH2_TRACE_CONN;\n        /// Error debugging\n        const ERROR     = raw::LIBSSH2_TRACE_ERROR;\n        /// Key exchange debugging\n        const KEX       = raw::LIBSSH2_TRACE_KEX;\n        /// Public Key Debugging\n        const PUBLICKEY = raw::LIBSSH2_TRACE_PUBLICKEY;\n        /// SCP debugging\n        const SCP       = raw::LIBSSH2_TRACE_SCP;\n        /// SFTP debugging\n        const SFTP      = raw::LIBSSH2_TRACE_SFTP;\n        /// Socket low-level debugging\n        const SOCKET    = raw::LIBSSH2_TRACE_SOCKET;\n        /// Transport layer debugging\n        const TRANS     = raw::LIBSSH2_TRACE_TRANS;\n    }\n}\n\n/// Called by libssh2 to respond to some number of challenges as part of\n/// keyboard interactive authentication.\npub trait KeyboardInteractivePrompt {\n    /// `username` is the user name to be authenticated. It may not be the\n    /// same as the username passed to `Session::userauth_keyboard_interactive`,\n    /// and may be empty.\n    /// `instructions` is some informational text to be displayed to the user.\n    /// `prompts` is a series of prompts (or challenges) that must be responded\n    /// to.\n    /// The return value should be a Vec that holds one response for each prompt.\n    fn prompt<'a>(\n        &mut self,\n        username: &str,\n        instructions: &str,\n        prompts: &[Prompt<'a>],\n    ) -> Vec<String>;\n}\n\n/// A prompt/challenge returned as part of keyboard-interactive authentication\n#[derive(Debug)]\npub struct Prompt<'a> {\n    /// The label to show when prompting the user\n    pub text: Cow<'a, str>,\n    /// If true, the response that the user inputs should be displayed\n    /// as they type.  If false then treat it as a password entry and\n    /// do not display what is typed in response to this prompt.\n    pub echo: bool,\n}\n\n/// This is a little helper function that is perhaps slightly overkill for the\n/// current needs.\n/// It saves the current sess->abstract pointer and replaces it with a\n/// different values for the duration of the call to the supplied lambda.\n/// When the lambda returns, the original abstract value is restored\n/// and the result of the lambda is returned.\nunsafe fn with_abstract<R, F: FnOnce() -> R>(\n    sess: *mut raw::LIBSSH2_SESSION,\n    new_value: *mut c_void,\n    f: F,\n) -> R {\n    let abstrakt = raw::libssh2_session_abstract(sess);\n    let old_value = *abstrakt;\n    *abstrakt = new_value;\n    let res = f();\n    *abstrakt = old_value;\n    res\n}\n\npub(crate) struct SessionInner {\n    pub(crate) raw: *mut raw::LIBSSH2_SESSION,\n    #[cfg(unix)]\n    tcp: Option<Box<dyn AsRawFd>>,\n    #[cfg(windows)]\n    tcp: Option<Box<dyn AsRawSocket>>,\n}\n\n// The compiler doesn't know that it is Send safe because of the raw\n// pointer inside.  We know that the way that it is used by libssh2\n// and this crate is Send safe.\nunsafe impl Send for SessionInner {}\n\n/// An SSH session, typically representing one TCP connection.\n///\n/// All other structures are based on an SSH session and cannot outlive a\n/// session. Sessions are created and then have the TCP socket handed to them\n/// (via the `set_tcp_stream` method).\n///\n/// `Session`, and any objects its methods return, hold a reference to the underlying\n/// SSH session.  You may clone `Session` to obtain another handle referencing\n/// the same session, and create multiple `Channel` and `Stream` objects\n/// from that same underlying session, which can all be passed across thread\n/// boundaries (they are `Send` and `Sync`).  These are all related objects and\n/// are internally synchronized via a `Mutex` to make it safe to pass them\n/// around in this way.\n///\n/// This means that a blocking read from a `Channel` or `Stream` will block\n/// all other calls on objects created from the same underlying `Session`.\n/// If you need the ability to perform concurrent operations then you will\n/// need to create separate `Session` instances, or employ non-blocking mode.\n#[derive(Clone)]\npub struct Session {\n    inner: Arc<Mutex<SessionInner>>,\n}\n\n/// Metadata returned about a remote file when received via `scp`.\npub struct ScpFileStat {\n    stat: libc::stat,\n}\n\n/// The io direction an application has to wait for in order not to block.\n#[derive(Debug, PartialEq)]\npub enum BlockDirections {\n    /// No direction blocked.\n    None,\n    /// Inbound direction blocked.\n    Inbound,\n    /// Outbound direction blockd.\n    Outbound,\n    /// Inbound and Outbound direction blocked.\n    Both,\n}\n\nimpl Session {\n    /// Initializes an SSH session object.\n    ///\n    /// This function does not associate the session with a remote connection\n    /// just yet. Various configuration options can be set such as the blocking\n    /// mode, compression, sigpipe, the banner, etc. To associate this session\n    /// with a TCP connection, use the `set_tcp_stream` method pass in an\n    /// already-established TCP socket, and then follow up with a call to\n    /// `handshake` to perform the ssh protocol handshake.\n    pub fn new() -> Result<Session, Error> {\n        ::init();\n        unsafe {\n            let ret = raw::libssh2_session_init_ex(None, None, None, null_mut());\n            if ret.is_null() {\n                Err(Error::unknown())\n            } else {\n                Ok(Session {\n                    inner: Arc::new(Mutex::new(SessionInner {\n                        raw: ret,\n                        tcp: None,\n                    })),\n                })\n            }\n        }\n    }\n\n    #[doc(hidden)]\n    pub fn raw(&self) -> MappedMutexGuard<'_, raw::LIBSSH2_SESSION> {\n        let inner = self.inner();\n        MutexGuard::map(inner, |inner| unsafe { &mut *inner.raw })\n    }\n\n    /// Set the SSH protocol banner for the local client\n    ///\n    /// Set the banner that will be sent to the remote host when the SSH session\n    /// is started with handshake(). This is optional; a banner\n    /// corresponding to the protocol and libssh2 version will be sent by\n    /// default.\n    pub fn set_banner(&self, banner: &str) -> Result<(), Error> {\n        let banner = CString::new(banner)?;\n        let inner = self.inner();\n        unsafe { inner.rc(raw::libssh2_session_banner_set(inner.raw, banner.as_ptr())) }\n    }\n\n    /// Flag indicating whether SIGPIPE signals will be allowed or blocked.\n    ///\n    /// By default (on relevant platforms) this library will attempt to block\n    /// and catch SIGPIPE signals. Setting this flag to `true` will cause\n    /// the library to not attempt to block SIGPIPE from the underlying socket\n    /// layer.\n    pub fn set_allow_sigpipe(&self, block: bool) {\n        let inner = self.inner();\n        let res = unsafe {\n            inner.rc(raw::libssh2_session_flag(\n                inner.raw,\n                raw::LIBSSH2_FLAG_SIGPIPE as c_int,\n                block as c_int,\n            ))\n        };\n        res.unwrap();\n    }\n\n    /// Flag indicating whether this library will attempt to negotiate\n    /// compression.\n    ///\n    /// If set - before the connection negotiation is performed - libssh2 will\n    /// try to negotiate compression enabling for this connection. By default\n    /// libssh2 will not attempt to use compression.\n    pub fn set_compress(&self, compress: bool) {\n        let inner = self.inner();\n        let res = unsafe {\n            inner.rc(raw::libssh2_session_flag(\n                inner.raw,\n                raw::LIBSSH2_FLAG_COMPRESS as c_int,\n                compress as c_int,\n            ))\n        };\n        res.unwrap();\n    }\n\n    /// Set or clear blocking mode on session\n    ///\n    /// This will instantly affect any channels associated with this session. If\n    /// a read is performed on a session with no data currently available, a\n    /// blocking session will wait for data to arrive and return what it\n    /// receives. A non-blocking session will return immediately with an empty\n    /// buffer. If a write is performed on a session with no room for more data,\n    /// a blocking session will wait for room. A non-blocking session will\n    /// return immediately without writing anything.\n    pub fn set_blocking(&self, blocking: bool) {\n        self.inner().set_blocking(blocking);\n    }\n\n    /// Returns whether the session was previously set to nonblocking.\n    pub fn is_blocking(&self) -> bool {\n        self.inner().is_blocking()\n    }\n\n    /// Set timeout for blocking functions.\n    ///\n    /// Set the timeout in milliseconds for how long a blocking the libssh2\n    /// function calls may wait until they consider the situation an error and\n    /// return an error.\n    ///\n    /// By default or if you set the timeout to zero, libssh2 has no timeout\n    /// for blocking functions.\n    pub fn set_timeout(&self, timeout_ms: u32) {\n        let timeout_ms = timeout_ms as c_long;\n        let inner = self.inner();\n        unsafe { raw::libssh2_session_set_timeout(inner.raw, timeout_ms) }\n    }\n\n    /// Returns the timeout, in milliseconds, for how long blocking calls may\n    /// wait until they time out.\n    ///\n    /// A timeout of 0 signifies no timeout.\n    pub fn timeout(&self) -> u32 {\n        let inner = self.inner();\n        unsafe { raw::libssh2_session_get_timeout(inner.raw) as u32 }\n    }\n\n    /// Begin transport layer protocol negotiation with the connected host.\n    ///\n    /// You must call this after associating the session with a tcp stream\n    /// via the `set_tcp_stream` function.\n    pub fn handshake(&mut self) -> Result<(), Error> {\n        #[cfg(windows)]\n        unsafe fn handshake(\n            raw: *mut raw::LIBSSH2_SESSION,\n            stream: &dyn AsRawSocket,\n        ) -> libc::c_int {\n            raw::libssh2_session_handshake(raw, stream.as_raw_socket())\n        }\n\n        #[cfg(unix)]\n        unsafe fn handshake(raw: *mut raw::LIBSSH2_SESSION, stream: &dyn AsRawFd) -> libc::c_int {\n            raw::libssh2_session_handshake(raw, stream.as_raw_fd())\n        }\n\n        let inner = self.inner();\n\n        unsafe {\n            let stream = inner.tcp.as_ref().ok_or_else(|| {\n                Error::new(\n                    ErrorCode::Session(raw::LIBSSH2_ERROR_BAD_SOCKET),\n                    \"use set_tcp_stream() to associate with a TcpStream\",\n                )\n            })?;\n\n            inner.rc(handshake(inner.raw, stream.as_ref()))\n        }\n    }\n\n    /// The session takes ownership of the stream provided.\n    /// You may use the `AsRawFd` (unix) or `AsRawSocket` (windows) traits\n    /// to obtain the raw fd later if required.\n    ///\n    /// It is also highly recommended that the stream provided is not used\n    /// concurrently elsewhere for the duration of this session as it may\n    /// interfere with the protocol.\n    #[cfg(unix)]\n    pub fn set_tcp_stream<S: 'static + AsRawFd>(&mut self, stream: S) {\n        let mut inner = self.inner();\n        let _ = inner.tcp.replace(Box::new(stream));\n    }\n\n    /// The session takes ownership of the stream provided.\n    /// You may use the tcp_stream() method to obtain the raw socket later.\n    ///\n    /// It is also highly recommended that the stream provided is not used\n    /// concurrently elsewhere for the duration of this session as it may\n    /// interfere with the protocol.\n    #[cfg(windows)]\n    pub fn set_tcp_stream<S: 'static + AsRawSocket>(&mut self, stream: S) {\n        let mut inner = self.inner();\n        let _ = inner.tcp.replace(Box::new(stream));\n    }\n\n    /// Attempt basic password authentication.\n    ///\n    /// Note that many SSH servers which appear to support ordinary password\n    /// authentication actually have it disabled and use Keyboard Interactive\n    /// authentication (routed via PAM or another authentication backed)\n    /// instead.\n    pub fn userauth_password(&self, username: &str, password: &str) -> Result<(), Error> {\n        let username = CString::new(username)?;\n        let username = username.as_bytes();\n        let password = CString::new(password)?;\n        let password = password.as_bytes();\n        let inner = self.inner();\n        inner.rc(unsafe {\n            raw::libssh2_userauth_password_ex(\n                inner.raw,\n                username.as_ptr() as *const _,\n                username.len() as c_uint,\n                password.as_ptr() as *const _,\n                password.len() as c_uint,\n                None,\n            )\n        })\n    }\n\n    /// Attempt keyboard interactive authentication.\n    ///\n    /// You must supply a callback function to\n    pub fn userauth_keyboard_interactive<P: KeyboardInteractivePrompt>(\n        &self,\n        username: &str,\n        prompter: &mut P,\n    ) -> Result<(), Error> {\n        // hold on to your hats, this is a bit involved.\n        // The keyboard interactive callback is a bit tricksy, and we want to wrap the\n        // raw C types with something a bit safer and more ergonomic.\n        // Since the interface is defined in terms of a simple function pointer, wrapping\n        // is a bit awkward.\n        //\n        // The session struct has an abstrakt pointer reserved for\n        // the user of the embedding application, and that pointer is passed to the\n        // prompt callback. We can use this to store a pointer to some state so that\n        // we can manage the conversion.\n        //\n        // The prompts and responses are defined to be UTF-8, but we use from_utf8_lossy\n        // to avoid panics in case the server isn't conformant for whatever reason.\n        extern \"C\" fn prompt<P: KeyboardInteractivePrompt>(\n            username: *const c_char,\n            username_len: c_int,\n            instruction: *const c_char,\n            instruction_len: c_int,\n            num_prompts: c_int,\n            prompts: *const raw::LIBSSH2_USERAUTH_KBDINT_PROMPT,\n            responses: *mut raw::LIBSSH2_USERAUTH_KBDINT_RESPONSE,\n            abstrakt: *mut *mut c_void,\n        ) {\n            use std::panic::{catch_unwind, AssertUnwindSafe};\n            // Catch panics; we can't let them unwind to C code.\n            // There's not much to be done with them though because the\n            // signature of the callback doesn't allow reporting an error.\n            let _ = catch_unwind(AssertUnwindSafe(|| {\n                if prompts.is_null() || responses.is_null() || num_prompts < 0 {\n                    return;\n                }\n\n                let prompter = unsafe { &mut **(abstrakt as *mut *mut P) };\n\n                let username = if !username.is_null() && username_len >= 0 {\n                    let username = unsafe {\n                        slice::from_raw_parts(username as *const u8, username_len as usize)\n                    };\n                    String::from_utf8_lossy(username)\n                } else {\n                    Cow::Borrowed(\"\")\n                };\n\n                let instruction = if !instruction.is_null() && instruction_len >= 0 {\n                    let instruction = unsafe {\n                        slice::from_raw_parts(instruction as *const u8, instruction_len as usize)\n                    };\n                    String::from_utf8_lossy(instruction)\n                } else {\n                    Cow::Borrowed(\"\")\n                };\n\n                let prompts = unsafe { slice::from_raw_parts(prompts, num_prompts as usize) };\n                let responses =\n                    unsafe { slice::from_raw_parts_mut(responses, num_prompts as usize) };\n\n                let prompts: Vec<Prompt> = prompts\n                    .iter()\n                    .map(|item| {\n                        let data = unsafe {\n                            slice::from_raw_parts(item.text as *const u8, item.length as usize)\n                        };\n                        Prompt {\n                            text: String::from_utf8_lossy(data),\n                            echo: item.echo != 0,\n                        }\n                    })\n                    .collect();\n\n                // libssh2 wants to be able to free(3) the response strings, so allocate\n                // storage and copy the responses into appropriately owned memory.\n                // We can't simply call strdup(3) here because the rust string types\n                // are not NUL terminated.\n                fn strdup_string(s: &str) -> *mut c_char {\n                    let len = s.len();\n                    let ptr = unsafe { libc::malloc(len + 1) as *mut c_char };\n                    if !ptr.is_null() {\n                        unsafe {\n                            ::std::ptr::copy_nonoverlapping(\n                                s.as_bytes().as_ptr() as *const c_char,\n                                ptr,\n                                len,\n                            );\n                            *ptr.offset(len as isize) = 0;\n                        }\n                    }\n                    ptr\n                }\n\n                for (i, response) in (*prompter)\n                    .prompt(&username, &instruction, &prompts)\n                    .into_iter()\n                    .take(prompts.len())\n                    .enumerate()\n                {\n                    let ptr = strdup_string(&response);\n                    if !ptr.is_null() {\n                        responses[i].length = response.len() as c_uint;\n                    } else {\n                        responses[i].length = 0;\n                    }\n                    responses[i].text = ptr;\n                }\n            }));\n        }\n\n        let username = CString::new(username)?;\n        let username = username.as_bytes();\n        let inner = self.inner();\n        unsafe {\n            with_abstract(inner.raw, prompter as *mut P as *mut c_void, || {\n                inner.rc(raw::libssh2_userauth_keyboard_interactive_ex(\n                    inner.raw,\n                    username.as_ptr() as *const _,\n                    username.len() as c_uint,\n                    Some(prompt::<P>),\n                ))\n            })\n        }\n    }\n\n    /// Attempt to perform SSH agent authentication.\n    ///\n    /// This is a helper method for attempting to authenticate the current\n    /// connection with the first public key found in an SSH agent. If more\n    /// control is needed than this method offers, it is recommended to use\n    /// `agent` directly to control how the identity is found.\n    pub fn userauth_agent(&self, username: &str) -> Result<(), Error> {\n        let mut agent = self.agent()?;\n        agent.connect()?;\n        agent.list_identities()?;\n        let identities = agent.identities()?;\n        let identity = match identities.get(0) {\n            Some(identity) => identity,\n            None => {\n                return Err(Error::new(\n                    ErrorCode::Session(raw::LIBSSH2_ERROR_INVAL),\n                    \"no identities found in the ssh agent\",\n                ))\n            }\n        };\n        agent.userauth(username, &identity)\n    }\n\n    /// Attempt public key authentication using a PEM encoded private key file\n    /// stored on disk.\n    pub fn userauth_pubkey_file(\n        &self,\n        username: &str,\n        pubkey: Option<&Path>,\n        privatekey: &Path,\n        passphrase: Option<&str>,\n    ) -> Result<(), Error> {\n        let username = CString::new(username)?;\n        let username = username.as_bytes();\n        let pubkey = match pubkey {\n            Some(s) => Some(CString::new(util::path2bytes(s)?)?),\n            None => None,\n        };\n        let privatekey = CString::new(util::path2bytes(privatekey)?)?;\n        let passphrase = match passphrase {\n            Some(s) => Some(CString::new(s)?),\n            None => None,\n        };\n        let inner = self.inner();\n        inner.rc(unsafe {\n            raw::libssh2_userauth_publickey_fromfile_ex(\n                inner.raw,\n                username.as_ptr() as *const _,\n                username.len() as c_uint,\n                pubkey.as_ref().map(|s| s.as_ptr()).unwrap_or(null()),\n                privatekey.as_ptr(),\n                passphrase\n                    .as_ref()\n                    .map(|s| s.as_ptr())\n                    .unwrap_or(null()),\n            )\n        })\n    }\n\n    /// Attempt public key authentication using a PEM encoded private key from\n    /// memory. Public key is computed from private key if none passed.\n    /// This is available with openssl enabled (Required for Unix, or with vendored-openssl or openssl-on-win32 features).\n    #[cfg(any(unix, feature = \"vendored-openssl\", feature = \"openssl-on-win32\"))]\n    pub fn userauth_pubkey_memory(\n        &self,\n        username: &str,\n        pubkeydata: Option<&str>,\n        privatekeydata: &str,\n        passphrase: Option<&str>,\n    ) -> Result<(), Error> {\n        let username = CString::new(username)?;\n        let username = username.as_bytes();\n        let (pubkeydata, pubkeydata_len) = match pubkeydata {\n            Some(s) => (Some(CString::new(s)?), s.len()),\n            None => (None, 0),\n        };\n        let privatekeydata_len = privatekeydata.len();\n        let privatekeydata = CString::new(privatekeydata)?;\n        let passphrase = match passphrase {\n            Some(s) => Some(CString::new(s)?),\n            None => None,\n        };\n        let inner = self.inner();\n        inner.rc(unsafe {\n            raw::libssh2_userauth_publickey_frommemory(\n                inner.raw,\n                username.as_ptr() as *const _,\n                username.len() as size_t,\n                pubkeydata\n                    .as_ref()\n                    .map(|s| s.as_ptr())\n                    .unwrap_or(null()),\n                pubkeydata_len as size_t,\n                privatekeydata.as_ptr(),\n                privatekeydata_len as size_t,\n                passphrase\n                    .as_ref()\n                    .map(|s| s.as_ptr())\n                    .unwrap_or(null()),\n            )\n        })\n    }\n\n    // Umm... I wish this were documented in libssh2?\n    #[allow(missing_docs)]\n    pub fn userauth_hostbased_file(\n        &self,\n        username: &str,\n        publickey: &Path,\n        privatekey: &Path,\n        passphrase: Option<&str>,\n        hostname: &str,\n        local_username: Option<&str>,\n    ) -> Result<(), Error> {\n        let publickey = CString::new(util::path2bytes(publickey)?)?;\n        let privatekey = CString::new(util::path2bytes(privatekey)?)?;\n        let passphrase = match passphrase {\n            Some(s) => Some(CString::new(s)?),\n            None => None,\n        };\n        let local_username = match local_username {\n            Some(local) => local,\n            None => username,\n        };\n        let username = CString::new(username)?;\n        let username = username.as_bytes();\n        let local_username = CString::new(local_username)?;\n        let local_username = local_username.as_bytes();\n        let inner = self.inner();\n        inner.rc(unsafe {\n            raw::libssh2_userauth_hostbased_fromfile_ex(\n                inner.raw,\n                username.as_ptr() as *const _,\n                username.len() as c_uint,\n                publickey.as_ptr(),\n                privatekey.as_ptr(),\n                passphrase\n                    .as_ref()\n                    .map(|s| s.as_ptr())\n                    .unwrap_or(null()),\n                hostname.as_ptr() as *const _,\n                hostname.len() as c_uint,\n                local_username.as_ptr() as *const _,\n                local_username.len() as c_uint,\n            )\n        })\n    }\n\n    /// Indicates whether or not the named session has been successfully\n    /// authenticated.\n    pub fn authenticated(&self) -> bool {\n        let inner = self.inner();\n        unsafe { raw::libssh2_userauth_authenticated(inner.raw) != 0 }\n    }\n\n    /// Send a SSH_USERAUTH_NONE request to the remote host.\n    ///\n    /// Unless the remote host is configured to accept none as a viable\n    /// authentication scheme (unlikely), it will return SSH_USERAUTH_FAILURE\n    /// along with a listing of what authentication schemes it does support. In\n    /// the unlikely event that none authentication succeeds, this method with\n    /// return an error. This case may be distinguished from a failing case by\n    /// examining the return value of the `authenticated` method.\n    ///\n    /// The return value is a comma-separated string of supported auth schemes,\n    /// and may be an empty string.\n    pub fn auth_methods(&self, username: &str) -> Result<&str, Error> {\n        let len = username.len();\n        let username = CString::new(username)?;\n        let inner = self.inner();\n        unsafe {\n            let ret = raw::libssh2_userauth_list(inner.raw, username.as_ptr(), len as c_uint);\n            if ret.is_null() {\n                match inner.last_error() {\n                    Some(err) => Err(err),\n                    None => Ok(\"\"),\n                }\n            } else {\n                Ok(str::from_utf8(::opt_bytes(self, ret).unwrap()).unwrap())\n            }\n        }\n    }\n\n    /// Retrieve banner message from server, if available.\n    ///\n    /// When no such message is sent by server or if no authentication attempt has\n    /// been made, this function returns LIBSSH2_ERROR_MISSING_AUTH_BANNER.\n    ///\n    /// The return value is the userauth banner or None or an Error\n    pub fn userauth_banner(&self) -> Result<Option<&str>, Error> {\n        let mut userauth_banner = null_mut();\n        let inner = self.inner();\n        match unsafe { raw::libssh2_userauth_banner(inner.raw, &mut userauth_banner)} {\n            0 => {\n                unsafe {\n                    Ok(::opt_bytes(self, userauth_banner).and_then(|s| str::from_utf8(s).ok()))\n                }\n            },\n            rc => {\n                Err(Error::from_errno(ErrorCode::Session(rc)))\n            }\n        }\n    }\n\n    /// Set preferred key exchange method\n    ///\n    /// The preferences provided are a comma delimited list of preferred methods\n    /// to use with the most preferred listed first and the least preferred\n    /// listed last. If a method is listed which is not supported by libssh2 it\n    /// will be ignored and not sent to the remote host during protocol\n    /// negotiation.\n    pub fn method_pref(&self, method_type: MethodType, prefs: &str) -> Result<(), Error> {\n        let prefs = CString::new(prefs)?;\n        let inner = self.inner();\n        unsafe {\n            inner.rc(raw::libssh2_session_method_pref(\n                inner.raw,\n                method_type as c_int,\n                prefs.as_ptr(),\n            ))\n        }\n    }\n\n    /// Return the currently active algorithms.\n    ///\n    /// Returns the actual method negotiated for a particular transport\n    /// parameter. May return `None` if the session has not yet been started.\n    pub fn methods(&self, method_type: MethodType) -> Option<&str> {\n        let inner = self.inner();\n        unsafe {\n            let ptr = raw::libssh2_session_methods(inner.raw, method_type as c_int);\n            ::opt_bytes(self, ptr).and_then(|s| str::from_utf8(s).ok())\n        }\n    }\n\n    /// Get list of supported algorithms.\n    pub fn supported_algs(&self, method_type: MethodType) -> Result<Vec<&'static str>, Error> {\n        static STATIC: () = ();\n        let method_type = method_type as c_int;\n        let mut ret = Vec::new();\n        let inner = self.inner();\n        unsafe {\n            let mut ptr = null_mut();\n            let rc = raw::libssh2_session_supported_algs(inner.raw, method_type, &mut ptr);\n            if rc <= 0 {\n                inner.rc(rc)?;\n            }\n            for i in 0..(rc as isize) {\n                let s = ::opt_bytes(&STATIC, *ptr.offset(i)).unwrap();\n                let s = str::from_utf8(s).unwrap();\n                ret.push(s);\n            }\n            raw::libssh2_free(inner.raw, ptr as *mut c_void);\n        }\n        Ok(ret)\n    }\n\n    /// Init an ssh-agent handle.\n    ///\n    /// The returned agent will still need to be connected manually before use.\n    pub fn agent(&self) -> Result<Agent, Error> {\n        let inner = self.inner();\n        unsafe {\n            let agent = raw::libssh2_agent_init(inner.raw);\n            let err = inner.last_error();\n            Agent::from_raw_opt(agent, err, &self.inner)\n        }\n    }\n\n    /// Init a collection of known hosts for this session.\n    ///\n    /// Returns the handle to an internal representation of a known host\n    /// collection.\n    pub fn known_hosts(&self) -> Result<KnownHosts, Error> {\n        let inner = self.inner();\n        unsafe {\n            let ptr = raw::libssh2_knownhost_init(inner.raw);\n            let err = inner.last_error();\n            KnownHosts::from_raw_opt(ptr, err, &self.inner)\n        }\n    }\n\n    /// Establish a new session-based channel.\n    ///\n    /// This method is commonly used to create a channel to execute commands\n    /// over or create a new login shell.\n    pub fn channel_session(&self) -> Result<Channel, Error> {\n        self.channel_open(\n            \"session\",\n            raw::LIBSSH2_CHANNEL_WINDOW_DEFAULT as u32,\n            raw::LIBSSH2_CHANNEL_PACKET_DEFAULT as u32,\n            None,\n        )\n    }\n\n    /// Tunnel a TCP connection through an SSH session.\n    ///\n    /// Tunnel a TCP/IP connection through the SSH transport via the remote host\n    /// to a third party. Communication from the client to the SSH server\n    /// remains encrypted, communication from the server to the 3rd party host\n    /// travels in cleartext.\n    ///\n    /// The optional `src` argument is the host/port to tell the SSH server\n    /// where the connection originated from.\n    ///\n    /// The `Channel` returned represents a connection between this host and the\n    /// specified remote host.\n    pub fn channel_direct_tcpip(\n        &self,\n        host: &str,\n        port: u16,\n        src: Option<(&str, u16)>,\n    ) -> Result<Channel, Error> {\n        let (shost, sport) = src.unwrap_or((\"127.0.0.1\", 22));\n        let host = CString::new(host)?;\n        let shost = CString::new(shost)?;\n        let inner = self.inner();\n        unsafe {\n            let ret = raw::libssh2_channel_direct_tcpip_ex(\n                inner.raw,\n                host.as_ptr(),\n                port as c_int,\n                shost.as_ptr(),\n                sport as c_int,\n            );\n            let err = inner.last_error();\n            Channel::from_raw_opt(ret, err, &self.inner)\n        }\n    }\n\n    /// Tunnel a Unix domain socket connection through an SSH session.\n    ///\n    /// Tunnel a UNIX socket connection through the SSH transport via the remote\n    /// host to a third party. Communication from the client to the SSH server\n    /// remains encrypted, communication from the server to the 3rd party host\n    /// travels in cleartext.\n    ///\n    /// The optional `src` argument is the host/port to tell the SSH server\n    /// where the connection originated from.\n    ///\n    /// The `Channel` returned represents a connection between this host and the\n    /// specified remote host.\n    pub fn channel_direct_streamlocal(\n        &self,\n        socket_path: &str,\n        src: Option<(&str, u16)>,\n    ) -> Result<Channel, Error> {\n        let (shost, sport) = src.unwrap_or((\"127.0.0.1\", 22));\n        let path = CString::new(socket_path)?;\n        let shost = CString::new(shost)?;\n        let inner = self.inner();\n        unsafe {\n            let ret = raw::libssh2_channel_direct_streamlocal_ex(\n                inner.raw,\n                path.as_ptr(),\n                shost.as_ptr(),\n                sport as c_int,\n            );\n            let err = inner.last_error();\n            Channel::from_raw_opt(ret, err, &self.inner)\n        }\n    }\n\n    /// Instruct the remote SSH server to begin listening for inbound TCP/IP\n    /// connections.\n    ///\n    /// New connections will be queued by the library until accepted by the\n    /// `accept` method on the returned `Listener`.\n    pub fn channel_forward_listen(\n        &self,\n        remote_port: u16,\n        host: Option<&str>,\n        queue_maxsize: Option<u32>,\n    ) -> Result<(Listener, u16), Error> {\n        let mut bound_port = 0;\n        let host = host.map(|s| CString::new(s)).transpose()?;\n        let inner = self.inner();\n        unsafe {\n            let ret = raw::libssh2_channel_forward_listen_ex(\n                inner.raw,\n                host\n                    .as_ref()\n                    .map(|s| s.as_ptr())\n                    .unwrap_or(null()),\n                remote_port as c_int,\n                &mut bound_port,\n                queue_maxsize.unwrap_or(0) as c_int,\n            );\n            let err = inner.last_error();\n            Listener::from_raw_opt(ret, err, &self.inner).map(|l| (l, bound_port as u16))\n        }\n    }\n\n    /// Request a file from the remote host via SCP.\n    ///\n    /// The path specified is a path on the remote host which will attempt to be\n    /// sent over the returned channel. Some stat information is also returned\n    /// about the remote file to prepare for receiving the file.\n    pub fn scp_recv(&self, path: &Path) -> Result<(Channel, ScpFileStat), Error> {\n        let path = CString::new(util::path2bytes(path)?)?;\n        let inner = self.inner();\n        unsafe {\n            let mut sb: raw::libssh2_struct_stat = mem::zeroed();\n            let ret = raw::libssh2_scp_recv2(inner.raw, path.as_ptr(), &mut sb);\n            let err = inner.last_error();\n            let mut c = Channel::from_raw_opt(ret, err, &self.inner)?;\n\n            // Hm, apparently when we scp_recv() a file the actual channel\n            // itself does not respond well to read_to_end(), and it also sends\n            // an extra 0 byte (or so it seems). To work around this we\n            // artificially limit the channel to a certain amount of bytes that\n            // can be read.\n            c.limit_read(sb.st_size as u64);\n            Ok((c, ScpFileStat { stat: *sb }))\n        }\n    }\n\n    /// Send a file to the remote host via SCP.\n    ///\n    /// The `remote_path` provided will be the remote file name. The `times`\n    /// argument is a tuple of (mtime, atime), and will default to the remote\n    /// host's current time if not specified.\n    ///\n    /// The size of the file, `size`, must be known ahead of time before\n    /// transmission.\n    pub fn scp_send(\n        &self,\n        remote_path: &Path,\n        mode: i32,\n        size: u64,\n        times: Option<(u64, u64)>,\n    ) -> Result<Channel, Error> {\n        let path = CString::new(util::path2bytes(remote_path)?)?;\n        let (mtime, atime) = times.unwrap_or((0, 0));\n        let inner = self.inner();\n        unsafe {\n            let ret = raw::libssh2_scp_send64(\n                inner.raw,\n                path.as_ptr(),\n                mode as c_int,\n                size as i64,\n                mtime as libc::time_t,\n                atime as libc::time_t,\n            );\n            let err = inner.last_error();\n            Channel::from_raw_opt(ret, err, &self.inner)\n        }\n    }\n\n    /// Open a channel and initialize the SFTP subsystem.\n    ///\n    /// Although the SFTP subsystem operates over the same type of channel as\n    /// those exported by the Channel API, the protocol itself implements its\n    /// own unique binary packet protocol which must be managed with the\n    /// methods on `Sftp`.\n    pub fn sftp(&self) -> Result<Sftp, Error> {\n        let inner = self.inner();\n        unsafe {\n            let ret = raw::libssh2_sftp_init(inner.raw);\n            let err = inner.last_error();\n            Sftp::from_raw_opt(ret, err, &self.inner)\n        }\n    }\n\n    /// Allocate a new channel for exchanging data with the server.\n    ///\n    /// This is typically not called directly but rather through\n    /// `channel_session`, `channel_direct_tcpip`, or `channel_forward_listen`.\n    pub fn channel_open(\n        &self,\n        channel_type: &str,\n        window_size: u32,\n        packet_size: u32,\n        message: Option<&str>,\n    ) -> Result<Channel, Error> {\n        let channel_type = CString::new(channel_type)?;\n        let channel_type = channel_type.as_bytes();\n        let message = message.map(|s| CString::new(s)).transpose()?;\n        let (message, message_len) = message\n            .as_ref()\n            .map(|s| (s.as_ptr(), s.as_bytes().len()))\n            .unwrap_or((null(), 0));\n        let inner = self.inner();\n        unsafe {\n            let ret = raw::libssh2_channel_open_ex(\n                inner.raw,\n                channel_type.as_ptr() as *const _,\n                channel_type.len() as c_uint,\n                window_size as c_uint,\n                packet_size as c_uint,\n                message,\n                message_len as c_uint,\n            );\n            let err = inner.last_error();\n            Channel::from_raw_opt(ret, err, &self.inner)\n        }\n    }\n\n    /// Get the remote banner\n    ///\n    /// Once the session has been setup and handshake() has completed\n    /// successfully, this function can be used to get the server id from the\n    /// banner each server presents.\n    ///\n    /// May return `None` on invalid utf-8 or if an error has occurred.\n    pub fn banner(&self) -> Option<&str> {\n        self.banner_bytes().and_then(|s| str::from_utf8(s).ok())\n    }\n\n    /// See `banner`.\n    ///\n    /// Will only return `None` if an error has occurred.\n    pub fn banner_bytes(&self) -> Option<&[u8]> {\n        let inner = self.inner();\n        unsafe { ::opt_bytes(self, raw::libssh2_session_banner_get(inner.raw)) }\n    }\n\n    /// Get the remote key.\n    ///\n    /// Returns `None` if something went wrong.\n    pub fn host_key(&self) -> Option<(&[u8], HostKeyType)> {\n        let mut len = 0;\n        let mut kind = 0;\n        let inner = self.inner();\n        unsafe {\n            let ret = raw::libssh2_session_hostkey(inner.raw, &mut len, &mut kind);\n            if ret.is_null() {\n                return None;\n            }\n            let data = slice::from_raw_parts(ret as *const u8, len as usize);\n            let kind = match kind {\n                raw::LIBSSH2_HOSTKEY_TYPE_RSA => HostKeyType::Rsa,\n                raw::LIBSSH2_HOSTKEY_TYPE_DSS => HostKeyType::Dss,\n                raw::LIBSSH2_HOSTKEY_TYPE_ECDSA_256 => HostKeyType::Ecdsa256,\n                raw::LIBSSH2_HOSTKEY_TYPE_ECDSA_384 => HostKeyType::Ecdsa384,\n                raw::LIBSSH2_HOSTKEY_TYPE_ECDSA_521 => HostKeyType::Ecdsa521,\n                raw::LIBSSH2_HOSTKEY_TYPE_ED25519 => HostKeyType::Ed25519,\n                raw::LIBSSH2_HOSTKEY_TYPE_UNKNOWN => HostKeyType::Unknown,\n                _ => HostKeyType::Unknown,\n            };\n            Some((data, kind))\n        }\n    }\n\n    /// Returns the computed digest of the remote system's hostkey.\n    ///\n    /// The bytes returned are the raw hash, and are not printable. If the hash\n    /// is not yet available `None` is returned.\n    pub fn host_key_hash(&self, hash: HashType) -> Option<&[u8]> {\n        let len = match hash {\n            HashType::Md5 => 16,\n            HashType::Sha1 => 20,\n            HashType::Sha256 => 32,\n        };\n        let inner = self.inner();\n        unsafe {\n            let ret = raw::libssh2_hostkey_hash(inner.raw, hash as c_int);\n            if ret.is_null() {\n                None\n            } else {\n                let ret = ret as *const u8;\n                Some(slice::from_raw_parts(ret, len))\n            }\n        }\n    }\n\n    /// Set how often keepalive messages should be sent.\n    ///\n    /// The want_reply argument indicates whether the keepalive messages should\n    /// request a response from the server.\n    ///\n    /// The interval argument is number of seconds that can pass without any\n    /// I/O, use 0 (the default) to disable keepalives. To avoid some busy-loop\n    /// corner-cases, if you specify an interval of 1 it will be treated as 2.\n    pub fn set_keepalive(&self, want_reply: bool, interval: u32) {\n        let inner = self.inner();\n        unsafe { raw::libssh2_keepalive_config(inner.raw, want_reply as c_int, interval as c_uint) }\n    }\n\n    /// Send a keepalive message if needed.\n    ///\n    /// Returns how many seconds you can sleep after this call before you need\n    /// to call it again.\n    pub fn keepalive_send(&self) -> Result<u32, Error> {\n        let mut ret = 0;\n        let inner = self.inner();\n        let rc = unsafe { raw::libssh2_keepalive_send(inner.raw, &mut ret) };\n        inner.rc(rc)?;\n        Ok(ret as u32)\n    }\n\n    /// Terminate the transport layer.\n    ///\n    /// Send a disconnect message to the remote host associated with session,\n    /// along with a reason symbol and a verbose description.\n    ///\n    /// Note that this does *not* close the underlying socket.\n    pub fn disconnect(\n        &self,\n        reason: Option<DisconnectCode>,\n        description: &str,\n        lang: Option<&str>,\n    ) -> Result<(), Error> {\n        let reason = reason.unwrap_or(ByApplication) as c_int;\n        let description = CString::new(description)?;\n        let lang = CString::new(lang.unwrap_or(\"\"))?;\n        let inner = self.inner();\n        unsafe {\n            inner.rc(raw::libssh2_session_disconnect_ex(\n                inner.raw,\n                reason,\n                description.as_ptr(),\n                lang.as_ptr(),\n            ))\n        }\n    }\n\n    /// Returns the blocked io directions that the application needs to wait for.\n    ///\n    /// This function should be used after an error of type `WouldBlock` is returned to\n    /// find out the socket events the application has to wait for.\n    pub fn block_directions(&self) -> BlockDirections {\n        let inner = self.inner();\n        let dir = unsafe { raw::libssh2_session_block_directions(inner.raw) };\n        match dir {\n            raw::LIBSSH2_SESSION_BLOCK_INBOUND => BlockDirections::Inbound,\n            raw::LIBSSH2_SESSION_BLOCK_OUTBOUND => BlockDirections::Outbound,\n            x if x == raw::LIBSSH2_SESSION_BLOCK_INBOUND | raw::LIBSSH2_SESSION_BLOCK_OUTBOUND => {\n                BlockDirections::Both\n            }\n            _ => BlockDirections::None,\n        }\n    }\n\n    fn inner(&self) -> MutexGuard<'_, SessionInner> {\n        self.inner.lock()\n    }\n\n    /// Sets the trace level for the session.\n    ///\n    pub fn trace(&self, bitmask: TraceFlags) {\n        let inner = self.inner();\n        unsafe { let _ = raw::libssh2_trace(inner.raw, bitmask.bits() as c_int); }\n    }\n}\n\n#[cfg(unix)]\nimpl AsRawFd for Session {\n    fn as_raw_fd(&self) -> RawFd {\n        let inner = self.inner();\n        match inner.tcp.as_ref() {\n            Some(tcp) => tcp.as_raw_fd(),\n            None => panic!(\"tried to obtain raw fd without tcp stream set\"),\n        }\n    }\n}\n\n#[cfg(windows)]\nimpl AsRawSocket for Session {\n    fn as_raw_socket(&self) -> RawSocket {\n        let inner = self.inner();\n        match inner.tcp.as_ref() {\n            Some(tcp) => tcp.as_raw_socket(),\n            None => panic!(\"tried to obtain raw socket without tcp stream set\"),\n        }\n    }\n}\n\nimpl SessionInner {\n    /// Translate a return code into a Rust-`Result`.\n    pub fn rc(&self, rc: c_int) -> Result<(), Error> {\n        if rc >= 0 {\n            Ok(())\n        } else {\n            Err(Error::from_session_error_raw(self.raw, rc))\n        }\n    }\n\n    pub fn last_error(&self) -> Option<Error> {\n        Error::last_session_error_raw(self.raw)\n    }\n\n    /// Set or clear blocking mode on session\n    pub fn set_blocking(&self, blocking: bool) {\n        unsafe { raw::libssh2_session_set_blocking(self.raw, blocking as c_int) }\n    }\n\n    /// Returns whether the session was previously set to nonblocking.\n    pub fn is_blocking(&self) -> bool {\n        unsafe { raw::libssh2_session_get_blocking(self.raw) != 0 }\n    }\n}\n\nimpl Drop for SessionInner {\n    fn drop(&mut self) {\n        unsafe {\n            let _rc = raw::libssh2_session_free(self.raw);\n        }\n    }\n}\n\nimpl ScpFileStat {\n    /// Returns the size of the remote file.\n    pub fn size(&self) -> u64 {\n        self.stat.st_size as u64\n    }\n    /// Returns the listed mode of the remote file.\n    pub fn mode(&self) -> i32 {\n        self.stat.st_mode as i32\n    }\n    /// Returns whether the remote file is a directory.\n    pub fn is_dir(&self) -> bool {\n        self.mode() & (libc::S_IFMT as i32) == (libc::S_IFDIR as i32)\n    }\n    /// Returns whether the remote file is a regular file.\n    pub fn is_file(&self) -> bool {\n        self.mode() & (libc::S_IFMT as i32) == (libc::S_IFREG as i32)\n    }\n}\n"
  },
  {
    "path": "src/sftp.rs",
    "content": "use libc::{c_int, c_long, c_uint, c_ulong, size_t};\nuse parking_lot::{Mutex, MutexGuard};\nuse std::convert::TryFrom;\nuse std::ffi::CString;\nuse std::io::prelude::*;\nuse std::io::{self, ErrorKind, SeekFrom};\nuse std::mem;\nuse std::path::{Path, PathBuf};\nuse std::ptr::null_mut;\nuse std::sync::Arc;\n\nuse util;\nuse {raw, Error, ErrorCode, SessionInner};\n\n/// A handle to a remote filesystem over SFTP.\n///\n/// Instances are created through the `sftp` method on a `Session`.\npub struct Sftp {\n    inner: Option<Arc<SftpInnerDropWrapper>>,\n}\n/// This contains an Option so that we're able to disable the Drop hook when dropping manually,\n/// while still dropping all the fields of SftpInner (which we couldn't do with `mem::forget`)\nstruct SftpInnerDropWrapper(Option<SftpInner>);\nstruct SftpInner {\n    raw: *mut raw::LIBSSH2_SFTP,\n    sess: Arc<Mutex<SessionInner>>,\n}\n\n// Sftp is both Send and Sync; the compiler can't see it because it\n// is pessimistic about the raw pointer.  We use Arc/Mutex to guard accessing\n// the raw pointer so we are safe for both.\nunsafe impl Send for Sftp {}\nunsafe impl Sync for Sftp {}\n\nstruct LockedSftp<'sftp> {\n    raw: *mut raw::LIBSSH2_SFTP,\n    sess: MutexGuard<'sftp, SessionInner>,\n}\n\n/// A file handle to an SFTP connection.\n///\n/// Files behave similarly to `std::old_io::File` in that they are readable and\n/// writable and support operations like stat and seek.\n///\n/// Files are created through `open`, `create`, and `open_mode` on an instance\n/// of `Sftp`.\npub struct File {\n    inner: Option<FileInner>,\n}\nstruct FileInner {\n    raw: *mut raw::LIBSSH2_SFTP_HANDLE,\n    sftp: Arc<SftpInnerDropWrapper>,\n}\n\n// File is both Send and Sync; the compiler can't see it because it\n// is pessimistic about the raw pointer.  We use Arc/Mutex to guard accessing\n// the raw pointer so we are safe for both.\nunsafe impl Send for File {}\nunsafe impl Sync for File {}\n\nstruct LockedFile<'file> {\n    raw: *mut raw::LIBSSH2_SFTP_HANDLE,\n    sess: MutexGuard<'file, SessionInner>,\n}\n\n/// Metadata information about a remote file.\n///\n/// Fields are not necessarily all provided\n#[derive(Debug, Clone, Eq, PartialEq)]\n#[allow(missing_copy_implementations)]\npub struct FileStat {\n    /// File size, in bytes of the file.\n    pub size: Option<u64>,\n    /// Owner ID of the file\n    pub uid: Option<u32>,\n    /// Owning group of the file\n    pub gid: Option<u32>,\n    /// Permissions (mode) of the file\n    pub perm: Option<u32>,\n    /// Last access time of the file\n    pub atime: Option<u64>,\n    /// Last modification time of the file\n    pub mtime: Option<u64>,\n}\n\n/// An enum representing a type of file.\n#[derive(PartialEq)]\npub enum FileType {\n    /// Named pipe (S_IFIFO)\n    NamedPipe,\n    /// Character device (S_IFCHR)\n    CharDevice,\n    /// Block device (S_IFBLK)\n    BlockDevice,\n    /// Directory (S_IFDIR)\n    Directory,\n    /// Regular file (S_IFREG)\n    RegularFile,\n    /// Symbolic link (S_IFLNK)\n    Symlink,\n    /// Unix domain socket (S_IFSOCK)\n    Socket,\n    /// Other filetype (does not correspond to any of the other ones)\n    Other(c_ulong),\n}\n\nbitflags! {\n    /// Options that can be used to configure how a file is opened\n    #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]\n    pub struct OpenFlags: c_ulong {\n        /// Open the file for reading.\n        const READ = raw::LIBSSH2_FXF_READ;\n        /// Open the file for writing. If both this and `Read` are specified,\n        /// the file is opened for both reading and writing.\n        const WRITE = raw::LIBSSH2_FXF_WRITE;\n        /// Force all writes to append data at the end of the file.\n        const APPEND = raw::LIBSSH2_FXF_APPEND;\n        /// If this flag is specified, then a new file will be created if one\n        /// does not already exist (if `Truncate` is specified, the new file\n        /// will be truncated to zero length if it previously exists).\n        const CREATE = raw::LIBSSH2_FXF_CREAT;\n        /// Forces an existing file with the same name to be truncated to zero\n        /// length when creating a file by specifying `Create`. Using this flag\n        /// implies the `Create` flag.\n        const TRUNCATE = raw::LIBSSH2_FXF_TRUNC | Self::CREATE.bits();\n        /// Causes the request to fail if the named file already exists. Using\n        /// this flag implies the `Create` flag.\n        const EXCLUSIVE = raw::LIBSSH2_FXF_EXCL | Self::CREATE.bits();\n    }\n}\n\nbitflags! {\n    /// Options to `Sftp::rename`.\n    #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]\n    pub struct RenameFlags: c_long {\n        /// In a rename operation, overwrite the destination if it already\n        /// exists. If this flag is not present then it is an error if the\n        /// destination already exists.\n        const OVERWRITE = raw::LIBSSH2_SFTP_RENAME_OVERWRITE;\n        /// Inform the remote that an atomic rename operation is desired if\n        /// available.\n        const ATOMIC = raw::LIBSSH2_SFTP_RENAME_ATOMIC;\n        /// Inform the remote end that the native system calls for renaming\n        /// should be used.\n        const NATIVE = raw::LIBSSH2_SFTP_RENAME_NATIVE;\n    }\n}\n\n/// How to open a file handle with libssh2.\n#[derive(Copy, Clone)]\npub enum OpenType {\n    /// Specify that a file shoud be opened.\n    File = raw::LIBSSH2_SFTP_OPENFILE as isize,\n    /// Specify that a directory should be opened.\n    Dir = raw::LIBSSH2_SFTP_OPENDIR as isize,\n}\n\nimpl Sftp {\n    pub(crate) fn from_raw_opt(\n        raw: *mut raw::LIBSSH2_SFTP,\n        err: Option<Error>,\n        sess: &Arc<Mutex<SessionInner>>,\n    ) -> Result<Self, Error> {\n        if raw.is_null() {\n            Err(err.unwrap_or_else(Error::unknown))\n        } else {\n            Ok(Self {\n                inner: Some(Arc::new(SftpInnerDropWrapper(Some(SftpInner {\n                    raw,\n                    sess: Arc::clone(sess),\n                })))),\n            })\n        }\n    }\n\n    /// Open a handle to a file.\n    ///\n    /// The mode will represent the permissions for the file ([Wikipedia](<https://en.wikipedia.org/wiki/File-system_permissions#Numeric_notation>)).\n    pub fn open_mode<T: AsRef<Path>>(\n        &self,\n        filename: T,\n        flags: OpenFlags,\n        mode: i32,\n        open_type: OpenType,\n    ) -> Result<File, Error> {\n        let filename = CString::new(util::path2bytes(filename.as_ref())?)?;\n\n        let locked = self.lock()?;\n        unsafe {\n            let ret = raw::libssh2_sftp_open_ex(\n                locked.raw,\n                filename.as_ptr() as *const _,\n                filename.as_bytes().len() as c_uint,\n                flags.bits() as c_ulong,\n                mode as c_long,\n                open_type as c_int,\n            );\n            if ret.is_null() {\n                let rc = raw::libssh2_session_last_errno(locked.sess.raw);\n                Err(Self::error_code_into_error(locked.sess.raw, locked.raw, rc))\n            } else {\n                Ok(File::from_raw(self, ret))\n            }\n        }\n    }\n\n    /// Helper to open a file in the `Read` mode.\n    pub fn open<T: AsRef<Path>>(&self, filename: T) -> Result<File, Error> {\n        self.open_mode(filename, OpenFlags::READ, 0o644, OpenType::File)\n    }\n\n    /// Helper to create a file in write-only mode with truncation.\n    pub fn create(&self, filename: &Path) -> Result<File, Error> {\n        self.open_mode(\n            filename,\n            OpenFlags::WRITE | OpenFlags::TRUNCATE,\n            0o644,\n            OpenType::File,\n        )\n    }\n\n    /// Helper to open a directory for reading its contents.\n    pub fn opendir<T: AsRef<Path>>(&self, dirname: T) -> Result<File, Error> {\n        self.open_mode(dirname, OpenFlags::READ, 0, OpenType::Dir)\n    }\n\n    /// Convenience function to read the files in a directory.\n    ///\n    /// The returned paths are all joined with `dirname` when returned, and the\n    /// paths `.` and `..` are filtered out of the returned list.\n    pub fn readdir<T: AsRef<Path>>(&self, dirname: T) -> Result<Vec<(PathBuf, FileStat)>, Error> {\n        let mut dir = self.opendir(dirname.as_ref())?;\n        let mut ret = Vec::new();\n        loop {\n            match dir.readdir() {\n                Ok((filename, stat)) => {\n                    if &*filename == Path::new(\".\") || &*filename == Path::new(\"..\") {\n                        continue;\n                    }\n\n                    ret.push((dirname.as_ref().join(&filename), stat))\n                }\n                Err(ref e) if e.code() == ErrorCode::Session(raw::LIBSSH2_ERROR_FILE) => break,\n                Err(e) => {\n                    if e.code() != ErrorCode::Session(raw::LIBSSH2_ERROR_EAGAIN) {\n                        return Err(e);\n                    }\n                }\n            }\n        }\n        Ok(ret)\n    }\n\n    /// Create a directory on the remote file system.\n    ///\n    /// The mode will set the permissions of the new directory ([Wikipedia](<https://en.wikipedia.org/wiki/File-system_permissions#Numeric_notation>)).\n    pub fn mkdir(&self, filename: &Path, mode: i32) -> Result<(), Error> {\n        let filename = CString::new(util::path2bytes(filename)?)?;\n        let locked = self.lock()?;\n        Self::rc(&locked, unsafe {\n            raw::libssh2_sftp_mkdir_ex(\n                locked.raw,\n                filename.as_ptr() as *const _,\n                filename.as_bytes().len() as c_uint,\n                mode as c_long,\n            )\n        })\n    }\n\n    /// Remove a directory from the remote file system.\n    pub fn rmdir(&self, filename: &Path) -> Result<(), Error> {\n        let filename = CString::new(util::path2bytes(filename)?)?;\n        let locked = self.lock()?;\n        Self::rc(&locked, unsafe {\n            raw::libssh2_sftp_rmdir_ex(\n                locked.raw,\n                filename.as_ptr() as *const _,\n                filename.as_bytes().len() as c_uint,\n            )\n        })\n    }\n\n    /// Get the metadata for a file, performed by stat(2)\n    pub fn stat(&self, filename: &Path) -> Result<FileStat, Error> {\n        let filename = CString::new(util::path2bytes(filename)?)?;\n        let locked = self.lock()?;\n        unsafe {\n            let mut ret = mem::zeroed();\n            Self::rc(\n                &locked,\n                raw::libssh2_sftp_stat_ex(\n                    locked.raw,\n                    filename.as_ptr() as *const _,\n                    filename.as_bytes().len() as c_uint,\n                    raw::LIBSSH2_SFTP_STAT,\n                    &mut ret,\n                ),\n            )\n            .map(|_| FileStat::from_raw(&ret))\n        }\n    }\n\n    /// Get the metadata for a file, performed by lstat(2)\n    pub fn lstat(&self, filename: &Path) -> Result<FileStat, Error> {\n        let filename = CString::new(util::path2bytes(filename)?)?;\n        let locked = self.lock()?;\n        unsafe {\n            let mut ret = mem::zeroed();\n            Self::rc(\n                &locked,\n                raw::libssh2_sftp_stat_ex(\n                    locked.raw,\n                    filename.as_ptr() as *const _,\n                    filename.as_bytes().len() as c_uint,\n                    raw::LIBSSH2_SFTP_LSTAT,\n                    &mut ret,\n                ),\n            )\n            .map(|_| FileStat::from_raw(&ret))\n        }\n    }\n\n    /// Set the metadata for a file.\n    pub fn setstat(&self, filename: &Path, stat: FileStat) -> Result<(), Error> {\n        let filename = CString::new(util::path2bytes(filename)?)?;\n        let locked = self.lock()?;\n        Self::rc(&locked, unsafe {\n            let mut raw = stat.raw();\n            raw::libssh2_sftp_stat_ex(\n                locked.raw,\n                filename.as_ptr() as *const _,\n                filename.as_bytes().len() as c_uint,\n                raw::LIBSSH2_SFTP_SETSTAT,\n                &mut raw,\n            )\n        })\n    }\n\n    /// Create a symlink at `target` pointing at `path`.\n    pub fn symlink(&self, path: &Path, target: &Path) -> Result<(), Error> {\n        let path = CString::new(util::path2bytes(path)?)?;\n        let target = CString::new(util::path2bytes(target)?)?;\n        let locked = self.lock()?;\n        Self::rc(&locked, unsafe {\n            raw::libssh2_sftp_symlink_ex(\n                locked.raw,\n                path.as_ptr() as *const _,\n                path.as_bytes().len() as c_uint,\n                target.as_ptr() as *mut _,\n                target.as_bytes().len() as c_uint,\n                raw::LIBSSH2_SFTP_SYMLINK,\n            )\n        })\n    }\n\n    /// Read a symlink at `path`.\n    pub fn readlink(&self, path: &Path) -> Result<PathBuf, Error> {\n        self.readlink_op(path, raw::LIBSSH2_SFTP_READLINK)\n    }\n\n    /// Resolve the real path for `path`.\n    pub fn realpath(&self, path: &Path) -> Result<PathBuf, Error> {\n        self.readlink_op(path, raw::LIBSSH2_SFTP_REALPATH)\n    }\n\n    fn readlink_op(&self, path: &Path, op: c_int) -> Result<PathBuf, Error> {\n        let path = CString::new(util::path2bytes(path)?)?;\n        let mut ret = Vec::<u8>::with_capacity(128);\n        let mut rc;\n        let locked = self.lock()?;\n        loop {\n            rc = unsafe {\n                raw::libssh2_sftp_symlink_ex(\n                    locked.raw,\n                    path.as_ptr() as *const _,\n                    path.as_bytes().len() as c_uint,\n                    ret.as_ptr() as *mut _,\n                    ret.capacity() as c_uint,\n                    op,\n                )\n            };\n            if rc == raw::LIBSSH2_ERROR_BUFFER_TOO_SMALL {\n                let cap = ret.capacity();\n                ret.reserve(cap * 2);\n            } else {\n                break;\n            }\n        }\n        Self::rc(&locked, rc).map(move |_| {\n            unsafe { ret.set_len(rc as usize) }\n            mkpath(ret)\n        })\n    }\n\n    /// Rename a filesystem object on the remote filesystem.\n    ///\n    /// The semantics of this command typically include the ability to move a\n    /// filesystem object between folders and/or filesystem mounts. If the\n    /// `Overwrite` flag is not set and the destfile entry already exists, the\n    /// operation will fail.\n    ///\n    /// Use of the other flags (Native or Atomic) indicate a preference (but\n    /// not a requirement) for the remote end to perform an atomic rename\n    /// operation and/or using native system calls when possible.\n    ///\n    /// If no flags are specified then all flags are used.\n    pub fn rename(&self, src: &Path, dst: &Path, flags: Option<RenameFlags>) -> Result<(), Error> {\n        let flags =\n            flags.unwrap_or(RenameFlags::ATOMIC | RenameFlags::OVERWRITE | RenameFlags::NATIVE);\n        let src = CString::new(util::path2bytes(src)?)?;\n        let dst = CString::new(util::path2bytes(dst)?)?;\n        let locked = self.lock()?;\n        Self::rc(&locked, unsafe {\n            raw::libssh2_sftp_rename_ex(\n                locked.raw,\n                src.as_ptr() as *const _,\n                src.as_bytes().len() as c_uint,\n                dst.as_ptr() as *const _,\n                dst.as_bytes().len() as c_uint,\n                flags.bits(),\n            )\n        })\n    }\n\n    /// Remove a file on the remote filesystem\n    pub fn unlink(&self, file: &Path) -> Result<(), Error> {\n        let file = CString::new(util::path2bytes(file)?)?;\n        let locked = self.lock()?;\n        Self::rc(&locked, unsafe {\n            raw::libssh2_sftp_unlink_ex(\n                locked.raw,\n                file.as_ptr() as *const _,\n                file.as_bytes().len() as c_uint,\n            )\n        })\n    }\n\n    fn lock(&self) -> Result<LockedSftp<'_>, Error> {\n        match self.inner.as_ref() {\n            Some(sftp_inner_drop_wrapper) => {\n                let sftp_inner = sftp_inner_drop_wrapper\n                    .0\n                    .as_ref()\n                    .expect(\"Never unset until shutdown, in which case inner is also unset\");\n                let sess = sftp_inner.sess.lock();\n                Ok(LockedSftp {\n                    sess,\n                    raw: sftp_inner.raw,\n                })\n            }\n            None => Err(Error::from_errno(ErrorCode::Session(\n                raw::LIBSSH2_ERROR_BAD_USE,\n            ))),\n        }\n    }\n\n    // This method is used by the async ssh crate\n    #[doc(hidden)]\n    pub fn shutdown(&mut self) -> Result<(), Error> {\n        // We cannot shutdown the SFTP if files are still open, etc, as these store a ref to the sftp in libssh2.\n        // We have to make sure we are the last reference to it.\n        match self.inner.take() {\n            Some(sftp_inner_arc) => {\n                // We were not already un-initialized\n                match Arc::try_unwrap(sftp_inner_arc) {\n                    Ok(mut sftp_inner_wrapper) => {\n                        // Early drop\n                        let sftp_inner = sftp_inner_wrapper.0.take().expect(\n                            \"We were holding an Arc<SftpInnerDropWrapper>, \\\n                                    so nobody could unset this (set on creation)\",\n                        );\n                        sftp_inner\n                            .sess\n                            .lock()\n                            .rc(unsafe { raw::libssh2_sftp_shutdown(sftp_inner.raw) })?;\n                        Ok(())\n                    }\n                    Err(sftp_inner_arc) => {\n                        // We are failing shutdown as there are files left open, keep this object usable\n                        self.inner = Some(sftp_inner_arc);\n                        Err(Error::from_errno(ErrorCode::Session(\n                            raw::LIBSSH2_ERROR_BAD_USE,\n                        )))\n                    }\n                }\n            }\n            None => {\n                // We have already shut this down. Shutting down twice is a mistake from the caller code\n                Err(Error::from_errno(ErrorCode::Session(\n                    raw::LIBSSH2_ERROR_BAD_USE,\n                )))\n            }\n        }\n    }\n\n    fn error_code_into_error(\n        session_raw: *mut raw::LIBSSH2_SESSION,\n        sftp_raw: *mut raw::LIBSSH2_SFTP,\n        rc: libc::c_int,\n    ) -> Error {\n        if rc >= 0 {\n            Error::unknown()\n        } else if rc == raw::LIBSSH2_ERROR_SFTP_PROTOCOL {\n            let actual_rc = unsafe { raw::libssh2_sftp_last_error(sftp_raw) };\n            // TODO: This conversion from `c_ulong` to `c_int` should not be\n            // necessary if the constants `LIBSSH2_FX_*` in the `-sys` crate\n            // are typed as `c_ulong`, as they should be.\n            if let Ok(actual_rc) = libc::c_int::try_from(actual_rc) {\n                Error::from_errno(ErrorCode::SFTP(actual_rc))\n            } else {\n                Error::unknown()\n            }\n        } else {\n            Error::from_session_error_raw(session_raw, rc)\n        }\n    }\n\n    fn error_code_into_result(\n        session_raw: *mut raw::LIBSSH2_SESSION,\n        sftp_raw: *mut raw::LIBSSH2_SFTP,\n        rc: libc::c_int,\n    ) -> Result<(), Error> {\n        if rc >= 0 {\n            Ok(())\n        } else {\n            Err(Self::error_code_into_error(session_raw, sftp_raw, rc))\n        }\n    }\n\n    fn rc(locked: &LockedSftp, rc: libc::c_int) -> Result<(), Error> {\n        Self::error_code_into_result(locked.sess.raw, locked.raw, rc)\n    }\n}\n\nimpl Drop for SftpInnerDropWrapper {\n    fn drop(&mut self) {\n        // Check we were not early-dropped\n        if let Some(inner) = self.0.take() {\n            let sess = inner.sess.lock();\n            // Set ssh2 to blocking during the drop\n            let was_blocking = sess.is_blocking();\n            sess.set_blocking(true);\n            // The shutdown statement can go wrong and return an error code, but we are too late\n            // in the execution to recover it.\n            let _shutdown_result = unsafe { raw::libssh2_sftp_shutdown(inner.raw) };\n            sess.set_blocking(was_blocking);\n        }\n    }\n}\n\nimpl File {\n    /// Wraps a raw pointer in a new File structure tied to the lifetime of the\n    /// given session.\n    ///\n    /// This consumes ownership of `raw`.\n    unsafe fn from_raw(sftp: &Sftp, raw: *mut raw::LIBSSH2_SFTP_HANDLE) -> File {\n        File {\n            inner: Some(FileInner {\n                raw,\n                sftp: Arc::clone(\n                    &sftp\n                        .inner\n                        .as_ref()\n                        .expect(\"Cannot open file after sftp shutdown\"),\n                ),\n            }),\n        }\n    }\n\n    /// Set the metadata for this handle.\n    pub fn setstat(&mut self, stat: FileStat) -> Result<(), Error> {\n        let locked = self.lock()?;\n        self.rc(&locked, unsafe {\n            let mut raw = stat.raw();\n            raw::libssh2_sftp_fstat_ex(locked.raw, &mut raw, 1)\n        })\n    }\n\n    /// Get the metadata for this handle.\n    pub fn stat(&mut self) -> Result<FileStat, Error> {\n        let locked = self.lock()?;\n        unsafe {\n            let mut ret = mem::zeroed();\n            self.rc(&locked, raw::libssh2_sftp_fstat_ex(locked.raw, &mut ret, 0))\n                .map(|_| FileStat::from_raw(&ret))\n        }\n    }\n\n    #[allow(missing_docs)] // sure wish I knew what this did...\n    pub fn statvfs(&mut self) -> Result<raw::LIBSSH2_SFTP_STATVFS, Error> {\n        let locked = self.lock()?;\n        unsafe {\n            let mut ret = mem::zeroed();\n            self.rc(&locked, raw::libssh2_sftp_fstatvfs(locked.raw, &mut ret))\n                .map(move |_| ret)\n        }\n    }\n\n    /// Reads a block of data from a handle and returns file entry information\n    /// for the next entry, if any.\n    ///\n    /// Note that this provides raw access to the `readdir` function from\n    /// libssh2. This will return an error when there are no more files to\n    /// read, and files such as `.` and `..` will be included in the return\n    /// values.\n    ///\n    /// Also note that the return paths will not be absolute paths, they are\n    /// the filenames of the files in this directory.\n    pub fn readdir(&mut self) -> Result<(PathBuf, FileStat), Error> {\n        let locked = self.lock()?;\n\n        // libssh2 through 1.10.0 skips entries if the buffer\n        // is not large enough: it's not enough to resize and try again\n        // on getting an error. So, we make it quite large here.\n        // See <https://github.com/alexcrichton/ssh2-rs/issues/217>.\n        let mut buf = Vec::<u8>::with_capacity(4 * 1024);\n        let mut stat = unsafe { mem::zeroed() };\n        let mut rc;\n        loop {\n            rc = unsafe {\n                raw::libssh2_sftp_readdir_ex(\n                    locked.raw,\n                    buf.as_mut_ptr() as *mut _,\n                    buf.capacity() as size_t,\n                    null_mut(),\n                    0,\n                    &mut stat,\n                )\n            };\n            if rc == raw::LIBSSH2_ERROR_BUFFER_TOO_SMALL {\n                let cap = buf.capacity();\n                buf.reserve(cap * 2);\n            } else {\n                break;\n            }\n        }\n        if rc == 0 {\n            Err(Error::new(\n                ErrorCode::Session(raw::LIBSSH2_ERROR_FILE),\n                \"no more files\",\n            ))\n        } else {\n            self.rc(&locked, rc).map(move |_| {\n                unsafe {\n                    buf.set_len(rc as usize);\n                }\n                (mkpath(buf), FileStat::from_raw(&stat))\n            })\n        }\n    }\n\n    /// This function causes the remote server to synchronize the file data and\n    /// metadata to disk (like fsync(2)).\n    ///\n    /// For this to work requires fsync@openssh.com support on the server.\n    pub fn fsync(&mut self) -> Result<(), Error> {\n        let locked = self.lock()?;\n        self.rc(&locked, unsafe { raw::libssh2_sftp_fsync(locked.raw) })\n    }\n\n    fn lock(&self) -> Result<LockedFile<'_>, Error> {\n        match self.inner.as_ref() {\n            Some(file_inner) => {\n                let sftp_inner = file_inner.sftp.0.as_ref().expect(\n                    \"We are holding an Arc<SftpInnerDropWrapper>, \\\n                        so nobody could unset this (set on creation)\",\n                );\n                let sess = sftp_inner.sess.lock();\n                Ok(LockedFile {\n                    sess,\n                    raw: file_inner.raw,\n                })\n            }\n            None => Err(Error::from_errno(ErrorCode::Session(\n                raw::LIBSSH2_ERROR_BAD_USE,\n            ))),\n        }\n    }\n\n    #[doc(hidden)]\n    pub fn close(&mut self) -> Result<(), Error> {\n        let rc = {\n            let locked = self.lock()?;\n            self.rc(&locked, unsafe {\n                raw::libssh2_sftp_close_handle(locked.raw)\n            })\n        };\n\n        // If EGAIN was returned, we'll need to call this again to complete the operation.\n        // If any other error was returned, or if it completed OK, we must not use the\n        // handle again.\n        match rc {\n            Err(e) if e.code() == ErrorCode::Session(raw::LIBSSH2_ERROR_EAGAIN) => Err(e),\n            rc => {\n                self.inner = None;\n                rc\n            }\n        }\n    }\n\n    fn rc(&self, locked: &LockedFile, rc: libc::c_int) -> Result<(), Error> {\n        if let Some(file_inner) = self.inner.as_ref() {\n            let sftp_inner = file_inner.sftp.0.as_ref().expect(\n                \"We are holding an Arc<SftpInnerDropWrapper>, \\\n                        so nobody could unset this (set on creation)\",\n            );\n            Sftp::error_code_into_result(locked.sess.raw, sftp_inner.raw, rc)\n        } else if rc < 0 {\n            Err(Error::from_errno(ErrorCode::Session(rc)))\n        } else {\n            Ok(())\n        }\n    }\n}\n\nimpl Read for File {\n    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {\n        let locked = self.lock()?;\n        let rc = unsafe {\n            raw::libssh2_sftp_read(locked.raw, buf.as_mut_ptr() as *mut _, buf.len() as size_t)\n        };\n        if rc < 0 {\n            let rc = rc as libc::c_int;\n            if let Some(file_inner) = self.inner.as_ref() {\n                let sftp_inner = file_inner.sftp.0.as_ref().expect(\n                    \"We are holding an Arc<SftpInnerDropWrapper>, \\\n                        so nobody could unset this (set on creation)\",\n                );\n                Err(Sftp::error_code_into_error(locked.sess.raw, sftp_inner.raw, rc).into())\n            } else {\n                Err(Error::from_errno(ErrorCode::Session(rc)).into())\n            }\n        } else {\n            Ok(rc as usize)\n        }\n    }\n}\n\nimpl Write for File {\n    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {\n        let locked = self.lock()?;\n        let rc = unsafe {\n            raw::libssh2_sftp_write(locked.raw, buf.as_ptr() as *const _, buf.len() as size_t)\n        };\n        if rc < 0 {\n            let rc = rc as libc::c_int;\n            if let Some(file_inner) = self.inner.as_ref() {\n                let sftp_inner = file_inner.sftp.0.as_ref().expect(\n                    \"We are holding an Arc<SftpInnerDropWrapper>, \\\n                        so nobody could unset this (set on creation)\",\n                );\n                Err(Sftp::error_code_into_error(locked.sess.raw, sftp_inner.raw, rc).into())\n            } else {\n                Err(Error::from_errno(ErrorCode::Session(rc)).into())\n            }\n        } else {\n            Ok(rc as usize)\n        }\n    }\n\n    fn flush(&mut self) -> io::Result<()> {\n        Ok(())\n    }\n}\n\nimpl Seek for File {\n    /// Move the file handle's internal pointer to an arbitrary location.\n    ///\n    /// libssh2 implements file pointers as a localized concept to make file\n    /// access appear more POSIX like. No packets are exchanged with the server\n    /// during a seek operation. The localized file pointer is simply used as a\n    /// convenience offset during read/write operations.\n    ///\n    /// You MUST NOT seek during writing or reading a file with SFTP, as the\n    /// internals use outstanding packets and changing the \"file position\"\n    /// during transit will results in badness.\n    fn seek(&mut self, how: SeekFrom) -> io::Result<u64> {\n        let next = match how {\n            SeekFrom::Start(pos) => pos,\n            SeekFrom::Current(offset) => {\n                let locked = self.lock()?;\n                let cur = unsafe { raw::libssh2_sftp_tell64(locked.raw) };\n                (cur as i64 + offset) as u64\n            }\n            SeekFrom::End(offset) => match self.stat() {\n                Ok(s) => match s.size {\n                    Some(size) => (size as i64 + offset) as u64,\n                    None => return Err(io::Error::new(ErrorKind::Other, \"no file size available\")),\n                },\n                Err(e) => return Err(io::Error::new(ErrorKind::Other, e)),\n            },\n        };\n        let locked = self.lock()?;\n        unsafe { raw::libssh2_sftp_seek64(locked.raw, next) }\n        Ok(next)\n    }\n}\n\nimpl Drop for File {\n    fn drop(&mut self) {\n        // Set ssh2 to blocking if the file was not closed yet (by .close()).\n        if let Some(file_inner) = self.inner.take() {\n            let sftp_inner = file_inner.sftp.0.as_ref().expect(\n                \"We are holding an Arc<SftpInnerDropWrapper>, \\\n                    so nobody could unset this (set on creation)\",\n            );\n            let sess_inner = sftp_inner.sess.lock();\n            let was_blocking = sess_inner.is_blocking();\n            sess_inner.set_blocking(true);\n            // The close statement can go wrong and return an error code, but we are too late\n            // in the execution to recover it.\n            let _close_handle_result = unsafe { raw::libssh2_sftp_close_handle(file_inner.raw) };\n            sess_inner.set_blocking(was_blocking);\n        }\n    }\n}\n\nimpl FileStat {\n    /// Returns the file type for this filestat.\n    pub fn file_type(&self) -> FileType {\n        FileType::from_perm(self.perm.unwrap_or(0) as c_ulong)\n    }\n\n    /// Returns whether this metadata is for a directory.\n    pub fn is_dir(&self) -> bool {\n        self.file_type().is_dir()\n    }\n\n    /// Returns whether this metadata is for a regular file.\n    pub fn is_file(&self) -> bool {\n        self.file_type().is_file()\n    }\n\n    /// Creates a new instance of a stat from a raw instance.\n    pub fn from_raw(raw: &raw::LIBSSH2_SFTP_ATTRIBUTES) -> FileStat {\n        fn val<T: Copy>(raw: &raw::LIBSSH2_SFTP_ATTRIBUTES, t: &T, flag: c_ulong) -> Option<T> {\n            if raw.flags & flag != 0 {\n                Some(*t)\n            } else {\n                None\n            }\n        }\n\n        FileStat {\n            size: val(raw, &raw.filesize, raw::LIBSSH2_SFTP_ATTR_SIZE),\n            uid: val(raw, &raw.uid, raw::LIBSSH2_SFTP_ATTR_UIDGID).map(|s| s as u32),\n            gid: val(raw, &raw.gid, raw::LIBSSH2_SFTP_ATTR_UIDGID).map(|s| s as u32),\n            perm: val(raw, &raw.permissions, raw::LIBSSH2_SFTP_ATTR_PERMISSIONS).map(|s| s as u32),\n            mtime: val(raw, &raw.mtime, raw::LIBSSH2_SFTP_ATTR_ACMODTIME).map(|s| s as u64),\n            atime: val(raw, &raw.atime, raw::LIBSSH2_SFTP_ATTR_ACMODTIME).map(|s| s as u64),\n        }\n    }\n\n    /// Convert this stat structure to its raw representation.\n    pub fn raw(&self) -> raw::LIBSSH2_SFTP_ATTRIBUTES {\n        fn flag<T>(o: &Option<T>, flag: c_ulong) -> c_ulong {\n            if o.is_some() {\n                flag\n            } else {\n                0\n            }\n        }\n\n        raw::LIBSSH2_SFTP_ATTRIBUTES {\n            flags: flag(&self.size, raw::LIBSSH2_SFTP_ATTR_SIZE)\n                | flag(&self.uid, raw::LIBSSH2_SFTP_ATTR_UIDGID)\n                | flag(&self.gid, raw::LIBSSH2_SFTP_ATTR_UIDGID)\n                | flag(&self.perm, raw::LIBSSH2_SFTP_ATTR_PERMISSIONS)\n                | flag(&self.atime, raw::LIBSSH2_SFTP_ATTR_ACMODTIME)\n                | flag(&self.mtime, raw::LIBSSH2_SFTP_ATTR_ACMODTIME),\n            filesize: self.size.unwrap_or(0),\n            uid: self.uid.unwrap_or(0) as c_ulong,\n            gid: self.gid.unwrap_or(0) as c_ulong,\n            permissions: self.perm.unwrap_or(0) as c_ulong,\n            atime: self.atime.unwrap_or(0) as c_ulong,\n            mtime: self.mtime.unwrap_or(0) as c_ulong,\n        }\n    }\n}\n\nimpl FileType {\n    /// Test whether this file type represents a directory.\n    pub fn is_dir(&self) -> bool {\n        self == &FileType::Directory\n    }\n\n    /// Test whether this file type represents a regular file.\n    pub fn is_file(&self) -> bool {\n        self == &FileType::RegularFile\n    }\n\n    /// Test whether this file type represents a symbolic link.\n    pub fn is_symlink(&self) -> bool {\n        self == &FileType::Symlink\n    }\n\n    fn from_perm(perm: c_ulong) -> Self {\n        match perm & raw::LIBSSH2_SFTP_S_IFMT {\n            raw::LIBSSH2_SFTP_S_IFIFO => FileType::NamedPipe,\n            raw::LIBSSH2_SFTP_S_IFCHR => FileType::CharDevice,\n            raw::LIBSSH2_SFTP_S_IFDIR => FileType::Directory,\n            raw::LIBSSH2_SFTP_S_IFBLK => FileType::BlockDevice,\n            raw::LIBSSH2_SFTP_S_IFREG => FileType::RegularFile,\n            raw::LIBSSH2_SFTP_S_IFLNK => FileType::Symlink,\n            raw::LIBSSH2_SFTP_S_IFSOCK => FileType::Socket,\n            other => FileType::Other(other),\n        }\n    }\n}\n\n#[cfg(unix)]\nfn mkpath(v: Vec<u8>) -> PathBuf {\n    use std::ffi::OsStr;\n    use std::os::unix::prelude::*;\n    PathBuf::from(OsStr::from_bytes(&v))\n}\n#[cfg(windows)]\nfn mkpath(v: Vec<u8>) -> PathBuf {\n    use std::str;\n    PathBuf::from(str::from_utf8(&v).unwrap())\n}\n"
  },
  {
    "path": "src/util.rs",
    "content": "use std::borrow::Cow;\nuse std::path::Path;\n\nuse {raw, Error, ErrorCode};\n\n#[cfg(unix)]\npub fn path2bytes(p: &Path) -> Result<Cow<'_, [u8]>, Error> {\n    use std::ffi::OsStr;\n    use std::os::unix::prelude::*;\n    let s: &OsStr = p.as_ref();\n    check(Cow::Borrowed(s.as_bytes()))\n}\n#[cfg(windows)]\npub fn path2bytes(p: &Path) -> Result<Cow<'_, [u8]>, Error> {\n    p.to_str()\n        .map(|s| s.as_bytes())\n        .ok_or_else(|| {\n            Error::new(\n                ErrorCode::Session(raw::LIBSSH2_ERROR_INVAL),\n                \"only unicode paths on windows may be used\",\n            )\n        })\n        .map(|bytes| {\n            if bytes.contains(&b'\\\\') {\n                // Normalize to Unix-style path separators\n                let mut bytes = bytes.to_owned();\n                for b in &mut bytes {\n                    if *b == b'\\\\' {\n                        *b = b'/';\n                    }\n                }\n                Cow::Owned(bytes)\n            } else {\n                Cow::Borrowed(bytes)\n            }\n        })\n        .and_then(check)\n}\n\nfn check(b: Cow<[u8]>) -> Result<Cow<[u8]>, Error> {\n    if b.iter().any(|b| *b == 0) {\n        Err(Error::new(\n            ErrorCode::Session(raw::LIBSSH2_ERROR_INVAL),\n            \"path provided contains a 0 byte\",\n        ))\n    } else {\n        Ok(b)\n    }\n}\n"
  },
  {
    "path": "systest/Cargo.toml",
    "content": "[package]\nname = \"systest\"\nversion = \"0.1.0\"\nauthors = [\"Alex Crichton <alex@alexcrichton.com>\"]\nbuild = \"build.rs\"\n\n[dependencies]\nlibssh2-sys = { path = \"../libssh2-sys\" }\nlibc = \"0.2\"\n\n[build-dependencies]\nctest2 = \"0.4\"\n"
  },
  {
    "path": "systest/build.rs",
    "content": "extern crate ctest2;\n\nuse std::env;\n\nfn main() {\n    let mut cfg = ctest2::TestGenerator::new();\n    cfg.header(\"libssh2.h\")\n        .header(\"libssh2_publickey.h\")\n        .header(\"libssh2_sftp.h\")\n        .include(env::var(\"DEP_SSH2_INCLUDE\").unwrap())\n        .type_name(|s, is_struct, _is_union| {\n            if s == \"stat\" {\n                // Ensure that we emit `struct stat` rather than just a `stat` typedef.\n                format!(\"struct stat\")\n            } else if s == \"libssh2_struct_stat\" {\n                // libssh2_struct_stat is a typedef so ensure that we don't emit\n                // `struct libssh2_struct_stat` in the C code we generate\n                s.to_string()\n            } else if is_struct && !s.starts_with(\"LIB\") {\n                // Otherwise we prefer to emit `struct foo` unless the type is `LIB_XXX`\n                format!(\"struct {}\", s)\n            } else {\n                // All other cases: just emit the type name\n                s.to_string()\n            }\n        })\n        .skip_type(|t| t.ends_with(\"FUNC\") || t.contains(\"KBDINT\"))\n        .skip_fn(|f| {\n            f == \"libssh2_userauth_password_ex\"\n                || f == \"libssh2_session_init_ex\"\n                || f == \"libssh2_userauth_keyboard_interactive_ex\"\n        });\n    cfg.generate(\"../libssh2-sys/lib.rs\", \"all.rs\");\n}\n"
  },
  {
    "path": "systest/src/main.rs",
    "content": "#![allow(bad_style, improper_ctypes)]\n\nextern crate libc;\nextern crate libssh2_sys;\n\nuse libc::*;\nuse libssh2_sys::*;\n\ninclude!(concat!(env!(\"OUT_DIR\"), \"/all.rs\"));\n"
  },
  {
    "path": "tests/all/agent.rs",
    "content": "use ssh2::Session;\n\n#[test]\nfn smoke() {\n    let sess = Session::new().unwrap();\n    let mut agent = sess.agent().unwrap();\n    agent.connect().unwrap();\n    agent.list_identities().unwrap();\n    {\n        let a = agent.identities().unwrap();\n        let i1 = &a[0];\n        assert!(agent.userauth(\"foo\", &i1).is_err());\n    }\n    agent.disconnect().unwrap();\n}\n"
  },
  {
    "path": "tests/all/channel.rs",
    "content": "use ssh2::Channel;\nuse std::io::prelude::*;\nuse std::net::{TcpListener, TcpStream};\nuse std::thread;\n\n/// Consume all available stdout and stderr data.\n/// It is important to read both if you are using\n/// channel.eof() to make assertions that the stream\n/// is complete\nfn consume_stdio(channel: &mut Channel) -> (String, String) {\n    let mut stdout = String::new();\n    channel.read_to_string(&mut stdout).unwrap();\n\n    let mut stderr = String::new();\n    channel.stderr().read_to_string(&mut stderr).unwrap();\n\n    eprintln!(\"stdout: {}\", stdout);\n    eprintln!(\"stderr: {}\", stderr);\n\n    (stdout, stderr)\n}\n\n#[test]\nfn smoke() {\n    let sess = ::authed_session();\n    let mut channel = sess.channel_session().unwrap();\n\n    fn must_be_send<T: Send>(_: &T) -> bool {\n        true\n    }\n    assert!(must_be_send(&channel));\n    assert!(must_be_send(&channel.stream(0)));\n\n    channel.flush().unwrap();\n    channel.exec(\"true\").unwrap();\n    consume_stdio(&mut channel);\n\n    channel.wait_eof().unwrap();\n    assert!(channel.eof());\n\n    channel.close().unwrap();\n    channel.wait_close().unwrap();\n    assert_eq!(channel.exit_status().unwrap(), 0);\n    assert!(channel.eof());\n}\n\n#[test]\nfn agent_forward() {\n    let sess = ::authed_session();\n    let mut channel = sess.channel_session().unwrap();\n    channel.request_auth_agent_forwarding().unwrap();\n    channel.exec(\"echo $SSH_AUTH_SOCK\").unwrap();\n\n    let (output, _) = consume_stdio(&mut channel);\n    let output = output.trim();\n    // make sure that the sock is set\n    assert_ne!(output, \"\");\n    // and that it isn't just inherited the one we set for this\n    // test environment\n    assert_ne!(output, std::env::var(\"SSH_AUTH_SOCK\").unwrap());\n}\n\n#[test]\nfn bad_smoke() {\n    let sess = ::authed_session();\n    let mut channel = sess.channel_session().unwrap();\n    channel.flush().unwrap();\n    channel.exec(\"false\").unwrap();\n    consume_stdio(&mut channel);\n\n    channel.wait_eof().unwrap();\n    assert!(channel.eof());\n\n    channel.close().unwrap();\n    channel.wait_close().unwrap();\n    assert_eq!(channel.exit_status().unwrap(), 1);\n    assert!(channel.eof());\n}\n\n#[test]\nfn reading_data() {\n    let sess = ::authed_session();\n    let mut channel = sess.channel_session().unwrap();\n    channel.exec(\"echo foo\").unwrap();\n\n    let (output, _) = consume_stdio(&mut channel);\n    assert_eq!(output, \"foo\\n\");\n}\n\n#[test]\nfn handle_extended_data() {\n    let sess = ::authed_session();\n    let mut channel = sess.channel_session().unwrap();\n    channel\n        .handle_extended_data(ssh2::ExtendedData::Merge)\n        .unwrap();\n    channel.exec(\"echo foo >&2\").unwrap();\n    let (output, _) = consume_stdio(&mut channel);\n    // This is an ends_with test because stderr may have several\n    // lines of misc output on travis macos hosts; it appears as\n    // though the local shell configuration on travis macos is\n    // broken and contributes to this :-/\n    assert!(output.ends_with(\"foo\\n\"));\n}\n\n#[test]\nfn writing_data() {\n    let sess = ::authed_session();\n    let mut channel = sess.channel_session().unwrap();\n    channel.exec(\"read foo && echo $foo\").unwrap();\n    channel.write_all(b\"foo\\n\").unwrap();\n\n    let (output, _) = consume_stdio(&mut channel);\n    assert_eq!(output, \"foo\\n\");\n}\n\n#[test]\nfn eof() {\n    let sess = ::authed_session();\n    let mut channel = sess.channel_session().unwrap();\n    channel.adjust_receive_window(10, false).unwrap();\n    channel.exec(\"read foo\").unwrap();\n    channel.send_eof().unwrap();\n    let mut output = String::new();\n    channel.read_to_string(&mut output).unwrap();\n    assert_eq!(output, \"\");\n}\n\n#[test]\nfn shell() {\n    let sess = ::authed_session();\n    let mut channel = sess.channel_session().unwrap();\n    eprintln!(\"requesting pty\");\n    channel.request_pty(\"xterm\", None, None).unwrap();\n    eprintln!(\"shell\");\n    channel.shell().unwrap();\n    eprintln!(\"close\");\n    channel.close().unwrap();\n    eprintln!(\"done\");\n    consume_stdio(&mut channel);\n}\n\n#[test]\nfn setenv() {\n    let sess = ::authed_session();\n    let mut channel = sess.channel_session().unwrap();\n    let _ = channel.setenv(\"FOO\", \"BAR\");\n    channel.close().unwrap();\n}\n\n#[test]\nfn direct() {\n    let a = TcpListener::bind(\"127.0.0.1:0\").unwrap();\n    let addr = a.local_addr().unwrap();\n    let t = thread::spawn(move || {\n        let mut s = a.accept().unwrap().0;\n        let mut b = [0, 0, 0];\n        s.read(&mut b).unwrap();\n        assert_eq!(b, [1, 2, 3]);\n        s.write_all(&[4, 5, 6]).unwrap();\n    });\n    let sess = ::authed_session();\n    let mut channel = sess\n        .channel_direct_tcpip(\"127.0.0.1\", addr.port(), None)\n        .unwrap();\n    channel.write_all(&[1, 2, 3]).unwrap();\n    let mut r = [0, 0, 0];\n    channel.read(&mut r).unwrap();\n    assert_eq!(r, [4, 5, 6]);\n    t.join().ok().unwrap();\n}\n\n#[cfg(all(unix))]\n#[test]\nfn direct_stream_local() {\n    use std::os::unix::net::UnixListener;\n\n    let d = tempfile::tempdir().unwrap();\n    let path = d.path().join(\"ssh2-rs-test.sock\");\n    let a = UnixListener::bind(&path).unwrap();\n    let t = thread::spawn(move || {\n        let mut s = a.accept().unwrap().0;\n        let mut b = [0, 0, 0];\n        s.read(&mut b).unwrap();\n        assert_eq!(b, [1, 2, 3]);\n        s.write_all(&[4, 5, 6]).unwrap();\n    });\n    let sess = ::authed_session();\n    let mut channel = sess\n        .channel_direct_streamlocal(path.to_str().unwrap(), None)\n        .unwrap();\n    channel.write_all(&[1, 2, 3]).unwrap();\n    let mut r = [0, 0, 0];\n    channel.read(&mut r).unwrap();\n    assert_eq!(r, [4, 5, 6]);\n    t.join().ok().unwrap();\n}\n\n#[test]\nfn forward() {\n    let sess = ::authed_session();\n    let (mut listen, port) = sess.channel_forward_listen(39249, None, None).unwrap();\n    let t = thread::spawn(move || {\n        let mut s = TcpStream::connect(&(\"127.0.0.1\", port)).unwrap();\n        let mut b = [0, 0, 0];\n        s.read(&mut b).unwrap();\n        assert_eq!(b, [1, 2, 3]);\n        s.write_all(&[4, 5, 6]).unwrap();\n    });\n\n    let mut channel = listen.accept().unwrap();\n    channel.write_all(&[1, 2, 3]).unwrap();\n    let mut r = [0, 0, 0];\n    channel.read(&mut r).unwrap();\n    assert_eq!(r, [4, 5, 6]);\n    t.join().ok().unwrap();\n}\n\n#[test]\nfn drop_nonblocking() {\n    let listener = TcpListener::bind(\"127.0.0.1:0\").unwrap();\n    let addr = listener.local_addr().unwrap();\n\n    let sess = ::authed_session();\n    sess.set_blocking(false);\n\n    thread::spawn(move || {\n        let _s = listener.accept().unwrap();\n    });\n\n    let _ = sess.channel_direct_tcpip(\"127.0.0.1\", addr.port(), None);\n    drop(sess);\n}\n\n#[test]\nfn nonblocking_before_exit_code() {\n    let sess = ::authed_session();\n    let mut channel = sess.channel_session().unwrap();\n    channel.send_eof().unwrap();\n    let mut output = String::new();\n\n    channel.exec(\"sleep 1; echo foo\").unwrap();\n    sess.set_blocking(false);\n    assert!(channel.read_to_string(&mut output).is_err());\n    {\n        use std::thread;\n        use std::time::Duration;\n        thread::sleep(Duration::from_millis(1500));\n    }\n    sess.set_blocking(true);\n    assert!(channel.read_to_string(&mut output).is_ok());\n\n    channel.wait_eof().unwrap();\n    channel.close().unwrap();\n    channel.wait_close().unwrap();\n    assert_eq!(output, \"foo\\n\");\n    assert!(channel.exit_status().unwrap() == 0);\n}\n\n#[test]\nfn exit_code_ignores_other_errors() {\n    let sess = ::authed_session();\n    let mut channel = sess.channel_session().unwrap();\n    channel.exec(\"true\").unwrap();\n    channel.wait_eof().unwrap();\n    channel.close().unwrap();\n    channel.wait_close().unwrap();\n    let longdescription: String = ::std::iter::repeat('a').take(300).collect();\n    assert!(sess.disconnect(None, &longdescription, None).is_err()); // max len == 256\n    assert!(channel.exit_status().unwrap() == 0);\n}\n\n#[test]\nfn pty_modes_are_propagated() {\n    let sess = ::authed_session();\n    let mut channel = sess.channel_session().unwrap();\n    eprintln!(\"requesting pty\");\n\n    let mut mode = ssh2::PtyModes::new();\n    // intr is typically CTRL-C; setting it to unmodified `y`\n    // should be very high signal that it took effect\n    mode.set_character(ssh2::PtyModeOpcode::VINTR, Some('y'));\n\n    channel.request_pty(\"xterm\", Some(mode), None).unwrap();\n    channel.exec(\"stty -a\").unwrap();\n\n    let (out, _err) = consume_stdio(&mut channel);\n    channel.close().unwrap();\n\n    // This may well be linux specific\n    assert!(out.contains(\"intr = y\"), \"mode was propagated\");\n}\n"
  },
  {
    "path": "tests/all/knownhosts.rs",
    "content": "use ssh2::{KnownHostFileKind, Session};\n\n#[test]\nfn smoke() {\n    let sess = Session::new().unwrap();\n    let known_hosts = sess.known_hosts().unwrap();\n    let hosts = known_hosts.hosts().unwrap();\n    assert_eq!(hosts.len(), 0);\n}\n\n#[test]\nfn reading() {\n    let encoded = \"\\\n|1|VXwDpq2cv4j3QtmrGiY+HntJc+Q=|80E+wqnFDhkxBDxRBOIPJPAVE6Y= \\\nssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9I\\\nDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVD\\\nBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eF\\\nzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKS\\\nCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2R\\\nPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi\\\n/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==\n\";\n    let sess = Session::new().unwrap();\n    let mut known_hosts = sess.known_hosts().unwrap();\n    known_hosts\n        .read_str(encoded, KnownHostFileKind::OpenSSH)\n        .unwrap();\n\n    let hosts = known_hosts.hosts().unwrap();\n    assert_eq!(hosts.len(), 1);\n    let host = &hosts[0];\n    assert_eq!(host.name(), None);\n    assert_eq!(\n        host.key(),\n        \"\\\n         AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9I\\\n         DSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVD\\\n         BfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eF\\\n         zLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKS\\\n         CZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2R\\\n         PW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi\\\n         /w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==\"\n    );\n\n    assert_eq!(\n        known_hosts\n            .write_string(host, KnownHostFileKind::OpenSSH)\n            .unwrap(),\n        encoded\n    );\n    known_hosts.remove(host).unwrap();\n}\n"
  },
  {
    "path": "tests/all/main.rs",
    "content": "#![deny(warnings)]\n\nextern crate ssh2;\nextern crate tempfile;\n\nuse std::env;\nuse std::net::TcpStream;\n\nmod agent;\nmod channel;\nmod knownhosts;\nmod session;\nmod sftp;\n\npub fn test_addr() -> String {\n    let port = env::var(\"RUST_SSH2_FIXTURE_PORT\")\n        .map(|s| s.parse().unwrap())\n        .unwrap_or(22);\n    let addr = format!(\"127.0.0.1:{}\", port);\n    addr\n}\n\npub fn socket() -> TcpStream {\n    TcpStream::connect(&test_addr()).unwrap()\n}\n\npub fn authed_session() -> ssh2::Session {\n    let user = env::var(\"USER\").unwrap();\n    let socket = socket();\n    let mut sess = ssh2::Session::new().unwrap();\n    sess.set_tcp_stream(socket);\n    sess.handshake().unwrap();\n    assert!(!sess.authenticated());\n\n    {\n        let mut agent = sess.agent().unwrap();\n        agent.connect().unwrap();\n        agent.list_identities().unwrap();\n        let identities = agent.identities().unwrap();\n        let identity = &identities[0];\n        agent.userauth(&user, &identity).unwrap();\n    }\n    assert!(sess.authenticated());\n    sess\n}\n"
  },
  {
    "path": "tests/all/session.rs",
    "content": "use std::env;\nuse std::fs::File;\nuse std::io::{self, prelude::*};\nuse std::path::Path;\nuse tempfile::TempDir;\n\nuse ssh2::{BlockDirections, HashType, KeyboardInteractivePrompt, MethodType, Prompt, Session};\n\n#[test]\nfn session_is_send() {\n    fn must_be_send<T: Send>(_: &T) -> bool {\n        true\n    }\n\n    let sess = Session::new().unwrap();\n    assert!(must_be_send(&sess));\n}\n\n#[test]\nfn smoke() {\n    let sess = Session::new().unwrap();\n    assert!(sess.banner_bytes().is_none());\n    sess.set_banner(\"foo\").unwrap();\n    assert!(sess.is_blocking());\n    assert_eq!(sess.timeout(), 0);\n    sess.set_compress(true);\n    assert!(sess.host_key().is_none());\n    sess.method_pref(MethodType::Kex, \"diffie-hellman-group14-sha1\")\n        .unwrap();\n    assert!(sess.methods(MethodType::Kex).is_none());\n    sess.set_blocking(true);\n    sess.set_timeout(0);\n    sess.supported_algs(MethodType::Kex).unwrap();\n    sess.supported_algs(MethodType::HostKey).unwrap();\n    sess.channel_session().err().unwrap();\n}\n\n#[test]\nfn smoke_handshake() {\n    let user = env::var(\"USER\").unwrap();\n    let socket = ::socket();\n    let mut sess = Session::new().unwrap();\n    sess.set_tcp_stream(socket);\n    sess.handshake().unwrap();\n    sess.host_key().unwrap();\n    let methods = sess.auth_methods(&user).unwrap();\n    assert!(methods.contains(\"publickey\"), \"{}\", methods);\n    assert!(!sess.authenticated());\n\n    let mut agent = sess.agent().unwrap();\n    agent.connect().unwrap();\n    agent.list_identities().unwrap();\n    {\n        let identity = &agent.identities().unwrap()[0];\n        agent.userauth(&user, &identity).unwrap();\n    }\n    assert!(sess.authenticated());\n    sess.host_key_hash(HashType::Md5).unwrap();\n}\n\n#[test]\nfn smoke_userauth_banner() {\n    let user = env::var(\"USER\").unwrap();\n    let socket = ::socket();\n    let mut sess = Session::new().unwrap();\n    sess.set_tcp_stream(socket);\n    sess.handshake().unwrap();\n    sess.host_key().unwrap();\n    sess.auth_methods(&user).unwrap();\n    let banner = sess.userauth_banner().unwrap().unwrap();\n    assert_eq!(banner, \"Authorized access only!\");\n}\n\n#[test]\nfn keyboard_interactive() {\n    let user = env::var(\"USER\").unwrap();\n    let socket = ::socket();\n    let mut sess = Session::new().unwrap();\n    sess.set_tcp_stream(socket);\n    sess.handshake().unwrap();\n    sess.host_key().unwrap();\n    let methods = sess.auth_methods(&user).unwrap();\n    assert!(\n        methods.contains(\"keyboard-interactive\"),\n        \"test server ({}) must support `ChallengeResponseAuthentication yes`, not just {}\",\n        ::test_addr(),\n        methods\n    );\n    assert!(!sess.authenticated());\n\n    // We don't know the correct response for whatever challenges\n    // will be returned to us, but that's ok; the purpose of this\n    // test is to check that we have some basically sane interaction\n    // with the library.\n\n    struct Prompter {\n        some_data: usize,\n    }\n\n    impl KeyboardInteractivePrompt for Prompter {\n        fn prompt<'a>(\n            &mut self,\n            username: &str,\n            instructions: &str,\n            prompts: &[Prompt<'a>],\n        ) -> Vec<String> {\n            // Sanity check that the pointer manipulation resolves and\n            // we read back our member data ok\n            assert_eq!(self.some_data, 42);\n\n            eprintln!(\"username: {}\", username);\n            eprintln!(\"instructions: {}\", instructions);\n            eprintln!(\"prompts: {:?}\", prompts);\n\n            // Unfortunately, we can't make any assertions about username\n            // or instructions, as they can be empty (on my linux system)\n            // or may have arbitrary contents\n            // assert_eq!(username, env::var(\"USER\").unwrap());\n            // assert!(!instructions.is_empty());\n\n            // Hopefully this isn't too brittle an assertion\n            if prompts.len() == 1 {\n                assert_eq!(prompts.len(), 1);\n                // Might be \"Password: \" or \"Password:\" or other variations\n                assert!(prompts[0].text.contains(\"sword\"));\n                assert_eq!(prompts[0].echo, false);\n            } else {\n                // maybe there's some PAM configuration that results\n                // in multiple prompts. We can't make any real assertions\n                // in this case, other than that there has to be at least\n                // one prompt.\n                assert!(!prompts.is_empty());\n            }\n\n            prompts.iter().map(|_| \"bogus\".to_string()).collect()\n        }\n    }\n\n    let mut p = Prompter { some_data: 42 };\n\n    match sess.userauth_keyboard_interactive(&user, &mut p) {\n        Ok(_) => eprintln!(\"auth succeeded somehow(!)\"),\n        Err(err) => eprintln!(\"auth failed as expected: {}\", err),\n    };\n\n    // The only way this assertion will be false is if the person\n    // running these tests has \"bogus\" as their password\n    assert!(!sess.authenticated());\n}\n\n#[test]\nfn keepalive() {\n    let sess = ::authed_session();\n    sess.set_keepalive(false, 10);\n    sess.keepalive_send().unwrap();\n}\n\n#[test]\nfn scp_recv() {\n    let sess = ::authed_session();\n\n    // Download our own source file; it's the only path that\n    // we know for sure exists on this system.\n    let p = Path::new(file!()).canonicalize().unwrap();\n\n    let (mut ch, _) = sess.scp_recv(&p).unwrap();\n    let mut data = String::new();\n    ch.read_to_string(&mut data).unwrap();\n    let mut expected = String::new();\n    File::open(&p)\n        .unwrap()\n        .read_to_string(&mut expected)\n        .unwrap();\n    assert!(data == expected);\n}\n\n#[test]\nfn scp_send() {\n    let td = TempDir::new().unwrap();\n    let sess = ::authed_session();\n    let mut ch = sess\n        .scp_send(&td.path().join(\"foo\"), 0o644, 6, None)\n        .unwrap();\n    ch.write_all(b\"foobar\").unwrap();\n    drop(ch);\n    let mut actual = Vec::new();\n    File::open(&td.path().join(\"foo\"))\n        .unwrap()\n        .read_to_end(&mut actual)\n        .unwrap();\n    assert_eq!(actual, b\"foobar\");\n}\n\n#[test]\nfn block_directions() {\n    let mut sess = ::authed_session();\n    sess.set_blocking(false);\n    let actual = sess.handshake().map_err(|e| io::Error::from(e).kind());\n    assert_eq!(actual, Err(io::ErrorKind::WouldBlock));\n    assert_eq!(sess.block_directions(), BlockDirections::Inbound);\n}\n"
  },
  {
    "path": "tests/all/sftp.rs",
    "content": "use std::fs::{self, File};\nuse std::io::prelude::*;\nuse tempfile::TempDir;\n\n#[test]\nfn smoke() {\n    let sess = ::authed_session();\n    sess.sftp().unwrap();\n}\n\n#[test]\nfn ops() {\n    let td = TempDir::new().unwrap();\n    File::create(&td.path().join(\"foo\")).unwrap();\n    fs::create_dir(&td.path().join(\"bar\")).unwrap();\n\n    let sess = ::authed_session();\n    let sftp = sess.sftp().unwrap();\n    let path = td.path().join(\"bar\");\n    let path_str: &str = path.to_str().unwrap();\n    sftp.opendir(&path).unwrap();\n    sftp.opendir(path_str).unwrap();\n    let mut foo = sftp.open(&td.path().join(\"foo\")).unwrap();\n    sftp.mkdir(&td.path().join(\"bar2\"), 0o755).unwrap();\n    assert!(fs::metadata(&td.path().join(\"bar2\"))\n        .map(|m| m.is_dir())\n        .unwrap_or(false));\n    sftp.rmdir(&td.path().join(\"bar2\")).unwrap();\n\n    sftp.create(&td.path().join(\"foo5\"))\n        .unwrap()\n        .write_all(b\"foo\")\n        .unwrap();\n    let mut v = Vec::new();\n    File::open(&td.path().join(\"foo5\"))\n        .unwrap()\n        .read_to_end(&mut v)\n        .unwrap();\n    assert_eq!(v, b\"foo\");\n\n    assert_eq!(sftp.stat(&td.path().join(\"foo\")).unwrap().size, Some(0));\n    v.truncate(0);\n    foo.read_to_end(&mut v).unwrap();\n    assert_eq!(v, Vec::new());\n\n    sftp.symlink(&td.path().join(\"foo\"), &td.path().join(\"foo2\"))\n        .unwrap();\n    let readlink = sftp.readlink(&td.path().join(\"foo2\")).unwrap();\n    assert!(readlink == td.path().join(\"foo\"));\n    let realpath = sftp.realpath(&td.path().join(\"foo2\")).unwrap();\n    assert_eq!(realpath, td.path().join(\"foo\").canonicalize().unwrap());\n\n    let path = td.path();\n    let path_str: &str = path.to_str().unwrap();\n    let files = sftp.readdir(path).unwrap();\n    let files_from_str = sftp.readdir(path_str).unwrap();\n    for (f1, f2) in files.iter().zip(files_from_str.iter()) {\n        assert_eq!(f1, f2);\n    }\n}\n\n#[test]\nfn not_found() {\n    let td = TempDir::new().unwrap();\n\n    let sess = ::authed_session();\n    let sftp = sess.sftp().unwrap();\n\n    // Can't use unwrap_err here since File does not impl Debug.\n    let err = sftp\n        .opendir(&td.path().join(\"nonexistent\"))\n        .err()\n        .expect(\"open nonexistent dir\");\n    assert_eq!(err.to_string(), \"[SFTP(2)] no such file\");\n\n    let io_err: std::io::Error = err.into();\n    assert_eq!(io_err.kind(), std::io::ErrorKind::NotFound);\n    assert_eq!(io_err.to_string(), \"no such file\");\n\n    let err = sftp\n        .stat(&td.path().join(\"nonexistent\"))\n        .err()\n        .expect(\"stat nonexistent\");\n    assert_eq!(err.to_string(), \"[SFTP(2)] no such file\");\n    let io_err: std::io::Error = err.into();\n    assert_eq!(io_err.kind(), std::io::ErrorKind::NotFound);\n    assert_eq!(io_err.to_string(), \"no such file\");\n}\n"
  },
  {
    "path": "tests/run_integration_tests.sh",
    "content": "#!/bin/bash\nset -e\nset -x\n\n# This script spawns an ssh daemon with a known configuration so that we can\n# test various functionality against it.\n\n# Tell the tests to use the port number we're using to spawn this server\nexport RUST_SSH2_FIXTURE_PORT=8022\nexport RUST_BACKTRACE=1\n\ncleanup() {\n  # Stop the ssh server and local ssh agent\n  kill $(< $SSHDIR/sshd.pid) $SSH_AGENT_PID || true\n\n  test -f $SSHDIR/sshd.log && cat $SSHDIR/sshd.log\n}\ntrap cleanup EXIT\n\n# Blow away any prior state and re-configure our test server\nSSHDIR=$(pwd)/tests/sshd\n\nrm -rf $SSHDIR\nmkdir -p $SSHDIR\n\neval $(ssh-agent -s)\n\nssh-keygen -t rsa -f $SSHDIR/id_rsa -N \"\" -q\nchmod 0600 $SSHDIR/id_rsa*\nssh-add $SSHDIR/id_rsa\ncp $SSHDIR/id_rsa.pub $SSHDIR/authorized_keys\n\nssh-keygen -f $SSHDIR/ssh_host_rsa_key -N '' -t rsa\n\necho -n \"Authorized access only!\" > $SSHDIR/banner\n\ncat > $SSHDIR/sshd_config <<-EOT\nAuthorizedKeysFile=$SSHDIR/authorized_keys\nHostKey=$SSHDIR/ssh_host_rsa_key\nPidFile=$SSHDIR/sshd.pid\nBanner $SSHDIR/banner\nSubsystem sftp internal-sftp\nUsePAM yes\nX11Forwarding yes\nUsePrivilegeSeparation no\nPrintMotd yes\nPermitTunnel yes\nKbdInteractiveAuthentication yes\nAllowTcpForwarding yes\nMaxStartups 500\n# Relax modes when the repo is under eg: /var/tmp\nStrictModes no\nEOT\n\ncat $SSHDIR/sshd_config\n\n# Start an ssh server\n/usr/sbin/sshd -p $RUST_SSH2_FIXTURE_PORT -f $SSHDIR/sshd_config -E $SSHDIR/sshd.log\n# Give it a moment to start up\nsleep 2\n\n# Run the tests against it\ncargo test --all -- --nocapture\ncargo test --features vendored-openssl -- --nocapture\n"
  }
]