net-ssh 1.1.4 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +37 -0
- data/Manifest +101 -0
- data/README.rdoc +110 -0
- data/Rakefile +26 -0
- data/{THANKS → THANKS.rdoc} +2 -5
- data/lib/net/ssh.rb +189 -57
- data/lib/net/ssh/authentication/agent.rb +175 -0
- data/lib/net/ssh/authentication/constants.rb +18 -0
- data/lib/net/ssh/authentication/key_manager.rb +166 -0
- data/lib/net/ssh/authentication/methods/abstract.rb +60 -0
- data/lib/net/ssh/authentication/methods/hostbased.rb +71 -0
- data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +66 -0
- data/lib/net/ssh/authentication/methods/password.rb +39 -0
- data/lib/net/ssh/authentication/methods/publickey.rb +92 -0
- data/lib/net/ssh/authentication/pageant.rb +176 -0
- data/lib/net/ssh/authentication/session.rb +116 -0
- data/lib/net/ssh/buffer.rb +339 -0
- data/lib/net/ssh/buffered_io.rb +149 -0
- data/lib/net/ssh/config.rb +173 -0
- data/lib/net/ssh/connection/channel.rb +575 -454
- data/lib/net/ssh/connection/constants.rb +31 -45
- data/lib/net/ssh/connection/session.rb +569 -0
- data/lib/net/ssh/connection/term.rb +176 -88
- data/lib/net/ssh/errors.rb +83 -61
- data/lib/net/ssh/key_factory.rb +85 -0
- data/lib/net/ssh/known_hosts.rb +129 -0
- data/lib/net/ssh/loggable.rb +61 -0
- data/lib/net/ssh/packet.rb +102 -0
- data/lib/net/ssh/prompt.rb +93 -0
- data/lib/net/ssh/proxy/errors.rb +8 -28
- data/lib/net/ssh/proxy/http.rb +75 -107
- data/lib/net/ssh/proxy/socks4.rb +35 -48
- data/lib/net/ssh/proxy/socks5.rb +76 -108
- data/lib/net/ssh/service/forward.rb +267 -0
- data/lib/net/ssh/test.rb +89 -0
- data/lib/net/ssh/test/channel.rb +129 -0
- data/lib/net/ssh/test/extensions.rb +152 -0
- data/lib/net/ssh/test/kex.rb +44 -0
- data/lib/net/ssh/test/local_packet.rb +51 -0
- data/lib/net/ssh/test/packet.rb +81 -0
- data/lib/net/ssh/test/remote_packet.rb +38 -0
- data/lib/net/ssh/test/script.rb +157 -0
- data/lib/net/ssh/test/socket.rb +59 -0
- data/lib/net/ssh/transport/algorithms.rb +384 -0
- data/lib/net/ssh/transport/cipher_factory.rb +72 -0
- data/lib/net/ssh/transport/constants.rb +22 -58
- data/lib/net/ssh/transport/hmac.rb +31 -0
- data/lib/net/ssh/transport/hmac/abstract.rb +48 -0
- data/lib/net/ssh/transport/hmac/md5.rb +12 -0
- data/lib/net/ssh/transport/hmac/md5_96.rb +11 -0
- data/lib/net/ssh/transport/hmac/none.rb +15 -0
- data/lib/net/ssh/transport/hmac/sha1.rb +13 -0
- data/lib/net/ssh/transport/hmac/sha1_96.rb +11 -0
- data/lib/net/ssh/transport/identity_cipher.rb +40 -0
- data/lib/net/ssh/transport/kex.rb +13 -0
- data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +208 -0
- data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +77 -0
- data/lib/net/ssh/{util → transport}/openssl.rb +22 -40
- data/lib/net/ssh/transport/packet_stream.rb +230 -0
- data/lib/net/ssh/transport/server_version.rb +61 -0
- data/lib/net/ssh/transport/session.rb +225 -303
- data/lib/net/ssh/transport/state.rb +170 -0
- data/lib/net/ssh/verifiers/lenient.rb +30 -0
- data/lib/net/ssh/verifiers/null.rb +12 -0
- data/lib/net/ssh/verifiers/strict.rb +53 -0
- data/lib/net/ssh/version.rb +57 -26
- data/net-ssh.gemspec +54 -0
- data/setup.rb +1585 -0
- data/test/authentication/methods/common.rb +28 -0
- data/test/authentication/methods/test_abstract.rb +51 -0
- data/test/authentication/methods/test_hostbased.rb +108 -0
- data/test/authentication/methods/test_keyboard_interactive.rb +98 -0
- data/test/authentication/methods/test_password.rb +50 -0
- data/test/authentication/methods/test_publickey.rb +123 -0
- data/test/authentication/test_agent.rb +205 -0
- data/test/authentication/test_key_manager.rb +100 -0
- data/test/authentication/test_session.rb +93 -0
- data/test/common.rb +106 -0
- data/test/configs/exact_match +8 -0
- data/test/configs/wild_cards +14 -0
- data/test/connection/test_channel.rb +452 -0
- data/test/connection/test_session.rb +483 -0
- data/test/test_all.rb +6 -0
- data/test/test_buffer.rb +336 -0
- data/test/test_buffered_io.rb +63 -0
- data/test/test_config.rb +78 -0
- data/test/test_key_factory.rb +67 -0
- data/test/transport/hmac/test_md5.rb +34 -0
- data/test/transport/hmac/test_md5_96.rb +25 -0
- data/test/transport/hmac/test_none.rb +34 -0
- data/test/transport/hmac/test_sha1.rb +34 -0
- data/test/transport/hmac/test_sha1_96.rb +25 -0
- data/test/transport/kex/test_diffie_hellman_group1_sha1.rb +146 -0
- data/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +92 -0
- data/test/transport/test_algorithms.rb +302 -0
- data/test/transport/test_cipher_factory.rb +163 -0
- data/test/transport/test_hmac.rb +34 -0
- data/test/transport/test_identity_cipher.rb +40 -0
- data/test/transport/test_packet_stream.rb +433 -0
- data/test/transport/test_server_version.rb +55 -0
- data/test/transport/test_session.rb +312 -0
- data/test/transport/test_state.rb +173 -0
- metadata +102 -253
- data/ChangeLog +0 -560
- data/LICENSE +0 -7
- data/NEWS +0 -152
- data/README +0 -14
- data/bin/rb-keygen +0 -210
- data/doc/LICENSE-BSD +0 -27
- data/doc/LICENSE-GPL +0 -280
- data/doc/LICENSE-RUBY +0 -56
- data/doc/manual-html/chapter-1.html +0 -388
- data/doc/manual-html/chapter-2.html +0 -552
- data/doc/manual-html/chapter-3.html +0 -470
- data/doc/manual-html/chapter-4.html +0 -413
- data/doc/manual-html/chapter-5.html +0 -525
- data/doc/manual-html/chapter-6.html +0 -456
- data/doc/manual-html/chapter-7.html +0 -343
- data/doc/manual-html/index.html +0 -235
- data/doc/manual-html/stylesheets/manual.css +0 -270
- data/doc/manual-html/stylesheets/ruby.css +0 -17
- data/doc/manual/chapter.erb +0 -38
- data/doc/manual/example.erb +0 -18
- data/doc/manual/index.erb +0 -29
- data/doc/manual/manual.rb +0 -311
- data/doc/manual/manual.yml +0 -73
- data/doc/manual/page.erb +0 -87
- data/doc/manual/parts/0000.txt +0 -5
- data/doc/manual/parts/0001.txt +0 -3
- data/doc/manual/parts/0002.txt +0 -40
- data/doc/manual/parts/0003.txt +0 -6
- data/doc/manual/parts/0004.txt +0 -7
- data/doc/manual/parts/0005.txt +0 -1
- data/doc/manual/parts/0006.txt +0 -49
- data/doc/manual/parts/0007.txt +0 -67
- data/doc/manual/parts/0008.txt +0 -43
- data/doc/manual/parts/0009.txt +0 -14
- data/doc/manual/parts/0010.txt +0 -7
- data/doc/manual/parts/0011.txt +0 -14
- data/doc/manual/parts/0012.txt +0 -3
- data/doc/manual/parts/0013.txt +0 -20
- data/doc/manual/parts/0014.txt +0 -32
- data/doc/manual/parts/0015.txt +0 -14
- data/doc/manual/parts/0016.txt +0 -28
- data/doc/manual/parts/0017.txt +0 -50
- data/doc/manual/parts/0018.txt +0 -35
- data/doc/manual/parts/0019.txt +0 -7
- data/doc/manual/parts/0020.txt +0 -72
- data/doc/manual/parts/0021.txt +0 -50
- data/doc/manual/parts/0022.txt +0 -42
- data/doc/manual/parts/0023.txt +0 -51
- data/doc/manual/parts/0024.txt +0 -18
- data/doc/manual/parts/0025.txt +0 -18
- data/doc/manual/parts/0026.txt +0 -15
- data/doc/manual/parts/0027.txt +0 -37
- data/doc/manual/parts/0028.txt +0 -16
- data/doc/manual/parts/0029.txt +0 -1
- data/doc/manual/parts/0030.txt +0 -52
- data/doc/manual/parts/0031.txt +0 -25
- data/doc/manual/stylesheets/manual.css +0 -270
- data/doc/manual/stylesheets/ruby.css +0 -17
- data/doc/manual/tutorial.erb +0 -30
- data/examples/auth-forward.rb +0 -41
- data/examples/channel-demo.rb +0 -81
- data/examples/port-forward.rb +0 -51
- data/examples/process-demo.rb +0 -91
- data/examples/remote-net-port-forward.rb +0 -45
- data/examples/remote-port-forward.rb +0 -80
- data/examples/shell-demo.rb +0 -46
- data/examples/ssh-client.rb +0 -67
- data/examples/sync-shell-demo.rb +0 -69
- data/examples/tail-demo.rb +0 -49
- data/lib/net/ssh/connection/driver.rb +0 -446
- data/lib/net/ssh/connection/services.rb +0 -72
- data/lib/net/ssh/host-key-verifier.rb +0 -52
- data/lib/net/ssh/known-hosts.rb +0 -96
- data/lib/net/ssh/lenient-host-key-verifier.rb +0 -25
- data/lib/net/ssh/null-host-key-verifier.rb +0 -14
- data/lib/net/ssh/service/agentforward/driver.rb +0 -78
- data/lib/net/ssh/service/agentforward/services.rb +0 -41
- data/lib/net/ssh/service/forward/driver.rb +0 -319
- data/lib/net/ssh/service/forward/local-network-handler.rb +0 -71
- data/lib/net/ssh/service/forward/remote-network-handler.rb +0 -83
- data/lib/net/ssh/service/forward/services.rb +0 -76
- data/lib/net/ssh/service/process/driver.rb +0 -153
- data/lib/net/ssh/service/process/open.rb +0 -193
- data/lib/net/ssh/service/process/popen3.rb +0 -178
- data/lib/net/ssh/service/process/services.rb +0 -66
- data/lib/net/ssh/service/services.rb +0 -60
- data/lib/net/ssh/service/shell/driver.rb +0 -86
- data/lib/net/ssh/service/shell/services.rb +0 -54
- data/lib/net/ssh/service/shell/shell.rb +0 -222
- data/lib/net/ssh/service/shell/sync.rb +0 -114
- data/lib/net/ssh/session.rb +0 -305
- data/lib/net/ssh/transport/algorithm-negotiator.rb +0 -275
- data/lib/net/ssh/transport/compress/compressor.rb +0 -53
- data/lib/net/ssh/transport/compress/decompressor.rb +0 -53
- data/lib/net/ssh/transport/compress/none-compressor.rb +0 -39
- data/lib/net/ssh/transport/compress/none-decompressor.rb +0 -39
- data/lib/net/ssh/transport/compress/services.rb +0 -68
- data/lib/net/ssh/transport/compress/zlib-compressor.rb +0 -60
- data/lib/net/ssh/transport/compress/zlib-decompressor.rb +0 -52
- data/lib/net/ssh/transport/errors.rb +0 -47
- data/lib/net/ssh/transport/identity-cipher.rb +0 -61
- data/lib/net/ssh/transport/kex/dh-gex.rb +0 -106
- data/lib/net/ssh/transport/kex/dh.rb +0 -249
- data/lib/net/ssh/transport/kex/services.rb +0 -62
- data/lib/net/ssh/transport/ossl/buffer-factory.rb +0 -52
- data/lib/net/ssh/transport/ossl/buffer.rb +0 -87
- data/lib/net/ssh/transport/ossl/cipher-factory.rb +0 -98
- data/lib/net/ssh/transport/ossl/digest-factory.rb +0 -51
- data/lib/net/ssh/transport/ossl/hmac-factory.rb +0 -71
- data/lib/net/ssh/transport/ossl/hmac/hmac.rb +0 -62
- data/lib/net/ssh/transport/ossl/hmac/md5-96.rb +0 -44
- data/lib/net/ssh/transport/ossl/hmac/md5.rb +0 -46
- data/lib/net/ssh/transport/ossl/hmac/none.rb +0 -46
- data/lib/net/ssh/transport/ossl/hmac/services.rb +0 -68
- data/lib/net/ssh/transport/ossl/hmac/sha1-96.rb +0 -44
- data/lib/net/ssh/transport/ossl/hmac/sha1.rb +0 -45
- data/lib/net/ssh/transport/ossl/key-factory.rb +0 -116
- data/lib/net/ssh/transport/ossl/services.rb +0 -149
- data/lib/net/ssh/transport/packet-stream.rb +0 -236
- data/lib/net/ssh/transport/services.rb +0 -146
- data/lib/net/ssh/transport/version-negotiator.rb +0 -73
- data/lib/net/ssh/userauth/agent.rb +0 -222
- data/lib/net/ssh/userauth/constants.rb +0 -35
- data/lib/net/ssh/userauth/driver.rb +0 -183
- data/lib/net/ssh/userauth/methods/hostbased.rb +0 -119
- data/lib/net/ssh/userauth/methods/keyboard-interactive.rb +0 -104
- data/lib/net/ssh/userauth/methods/password.rb +0 -70
- data/lib/net/ssh/userauth/methods/publickey.rb +0 -137
- data/lib/net/ssh/userauth/methods/services.rb +0 -90
- data/lib/net/ssh/userauth/pageant.rb +0 -197
- data/lib/net/ssh/userauth/services.rb +0 -141
- data/lib/net/ssh/userauth/userkeys.rb +0 -258
- data/lib/net/ssh/util/buffer.rb +0 -274
- data/lib/net/ssh/util/prompter.rb +0 -73
- data/test/ALL-TESTS.rb +0 -18
- data/test/connection/tc_channel.rb +0 -136
- data/test/connection/tc_driver.rb +0 -287
- data/test/connection/tc_integration.rb +0 -87
- data/test/proxy/tc_http.rb +0 -209
- data/test/proxy/tc_socks4.rb +0 -148
- data/test/proxy/tc_socks5.rb +0 -214
- data/test/service/agentforward/tc_driver.rb +0 -138
- data/test/service/forward/tc_driver.rb +0 -289
- data/test/service/forward/tc_local_network_handler.rb +0 -123
- data/test/service/forward/tc_remote_network_handler.rb +0 -111
- data/test/service/process/tc_driver.rb +0 -79
- data/test/service/process/tc_integration.rb +0 -119
- data/test/service/process/tc_open.rb +0 -179
- data/test/service/process/tc_popen3.rb +0 -164
- data/test/tc_integration.rb +0 -80
- data/test/transport/compress/tc_none_compress.rb +0 -41
- data/test/transport/compress/tc_none_decompress.rb +0 -45
- data/test/transport/compress/tc_zlib_compress.rb +0 -61
- data/test/transport/compress/tc_zlib_decompress.rb +0 -48
- data/test/transport/kex/tc_dh.rb +0 -312
- data/test/transport/kex/tc_dh_gex.rb +0 -71
- data/test/transport/ossl/fixtures/dsa-encrypted +0 -15
- data/test/transport/ossl/fixtures/dsa-encrypted-bad +0 -15
- data/test/transport/ossl/fixtures/dsa-unencrypted +0 -12
- data/test/transport/ossl/fixtures/dsa-unencrypted-bad +0 -12
- data/test/transport/ossl/fixtures/dsa-unencrypted.pub +0 -1
- data/test/transport/ossl/fixtures/not-a-private-key +0 -4
- data/test/transport/ossl/fixtures/not-supported +0 -2
- data/test/transport/ossl/fixtures/rsa-encrypted +0 -18
- data/test/transport/ossl/fixtures/rsa-encrypted-bad +0 -18
- data/test/transport/ossl/fixtures/rsa-unencrypted +0 -15
- data/test/transport/ossl/fixtures/rsa-unencrypted-bad +0 -15
- data/test/transport/ossl/fixtures/rsa-unencrypted.pub +0 -1
- data/test/transport/ossl/hmac/tc_hmac.rb +0 -58
- data/test/transport/ossl/hmac/tc_md5.rb +0 -50
- data/test/transport/ossl/hmac/tc_md5_96.rb +0 -50
- data/test/transport/ossl/hmac/tc_none.rb +0 -50
- data/test/transport/ossl/hmac/tc_sha1.rb +0 -50
- data/test/transport/ossl/hmac/tc_sha1_96.rb +0 -50
- data/test/transport/ossl/tc_buffer.rb +0 -97
- data/test/transport/ossl/tc_buffer_factory.rb +0 -67
- data/test/transport/ossl/tc_cipher_factory.rb +0 -84
- data/test/transport/ossl/tc_digest_factory.rb +0 -39
- data/test/transport/ossl/tc_hmac_factory.rb +0 -72
- data/test/transport/ossl/tc_key_factory.rb +0 -199
- data/test/transport/tc_algorithm_negotiator.rb +0 -170
- data/test/transport/tc_identity_cipher.rb +0 -52
- data/test/transport/tc_integration.rb +0 -115
- data/test/transport/tc_packet_stream.rb +0 -184
- data/test/transport/tc_session.rb +0 -296
- data/test/transport/tc_version_negotiator.rb +0 -86
- data/test/userauth/methods/tc_hostbased.rb +0 -136
- data/test/userauth/methods/tc_password.rb +0 -89
- data/test/userauth/methods/tc_publickey.rb +0 -167
- data/test/userauth/tc_agent.rb +0 -223
- data/test/userauth/tc_driver.rb +0 -190
- data/test/userauth/tc_integration.rb +0 -97
- data/test/userauth/tc_userkeys.rb +0 -265
- data/test/util/tc_buffer.rb +0 -217
data/CHANGELOG.rdoc
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
=== 2.0.0 / 1 May 2008
|
2
|
+
|
3
|
+
* Allow the :verbose argument to accept symbols (:debug, etc.) as well as Logger level constants (Logger::DEBUG, etc.) [Jamis Buck]
|
4
|
+
|
5
|
+
|
6
|
+
=== 2.0 Preview Release 4 (1.99.3) / 19 Apr 2008
|
7
|
+
|
8
|
+
* Make sure HOME is set to something sane, even on OS's that don't set it by default [Jamis Buck]
|
9
|
+
|
10
|
+
* Add a :passphrase option to specify the passphrase to use with private keys [Francis Sullivan]
|
11
|
+
|
12
|
+
* Open a new auth agent connection for every auth-agent channel request [Jamis Buck]
|
13
|
+
|
14
|
+
|
15
|
+
=== 2.0 Preview Release 3 (1.99.2) / 10 Apr 2008
|
16
|
+
|
17
|
+
* Session properties [Jamis Buck]
|
18
|
+
|
19
|
+
* Make channel open failure work with a callback so that failures can be handled similarly to successes [Jamis Buck]
|
20
|
+
|
21
|
+
|
22
|
+
=== 2.0 Preview Release 2 (1.99.1) / 22 Mar 2008
|
23
|
+
|
24
|
+
* Partial support for ~/.ssh/config (and related) SSH configuration files [Daniel J. Berger, Jamis Buck]
|
25
|
+
|
26
|
+
* Added Net::SSH::Test to facilitate testing complex SSH state machines [Jamis Buck]
|
27
|
+
|
28
|
+
* Reworked Net::SSH::Prompt to use conditionally-selected modules [Jamis Buck, suggested by James Rosen]
|
29
|
+
|
30
|
+
* Added Channel#eof? and Channel#eof! [Jamis Buck]
|
31
|
+
|
32
|
+
* Fixed bug in strict host key verifier on cache miss [Mike Timm]
|
33
|
+
|
34
|
+
|
35
|
+
=== 2.0 Preview Release 1 (1.99.0) / 21 Aug 2007
|
36
|
+
|
37
|
+
* First preview release of Net::SSH v2
|
data/Manifest
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
CHANGELOG.rdoc
|
2
|
+
lib/net/ssh/authentication/agent.rb
|
3
|
+
lib/net/ssh/authentication/constants.rb
|
4
|
+
lib/net/ssh/authentication/key_manager.rb
|
5
|
+
lib/net/ssh/authentication/methods/abstract.rb
|
6
|
+
lib/net/ssh/authentication/methods/hostbased.rb
|
7
|
+
lib/net/ssh/authentication/methods/keyboard_interactive.rb
|
8
|
+
lib/net/ssh/authentication/methods/password.rb
|
9
|
+
lib/net/ssh/authentication/methods/publickey.rb
|
10
|
+
lib/net/ssh/authentication/pageant.rb
|
11
|
+
lib/net/ssh/authentication/session.rb
|
12
|
+
lib/net/ssh/buffer.rb
|
13
|
+
lib/net/ssh/buffered_io.rb
|
14
|
+
lib/net/ssh/config.rb
|
15
|
+
lib/net/ssh/connection/channel.rb
|
16
|
+
lib/net/ssh/connection/constants.rb
|
17
|
+
lib/net/ssh/connection/session.rb
|
18
|
+
lib/net/ssh/connection/term.rb
|
19
|
+
lib/net/ssh/errors.rb
|
20
|
+
lib/net/ssh/key_factory.rb
|
21
|
+
lib/net/ssh/known_hosts.rb
|
22
|
+
lib/net/ssh/loggable.rb
|
23
|
+
lib/net/ssh/packet.rb
|
24
|
+
lib/net/ssh/prompt.rb
|
25
|
+
lib/net/ssh/proxy/errors.rb
|
26
|
+
lib/net/ssh/proxy/http.rb
|
27
|
+
lib/net/ssh/proxy/socks4.rb
|
28
|
+
lib/net/ssh/proxy/socks5.rb
|
29
|
+
lib/net/ssh/service/forward.rb
|
30
|
+
lib/net/ssh/test/channel.rb
|
31
|
+
lib/net/ssh/test/extensions.rb
|
32
|
+
lib/net/ssh/test/kex.rb
|
33
|
+
lib/net/ssh/test/local_packet.rb
|
34
|
+
lib/net/ssh/test/packet.rb
|
35
|
+
lib/net/ssh/test/remote_packet.rb
|
36
|
+
lib/net/ssh/test/script.rb
|
37
|
+
lib/net/ssh/test/socket.rb
|
38
|
+
lib/net/ssh/test.rb
|
39
|
+
lib/net/ssh/transport/algorithms.rb
|
40
|
+
lib/net/ssh/transport/cipher_factory.rb
|
41
|
+
lib/net/ssh/transport/constants.rb
|
42
|
+
lib/net/ssh/transport/hmac/abstract.rb
|
43
|
+
lib/net/ssh/transport/hmac/md5.rb
|
44
|
+
lib/net/ssh/transport/hmac/md5_96.rb
|
45
|
+
lib/net/ssh/transport/hmac/none.rb
|
46
|
+
lib/net/ssh/transport/hmac/sha1.rb
|
47
|
+
lib/net/ssh/transport/hmac/sha1_96.rb
|
48
|
+
lib/net/ssh/transport/hmac.rb
|
49
|
+
lib/net/ssh/transport/identity_cipher.rb
|
50
|
+
lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb
|
51
|
+
lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb
|
52
|
+
lib/net/ssh/transport/kex.rb
|
53
|
+
lib/net/ssh/transport/openssl.rb
|
54
|
+
lib/net/ssh/transport/packet_stream.rb
|
55
|
+
lib/net/ssh/transport/server_version.rb
|
56
|
+
lib/net/ssh/transport/session.rb
|
57
|
+
lib/net/ssh/transport/state.rb
|
58
|
+
lib/net/ssh/verifiers/lenient.rb
|
59
|
+
lib/net/ssh/verifiers/null.rb
|
60
|
+
lib/net/ssh/verifiers/strict.rb
|
61
|
+
lib/net/ssh/version.rb
|
62
|
+
lib/net/ssh.rb
|
63
|
+
Rakefile
|
64
|
+
README.rdoc
|
65
|
+
setup.rb
|
66
|
+
test/authentication/methods/common.rb
|
67
|
+
test/authentication/methods/test_abstract.rb
|
68
|
+
test/authentication/methods/test_hostbased.rb
|
69
|
+
test/authentication/methods/test_keyboard_interactive.rb
|
70
|
+
test/authentication/methods/test_password.rb
|
71
|
+
test/authentication/methods/test_publickey.rb
|
72
|
+
test/authentication/test_agent.rb
|
73
|
+
test/authentication/test_key_manager.rb
|
74
|
+
test/authentication/test_session.rb
|
75
|
+
test/common.rb
|
76
|
+
test/configs/exact_match
|
77
|
+
test/configs/wild_cards
|
78
|
+
test/connection/test_channel.rb
|
79
|
+
test/connection/test_session.rb
|
80
|
+
test/test_all.rb
|
81
|
+
test/test_buffer.rb
|
82
|
+
test/test_buffered_io.rb
|
83
|
+
test/test_config.rb
|
84
|
+
test/test_key_factory.rb
|
85
|
+
test/transport/hmac/test_md5.rb
|
86
|
+
test/transport/hmac/test_md5_96.rb
|
87
|
+
test/transport/hmac/test_none.rb
|
88
|
+
test/transport/hmac/test_sha1.rb
|
89
|
+
test/transport/hmac/test_sha1_96.rb
|
90
|
+
test/transport/kex/test_diffie_hellman_group1_sha1.rb
|
91
|
+
test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb
|
92
|
+
test/transport/test_algorithms.rb
|
93
|
+
test/transport/test_cipher_factory.rb
|
94
|
+
test/transport/test_hmac.rb
|
95
|
+
test/transport/test_identity_cipher.rb
|
96
|
+
test/transport/test_packet_stream.rb
|
97
|
+
test/transport/test_server_version.rb
|
98
|
+
test/transport/test_session.rb
|
99
|
+
test/transport/test_state.rb
|
100
|
+
THANKS.rdoc
|
101
|
+
Manifest
|
data/README.rdoc
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
= Net::SSH
|
2
|
+
|
3
|
+
* http://net-ssh.rubyforge.org/ssh
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
Net::SSH is a pure-Ruby implementation of the SSH2 client protocol. It allows you to write programs that invoke and interact with processes on remote servers, via SSH2.
|
8
|
+
|
9
|
+
== FEATURES:
|
10
|
+
|
11
|
+
* Execute processes on remote servers and capture their output
|
12
|
+
* Run multiple processes in parallel over a single SSH connection
|
13
|
+
* Support for SSH subsystems
|
14
|
+
* Forward local and remote ports via an SSH connection
|
15
|
+
|
16
|
+
== SYNOPSIS:
|
17
|
+
|
18
|
+
In a nutshell:
|
19
|
+
|
20
|
+
require 'net/ssh'
|
21
|
+
|
22
|
+
Net::SSH.start('host', 'user', :password => "password") do |ssh|
|
23
|
+
# capture all stderr and stdout output from a remote process
|
24
|
+
output = ssh.exec!("hostname")
|
25
|
+
|
26
|
+
# capture only stdout matching a particular pattern
|
27
|
+
stdout = ""
|
28
|
+
ssh.exec!("ls -l /home/jamis") do |channel, stream, data|
|
29
|
+
stdout << data if stream == :stdout
|
30
|
+
end
|
31
|
+
puts stdout
|
32
|
+
|
33
|
+
# run multiple processes in parallel to completion
|
34
|
+
ssh.exec "sed ..."
|
35
|
+
ssh.exec "awk ..."
|
36
|
+
ssh.exec "rm -rf ..."
|
37
|
+
ssh.loop
|
38
|
+
|
39
|
+
# open a new channel and configure a minimal set of callbacks, then run
|
40
|
+
# the event loop until the channel finishes (closes)
|
41
|
+
channel = ssh.open_channel do |ch|
|
42
|
+
ch.exec "/usr/local/bin/ruby /path/to/file.rb" do |ch, success|
|
43
|
+
raise "could not execute command" unless success
|
44
|
+
|
45
|
+
# "on_data" is called when the process writes something to stdout
|
46
|
+
ch.on_data do |c, data|
|
47
|
+
$STDOUT.print data
|
48
|
+
end
|
49
|
+
|
50
|
+
# "on_extended_data" is called when the process writes something to stderr
|
51
|
+
ch.on_extended_data do |c, type, data|
|
52
|
+
$STDERR.print data
|
53
|
+
end
|
54
|
+
|
55
|
+
ch.on_close { puts "done!" }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
channel.wait
|
60
|
+
|
61
|
+
# forward connections on local port 1234 to port 80 of www.capify.org
|
62
|
+
ssh.forward.local(1234, "www.capify.org", 80)
|
63
|
+
ssh.loop { true }
|
64
|
+
end
|
65
|
+
|
66
|
+
See Net::SSH for more documentation, and links to further information.
|
67
|
+
|
68
|
+
== REQUIREMENTS:
|
69
|
+
|
70
|
+
The only requirement you might be missing is the OpenSSL bindings for Ruby. These are built by default on most platforms, but you can verify that they're built and installed on your system by running the following command line:
|
71
|
+
|
72
|
+
ruby -ropenssl -e 'puts OpenSSL::OPENSSL_VERSION'
|
73
|
+
|
74
|
+
If that spits out something like "OpenSSL 0.9.8g 19 Oct 2007", then you're set. If you get an error, then you'll need to see about rebuilding ruby with OpenSSL support, or (if your platform supports it) installing the OpenSSL bindings separately.
|
75
|
+
|
76
|
+
Additionally: if you are going to be having Net::SSH prompt you for things like passwords or certificate passphrases, you'll want to have either the Highline (recommended) or Termios (unix systems only) gem installed, so that the passwords don't echo in clear text.
|
77
|
+
|
78
|
+
Lastly, if you want to run the tests or use any of the Rake tasks, you'll need:
|
79
|
+
|
80
|
+
* Echoe (for the Rakefile)
|
81
|
+
* Mocha (for the tests)
|
82
|
+
|
83
|
+
== INSTALL:
|
84
|
+
|
85
|
+
* gem install net-ssh (might need sudo privileges)
|
86
|
+
|
87
|
+
== LICENSE:
|
88
|
+
|
89
|
+
(The MIT License)
|
90
|
+
|
91
|
+
Copyright (c) 2008 Jamis Buck <jamis@37signals.com>
|
92
|
+
|
93
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
94
|
+
a copy of this software and associated documentation files (the
|
95
|
+
'Software'), to deal in the Software without restriction, including
|
96
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
97
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
98
|
+
permit persons to whom the Software is furnished to do so, subject to
|
99
|
+
the following conditions:
|
100
|
+
|
101
|
+
The above copyright notice and this permission notice shall be
|
102
|
+
included in all copies or substantial portions of the Software.
|
103
|
+
|
104
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
105
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
106
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
107
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
108
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
109
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
110
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require './lib/net/ssh/version'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'echoe'
|
5
|
+
rescue LoadError
|
6
|
+
abort "You'll need to have `echoe' installed to use Net::SSH's Rakefile"
|
7
|
+
end
|
8
|
+
|
9
|
+
version = Net::SSH::Version::STRING.dup
|
10
|
+
if ENV['SNAPSHOT'].to_i == 1
|
11
|
+
version << "." << Time.now.utc.strftime("%Y%m%d%H%M%S")
|
12
|
+
end
|
13
|
+
|
14
|
+
Echoe.new('net-ssh', version) do |p|
|
15
|
+
p.changelog = "CHANGELOG.rdoc"
|
16
|
+
|
17
|
+
p.author = "Jamis Buck"
|
18
|
+
p.email = "jamis@jamisbuck.org"
|
19
|
+
p.summary = "a pure-Ruby implementation of the SSH2 client protocol"
|
20
|
+
p.url = "http://net-ssh.rubyforge.org/ssh"
|
21
|
+
|
22
|
+
p.need_zip = true
|
23
|
+
p.include_rakefile = true
|
24
|
+
|
25
|
+
p.rdoc_pattern = /^(lib|README.rdoc|CHANGELOG.rdoc|THANKS.rdoc)/
|
26
|
+
end
|
data/{THANKS → THANKS.rdoc}
RENAMED
@@ -9,11 +9,8 @@ Guillaume Mar
|
|
9
9
|
* support for communicating with the the PuTTY "pageant" process
|
10
10
|
|
11
11
|
Daniel Berger <djberg96@yahoo.com>
|
12
|
-
* help getting unit tests to pass in Windows
|
12
|
+
* help getting unit tests in earlier Net::SSH versions to pass in Windows
|
13
|
+
* initial version of Net::SSH::Config provided inspiration and encouragement
|
13
14
|
|
14
15
|
Chris Andrews <chris@nodnol.org> and Lee Jensen <lee@outerim.com>
|
15
16
|
* support for ssh agent forwarding
|
16
|
-
|
17
|
-
If you have contributed to Net::SSH and are not on the above list, please do
|
18
|
-
not take offense! Jamis is notoriously forgetful. Instead, just let him know
|
19
|
-
what your contribution was, and he'll be only too happy to add you.
|
data/lib/net/ssh.rb
CHANGED
@@ -1,67 +1,199 @@
|
|
1
|
-
|
2
|
-
#
|
3
|
-
|
4
|
-
# All rights reserved.
|
5
|
-
#
|
6
|
-
# This source file is distributed as part of the Net::SSH Secure Shell Client
|
7
|
-
# library for Ruby. This file (and the library as a whole) may be used only as
|
8
|
-
# allowed by either the BSD license, or the Ruby license (or, by association
|
9
|
-
# with the Ruby license, the GPL). See the "doc" subdirectory of the Net::SSH
|
10
|
-
# distribution for the texts of these licenses.
|
11
|
-
# -----------------------------------------------------------------------------
|
12
|
-
# net-ssh website : http://net-ssh.rubyforge.org
|
13
|
-
# project website: http://rubyforge.org/projects/net-ssh
|
14
|
-
# =============================================================================
|
15
|
-
#++
|
1
|
+
# Make sure HOME is set, regardless of OS, so that File.expand_path works
|
2
|
+
# as expected with tilde characters.
|
3
|
+
ENV['HOME'] ||= ENV['HOMEPATH'] ? "#{ENV['HOMEDRIVE']}#{ENV['HOMEPATH']}" : "."
|
16
4
|
|
17
|
-
require '
|
5
|
+
require 'logger'
|
18
6
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
# 2. Look at net/ssh/transport/services.rb to see how the transport services
|
26
|
-
# are defined, and what classes implement each of them.
|
27
|
-
#
|
28
|
-
# 3. Look at net/ssh/transport/session.rb (Net::SSH::Transport::Session). This
|
29
|
-
# implements the driver for the transport layer.
|
30
|
-
#
|
31
|
-
# 4. Look at net/ssh/userauth/services.rb to see how the services for the
|
32
|
-
# user authentication layer are defined, and implemented.
|
33
|
-
#
|
34
|
-
# 5. Look at net/ssh/connection/services.rb to see how the services for the
|
35
|
-
# connection layer are defined and implemented.
|
36
|
-
#
|
37
|
-
# 6. Look at net/ssh/service/services.rb to see how the auxiliary services
|
38
|
-
# (process and forward) are defined.
|
39
|
-
#++
|
7
|
+
require 'net/ssh/config'
|
8
|
+
require 'net/ssh/errors'
|
9
|
+
require 'net/ssh/loggable'
|
10
|
+
require 'net/ssh/transport/session'
|
11
|
+
require 'net/ssh/authentication/session'
|
12
|
+
require 'net/ssh/connection/session'
|
40
13
|
|
41
14
|
module Net
|
15
|
+
|
16
|
+
# Net::SSH is a library for interacting, programmatically, with remote
|
17
|
+
# processes via the SSH2 protocol. Sessions are always initiated via
|
18
|
+
# Net::SSH.start. From there, a program interacts with the new SSH session
|
19
|
+
# via the convenience methods on Net::SSH::Connection::Session, by opening
|
20
|
+
# and interacting with new channels (Net::SSH::Connection:Session#open_channel
|
21
|
+
# and Net::SSH::Connection::Channel), or by forwarding local and/or
|
22
|
+
# remote ports through the connection (Net::SSH::Service::Forward).
|
23
|
+
#
|
24
|
+
# The SSH protocol is very event-oriented. Requests are sent from the client
|
25
|
+
# to the server, and are answered asynchronously. This gives great flexibility
|
26
|
+
# (since clients can have multiple requests pending at a time), but it also
|
27
|
+
# adds complexity. Net::SSH tries to manage this complexity by providing
|
28
|
+
# some simpler methods of synchronous communication (see Net::SSH::Connection::Session#exec!).
|
29
|
+
#
|
30
|
+
# In general, though, and if you want to do anything more complicated than
|
31
|
+
# simply executing commands and capturing their output, you'll need to use
|
32
|
+
# channels (Net::SSH::Connection::Channel) to build state machines that are
|
33
|
+
# executed while the event loop runs (Net::SSH::Connection::Session#loop).
|
34
|
+
#
|
35
|
+
# Net::SSH::Connection::Session and Net::SSH::Connection::Channel have more
|
36
|
+
# information about this technique.
|
37
|
+
#
|
38
|
+
# = "Um, all I want to do is X, just show me how!"
|
39
|
+
#
|
40
|
+
# == X == "execute a command and capture the output"
|
41
|
+
#
|
42
|
+
# Net::SSH.start("host", "user", :password => "password") do |ssh|
|
43
|
+
# result = ssh.exec!("ls -l")
|
44
|
+
# puts result
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# == X == "forward connections on a local port to a remote host"
|
48
|
+
#
|
49
|
+
# Net::SSH.start("host", "user", :password => "password") do |ssh|
|
50
|
+
# ssh.forward.local(1234, "www.google.com", 80)
|
51
|
+
# ssh.loop { true }
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# == X == "forward connections on a remote port to the local host"
|
55
|
+
#
|
56
|
+
# Net::SSH.start("host", "user", :password => "password") do |ssh|
|
57
|
+
# ssh.forward.remote(80, "www.google.com", 1234)
|
58
|
+
# ssh.loop { true }
|
59
|
+
# end
|
42
60
|
module SSH
|
61
|
+
# This is the set of options that Net::SSH.start recognizes. See
|
62
|
+
# Net::SSH.start for a description of each option.
|
63
|
+
VALID_OPTIONS = [
|
64
|
+
:auth_methods, :compression, :compression_level, :config, :encryption,
|
65
|
+
:forward_agent, :hmac, :host_key, :kex, :keys, :languages,
|
66
|
+
:logger, :paranoid, :password, :port, :proxy, :rekey_blocks_limit,
|
67
|
+
:rekey_limit, :rekey_packet_limit, :timeout, :verbose,
|
68
|
+
:global_known_hosts_file, :user_known_hosts_file, :host_key_alias,
|
69
|
+
:host_name, :user, :properties, :passphrase
|
70
|
+
]
|
43
71
|
|
44
|
-
#
|
45
|
-
#
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
72
|
+
# The standard means of starting a new SSH connection. When used with a
|
73
|
+
# block, the connection will be closed when the block terminates, otherwise
|
74
|
+
# the connection will just be returned. The yielded (or returned) value
|
75
|
+
# will be an instance of Net::SSH::Connection::Session (q.v.). (See also
|
76
|
+
# Net::SSH::Connection::Channel and Net::SSH::Service::Forward.)
|
77
|
+
#
|
78
|
+
# Net::SSH.start("host", "user") do |ssh|
|
79
|
+
# ssh.exec! "cp /some/file /another/location"
|
80
|
+
# hostname = ssh.exec!("hostname")
|
81
|
+
#
|
82
|
+
# ssh.open_channel do |ch|
|
83
|
+
# ch.exec "sudo -p 'sudo password: ' ls" do |ch, success|
|
84
|
+
# abort "could not execute sudo ls" unless success
|
85
|
+
#
|
86
|
+
# ch.on_data do |ch, data|
|
87
|
+
# print data
|
88
|
+
# if data =~ /sudo password: /
|
89
|
+
# ch.send_data("password\n")
|
90
|
+
# end
|
91
|
+
# end
|
92
|
+
# end
|
93
|
+
# end
|
94
|
+
#
|
95
|
+
# ssh.loop
|
96
|
+
# end
|
97
|
+
#
|
98
|
+
# This method accepts the following options (all are optional):
|
99
|
+
#
|
100
|
+
# * :auth_methods => an array of authentication methods to try
|
101
|
+
# * :compression => the compression algorithm to use, or +true+ to use
|
102
|
+
# whatever is supported.
|
103
|
+
# * :compression_level => the compression level to use when sending data
|
104
|
+
# * :config => set to +true+ to load the default OpenSSH config files
|
105
|
+
# (~/.ssh/config, /etc/ssh_config), or to +false+ to not load them, or to
|
106
|
+
# a file-name (or array of file-names) to load those specific configuration
|
107
|
+
# files. Defaults to +true+.
|
108
|
+
# * :encryption => the encryption cipher (or ciphers) to use
|
109
|
+
# * :forward_agent => set to true if you want the SSH agent connection to
|
110
|
+
# be forwarded
|
111
|
+
# * :global_known_hosts_file => the location of the global known hosts
|
112
|
+
# file. Set to an array if you want to specify multiple global known
|
113
|
+
# hosts files. Defaults to %w(/etc/ssh/known_hosts /etc/ssh/known_hosts2).
|
114
|
+
# * :hmac => the hmac algorithm (or algorithms) to use
|
115
|
+
# * :host_key => the host key algorithm (or algorithms) to use
|
116
|
+
# * :host_key_alias => the host name to use when looking up or adding a
|
117
|
+
# host to a known_hosts dictionary file
|
118
|
+
# * :host_name => the real host name or IP to log into. This is used
|
119
|
+
# instead of the +host+ parameter, and is primarily only useful when
|
120
|
+
# specified in an SSH configuration file. It lets you specify an
|
121
|
+
# "alias", similarly to adding an entry in /etc/hosts but without needing
|
122
|
+
# to modify /etc/hosts.
|
123
|
+
# * :kex => the key exchange algorithm (or algorithms) to use
|
124
|
+
# * :keys => an array of file names of private keys to use for publickey
|
125
|
+
# and hostbased authentication
|
126
|
+
# * :logger => the logger instance to use when logging
|
127
|
+
# * :paranoid => either true, false, or :very, specifying how strict
|
128
|
+
# host-key verification should be
|
129
|
+
# * :passphrase => the passphrase to use when loading a private key (default
|
130
|
+
# is +nil+, for no passphrase)
|
131
|
+
# * :password => the password to use to login
|
132
|
+
# * :port => the port to use when connecting to the remote host
|
133
|
+
# * :properties => a hash of key/value pairs to add to the new connection's
|
134
|
+
# properties (see Net::SSH::Connection::Session#properties)
|
135
|
+
# * :proxy => a proxy instance (see Proxy) to use when connecting
|
136
|
+
# * :rekey_blocks_limit => the max number of blocks to process before rekeying
|
137
|
+
# * :rekey_limit => the max number of bytes to process before rekeying
|
138
|
+
# * :rekey_packet_limit => the max number of packets to process before rekeying
|
139
|
+
# * :timeout => how long to wait for the initial connection to be made
|
140
|
+
# * :user => the user name to log in as; this overrides the +user+
|
141
|
+
# parameter, and is primarily only useful when provided via an SSH
|
142
|
+
# configuration file.
|
143
|
+
# * :user_known_hosts_file => the location of the user known hosts file.
|
144
|
+
# Set to an array to specify multiple user known hosts files.
|
145
|
+
# Defaults to %w(~/.ssh/known_hosts ~/.ssh/known_hosts2).
|
146
|
+
# * :verbose => how verbose to be (Logger verbosity constants, Logger::DEBUG
|
147
|
+
# is very verbose, Logger::FATAL is all but silent). Logger::FATAL is the
|
148
|
+
# default. The symbols :debug, :info, :warn, :error, and :fatal are also
|
149
|
+
# supported and are translated to the corresponding Logger constant.
|
150
|
+
def self.start(host, user, options={}, &block)
|
151
|
+
invalid_options = options.keys - VALID_OPTIONS
|
152
|
+
if invalid_options.any?
|
153
|
+
raise ArgumentError, "invalid option(s): #{invalid_options.join(', ')}"
|
154
|
+
end
|
50
155
|
|
51
|
-
|
52
|
-
|
53
|
-
|
156
|
+
files = case options.fetch(:config, true)
|
157
|
+
when true then Net::SSH::Config.default_files
|
158
|
+
when false, nil then []
|
159
|
+
else Array(options[:config])
|
160
|
+
end
|
161
|
+
|
162
|
+
options = Net::SSH::Config.for(host, files).merge(options)
|
163
|
+
host = options.fetch(:host_name, host)
|
54
164
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
#
|
60
|
-
# The +name+ parameter should be a symbol.
|
61
|
-
def register_service( name, &block )
|
62
|
-
EXTERNAL_SERVICES[ name ] = block
|
63
|
-
end
|
64
|
-
module_function :register_service
|
165
|
+
if !options.key?(:logger)
|
166
|
+
options[:logger] = Logger.new(STDERR)
|
167
|
+
options[:logger].level = Logger::FATAL
|
168
|
+
end
|
65
169
|
|
170
|
+
if options[:verbose]
|
171
|
+
options[:logger].level = case options[:verbose]
|
172
|
+
when Fixnum then options[:verbose]
|
173
|
+
when :debug then Logger::DEBUG
|
174
|
+
when :info then Logger::INFO
|
175
|
+
when :warn then Logger::WARN
|
176
|
+
when :error then Logger::ERROR
|
177
|
+
when :fatal then Logger::FATAL
|
178
|
+
else raise ArgumentError, "can't convert #{options[:verbose].inspect} to any of the Logger level constants"
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
transport = Transport::Session.new(host, options)
|
183
|
+
auth = Authentication::Session.new(transport, options)
|
184
|
+
|
185
|
+
user = options.fetch(:user, user)
|
186
|
+
if auth.authenticate("ssh-connection", user, options[:password])
|
187
|
+
connection = Connection::Session.new(transport, options)
|
188
|
+
if block_given?
|
189
|
+
yield connection
|
190
|
+
connection.close
|
191
|
+
else
|
192
|
+
return connection
|
193
|
+
end
|
194
|
+
else
|
195
|
+
raise AuthenticationFailed, user
|
196
|
+
end
|
197
|
+
end
|
66
198
|
end
|
67
|
-
end
|
199
|
+
end
|