aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.classpath3
-rw-r--r--.gitattributes10
-rw-r--r--.gitignore3
-rw-r--r--.project2
-rw-r--r--.settings/org.eclipse.jdt.core.prefs281
-rw-r--r--.settings/org.eclipse.jdt.ui.prefs3
-rw-r--r--AndroidManifest.xml46
-rw-r--r--CHANGELOG.md31
-rw-r--r--ant.properties (renamed from build.properties)10
-rw-r--r--build.xml217
-rw-r--r--connectbot.sublime-project36
-rw-r--r--icons/icon14.pngbin0 -> 461 bytes
-rw-r--r--icons/icon64.pngbin0 -> 1555 bytes
-rw-r--r--jni/Application.mk2
-rw-r--r--jni/Exec/Android.mk1
-rwxr-xr-xlibs/armeabi/libcom_google_ase_Exec.sobin6598 -> 12688 bytes
-rwxr-xr-xlibs/x86/libcom_google_ase_Exec.sobin0 -> 6168 bytes
-rw-r--r--proguard.cfg46
-rw-r--r--proguard.flags9
-rw-r--r--project.properties (renamed from default.properties)6
-rw-r--r--res/drawable-hdpi/icon.pngbin6394 -> 961 bytes
-rw-r--r--res/drawable-hdpi/notification_icon.pngbin719 -> 385 bytes
-rw-r--r--res/drawable-mdpi-v6/icon.pngbin3525 -> 0 bytes
-rw-r--r--res/drawable-mdpi/icon.pngbin5657 -> 646 bytes
-rw-r--r--res/drawable-mdpi/notification_icon.pngbin397 -> 256 bytes
-rw-r--r--res/drawable-xhdpi/icon.pngbin0 -> 1018 bytes
-rw-r--r--res/drawable-xhdpi/notification_icon.pngbin0 -> 380 bytes
-rw-r--r--res/drawable-xxhdpi/icon.pngbin0 -> 1379 bytes
-rw-r--r--res/drawable-xxhdpi/notification_icon.pngbin0 -> 568 bytes
-rw-r--r--res/drawable-xxxhdpi/icon.pngbin0 -> 1777 bytes
-rw-r--r--res/drawable-xxxhdpi/notification_icon.pngbin0 -> 665 bytes
-rw-r--r--res/drawable/icon_older.pngbin5088 -> 0 bytes
-rw-r--r--res/layout-land/item_host.xml3
-rw-r--r--res/layout-port/item_host.xml1
-rw-r--r--res/layout/act_colors.xml57
-rw-r--r--res/layout/act_console.xml3
-rw-r--r--res/layout/act_generatepubkey.xml18
-rw-r--r--res/layout/act_hostlist.xml43
-rw-r--r--res/layout/item_pubkey.xml4
-rw-r--r--res/values-ar/strings.xml3
-rw-r--r--res/values-bg/strings.xml3
-rw-r--r--res/values-ca/strings.xml14
-rw-r--r--res/values-cs/strings.xml46
-rw-r--r--res/values-da/strings.xml105
-rw-r--r--res/values-de/strings.xml51
-rw-r--r--res/values-el/strings.xml6
-rw-r--r--res/values-en-rGB/strings.xml7
-rw-r--r--res/values-es/strings.xml21
-rw-r--r--res/values-eu/strings.xml211
-rw-r--r--res/values-fi/strings.xml34
-rw-r--r--res/values-fr/strings.xml169
-rw-r--r--res/values-gl/strings.xml8
-rw-r--r--res/values-he/strings.xml214
-rw-r--r--res/values-hr/strings.xml92
-rw-r--r--res/values-hu/strings.xml24
-rw-r--r--res/values-id/strings.xml152
-rw-r--r--res/values-is/strings.xml173
-rw-r--r--res/values-it/strings.xml28
-rw-r--r--res/values-ja/strings.xml42
-rw-r--r--res/values-ka/strings.xml15
-rw-r--r--res/values-ko/strings.xml11
-rw-r--r--res/values-mk/strings.xml67
-rw-r--r--res/values-nb/strings.xml108
-rw-r--r--res/values-nl/strings.xml11
-rw-r--r--res/values-oc/strings.xml6
-rw-r--r--res/values-pl/strings.xml106
-rw-r--r--res/values-pt-rBR/strings.xml51
-rw-r--r--res/values-pt/strings.xml64
-rw-r--r--res/values-ro/strings.xml55
-rw-r--r--res/values-ru/strings.xml46
-rw-r--r--res/values-sk/strings.xml171
-rw-r--r--res/values-sl/strings.xml180
-rw-r--r--res/values-sr/strings.xml228
-rw-r--r--res/values-sv/strings.xml97
-rw-r--r--res/values-tr/strings.xml116
-rw-r--r--res/values-v11/styles.xml29
-rw-r--r--res/values-v14/styles.xml29
-rw-r--r--res/values-zh-rCN/strings.xml154
-rw-r--r--res/values-zh-rTW/strings.xml278
-rw-r--r--res/values/arrays.xml16
-rw-r--r--res/values/notrans.xml4
-rw-r--r--res/values/strings.xml94
-rw-r--r--res/values/version.xml4
-rw-r--r--res/xml/host_prefs.xml6
-rw-r--r--res/xml/preferences.xml80
-rw-r--r--src/com/jcraft/jzlib/ZStreamException.java88
-rw-r--r--src/com/nullwire/trace/DefaultExceptionHandler.java58
-rw-r--r--src/com/nullwire/trace/ExceptionClickListener.java49
-rw-r--r--src/com/nullwire/trace/ExceptionHandler.java216
-rw-r--r--src/com/nullwire/trace/G.java14
-rw-r--r--src/com/trilead/ssh2/AuthAgentCallback.java5
-rw-r--r--src/com/trilead/ssh2/ChannelCondition.java122
-rw-r--r--src/com/trilead/ssh2/Connection.java3253
-rw-r--r--src/com/trilead/ssh2/ConnectionInfo.java106
-rw-r--r--src/com/trilead/ssh2/ConnectionMonitor.java66
-rw-r--r--src/com/trilead/ssh2/DHGexParameters.java242
-rw-r--r--src/com/trilead/ssh2/DebugLogger.java46
-rw-r--r--src/com/trilead/ssh2/HTTPProxyData.java166
-rw-r--r--src/com/trilead/ssh2/HTTPProxyException.java58
-rw-r--r--src/com/trilead/ssh2/InteractiveCallback.java110
-rw-r--r--src/com/trilead/ssh2/KnownHosts.java1688
-rw-r--r--src/com/trilead/ssh2/LocalPortForwarder.java126
-rw-r--r--src/com/trilead/ssh2/LocalStreamForwarder.java156
-rw-r--r--src/com/trilead/ssh2/ProxyData.java30
-rw-r--r--src/com/trilead/ssh2/SCPClient.java1458
-rw-r--r--src/com/trilead/ssh2/SFTPException.java182
-rw-r--r--src/com/trilead/ssh2/SFTPv3Client.java2776
-rw-r--r--src/com/trilead/ssh2/SFTPv3DirectoryEntry.java76
-rw-r--r--src/com/trilead/ssh2/SFTPv3FileAttributes.java290
-rw-r--r--src/com/trilead/ssh2/SFTPv3FileHandle.java90
-rw-r--r--src/com/trilead/ssh2/ServerHostKeyVerifier.java62
-rw-r--r--src/com/trilead/ssh2/Session.java1060
-rw-r--r--src/com/trilead/ssh2/StreamGobbler.java458
-rw-r--r--src/com/trilead/ssh2/auth/AuthenticationManager.java893
-rw-r--r--src/com/trilead/ssh2/channel/AuthAgentForwardThread.java110
-rw-r--r--src/com/trilead/ssh2/channel/Channel.java414
-rw-r--r--src/com/trilead/ssh2/channel/ChannelInputStream.java172
-rw-r--r--src/com/trilead/ssh2/channel/ChannelManager.java3512
-rw-r--r--src/com/trilead/ssh2/channel/ChannelOutputStream.java142
-rw-r--r--src/com/trilead/ssh2/channel/DynamicAcceptThread.java2
-rw-r--r--src/com/trilead/ssh2/channel/IChannelWorkerThread.java26
-rw-r--r--src/com/trilead/ssh2/channel/LocalAcceptThread.java270
-rw-r--r--src/com/trilead/ssh2/channel/RemoteAcceptThread.java206
-rw-r--r--src/com/trilead/ssh2/channel/RemoteForwardingData.java34
-rw-r--r--src/com/trilead/ssh2/channel/RemoteX11AcceptThread.java480
-rw-r--r--src/com/trilead/ssh2/channel/StreamForwarder.java225
-rw-r--r--src/com/trilead/ssh2/channel/X11ServerData.java32
-rw-r--r--src/com/trilead/ssh2/compression/CompressionFactory.java2
-rw-r--r--src/com/trilead/ssh2/compression/ICompressor.java2
-rw-r--r--src/com/trilead/ssh2/compression/Zlib.java4
-rw-r--r--src/com/trilead/ssh2/compression/ZlibOpenSSH.java35
-rw-r--r--src/com/trilead/ssh2/crypto/Base64.java296
-rw-r--r--src/com/trilead/ssh2/crypto/CryptoWishList.java52
-rw-r--r--src/com/trilead/ssh2/crypto/KeyMaterial.java182
-rw-r--r--src/com/trilead/ssh2/crypto/PEMDecoder.java871
-rw-r--r--src/com/trilead/ssh2/crypto/PEMStructure.java32
-rw-r--r--src/com/trilead/ssh2/crypto/SimpleDERReader.java389
-rw-r--r--src/com/trilead/ssh2/crypto/cipher/AES.java1396
-rw-r--r--src/com/trilead/ssh2/crypto/cipher/BlockCipher.java32
-rw-r--r--src/com/trilead/ssh2/crypto/cipher/BlockCipherFactory.java230
-rw-r--r--src/com/trilead/ssh2/crypto/cipher/BlowFish.java806
-rw-r--r--src/com/trilead/ssh2/crypto/cipher/CBCMode.java156
-rw-r--r--src/com/trilead/ssh2/crypto/cipher/CTRMode.java124
-rw-r--r--src/com/trilead/ssh2/crypto/cipher/CipherInputStream.java288
-rw-r--r--src/com/trilead/ssh2/crypto/cipher/CipherOutputStream.java284
-rw-r--r--src/com/trilead/ssh2/crypto/cipher/DES.java746
-rw-r--r--src/com/trilead/ssh2/crypto/cipher/DESede.java210
-rw-r--r--src/com/trilead/ssh2/crypto/cipher/NullCipher.java70
-rw-r--r--src/com/trilead/ssh2/crypto/dh/DhExchange.java279
-rw-r--r--src/com/trilead/ssh2/crypto/dh/DhGroupExchange.java226
-rw-r--r--src/com/trilead/ssh2/crypto/dh/EcDhExchange.java106
-rw-r--r--src/com/trilead/ssh2/crypto/dh/GenericDhExchange.java93
-rw-r--r--src/com/trilead/ssh2/crypto/digest/Digest.java25
-rw-r--r--src/com/trilead/ssh2/crypto/digest/HMAC.java95
-rw-r--r--src/com/trilead/ssh2/crypto/digest/HashForSSH2Types.java184
-rw-r--r--src/com/trilead/ssh2/crypto/digest/MAC.java245
-rw-r--r--src/com/trilead/ssh2/crypto/digest/MD5.java268
-rw-r--r--src/com/trilead/ssh2/crypto/digest/SHA1.java664
-rw-r--r--src/com/trilead/ssh2/log/Logger.java108
-rw-r--r--src/com/trilead/ssh2/packets/PacketChannelOpenConfirmation.java132
-rw-r--r--src/com/trilead/ssh2/packets/PacketChannelOpenFailure.java132
-rw-r--r--src/com/trilead/ssh2/packets/PacketChannelTrileadPing.java70
-rw-r--r--src/com/trilead/ssh2/packets/PacketChannelWindowAdjust.java114
-rw-r--r--src/com/trilead/ssh2/packets/PacketDisconnect.java114
-rw-r--r--src/com/trilead/ssh2/packets/PacketGlobalCancelForwardRequest.java84
-rw-r--r--src/com/trilead/ssh2/packets/PacketGlobalForwardRequest.java82
-rw-r--r--src/com/trilead/ssh2/packets/PacketGlobalTrileadPing.java64
-rw-r--r--src/com/trilead/ssh2/packets/PacketIgnore.java118
-rw-r--r--src/com/trilead/ssh2/packets/PacketKexDHInit.java64
-rw-r--r--src/com/trilead/ssh2/packets/PacketKexDHReply.java108
-rw-r--r--src/com/trilead/ssh2/packets/PacketKexDhGexGroup.java100
-rw-r--r--src/com/trilead/ssh2/packets/PacketKexDhGexInit.java66
-rw-r--r--src/com/trilead/ssh2/packets/PacketKexDhGexReply.java112
-rw-r--r--src/com/trilead/ssh2/packets/PacketKexDhGexRequest.java78
-rw-r--r--src/com/trilead/ssh2/packets/PacketKexDhGexRequestOld.java68
-rw-r--r--src/com/trilead/ssh2/packets/PacketKexInit.java331
-rw-r--r--src/com/trilead/ssh2/packets/PacketNewKeys.java92
-rw-r--r--src/com/trilead/ssh2/packets/PacketOpenDirectTCPIPChannel.java112
-rw-r--r--src/com/trilead/ssh2/packets/PacketOpenSessionChannel.java124
-rw-r--r--src/com/trilead/ssh2/packets/PacketServiceAccept.java122
-rw-r--r--src/com/trilead/ssh2/packets/PacketServiceRequest.java104
-rw-r--r--src/com/trilead/ssh2/packets/PacketSessionExecCommand.java78
-rw-r--r--src/com/trilead/ssh2/packets/PacketSessionPtyRequest.java114
-rw-r--r--src/com/trilead/ssh2/packets/PacketSessionStartShell.java72
-rw-r--r--src/com/trilead/ssh2/packets/PacketSessionSubsystemRequest.java80
-rw-r--r--src/com/trilead/ssh2/packets/PacketSessionX11Request.java106
-rw-r--r--src/com/trilead/ssh2/packets/PacketUserauthBanner.java120
-rw-r--r--src/com/trilead/ssh2/packets/PacketUserauthFailure.java106
-rw-r--r--src/com/trilead/ssh2/packets/PacketUserauthInfoRequest.java168
-rw-r--r--src/com/trilead/ssh2/packets/PacketUserauthInfoResponse.java70
-rw-r--r--src/com/trilead/ssh2/packets/PacketUserauthRequestInteractive.java84
-rw-r--r--src/com/trilead/ssh2/packets/PacketUserauthRequestNone.java122
-rw-r--r--src/com/trilead/ssh2/packets/PacketUserauthRequestPassword.java134
-rw-r--r--src/com/trilead/ssh2/packets/PacketUserauthRequestPublicKey.java130
-rw-r--r--src/com/trilead/ssh2/packets/Packets.java298
-rw-r--r--src/com/trilead/ssh2/packets/TypesReader.java354
-rw-r--r--src/com/trilead/ssh2/packets/TypesWriter.java338
-rw-r--r--src/com/trilead/ssh2/sftp/AttrTextHints.java76
-rw-r--r--src/com/trilead/ssh2/sftp/AttribBits.java258
-rw-r--r--src/com/trilead/ssh2/sftp/AttribFlags.java224
-rw-r--r--src/com/trilead/ssh2/sftp/AttribPermissions.java64
-rw-r--r--src/com/trilead/ssh2/sftp/AttribTypes.java56
-rw-r--r--src/com/trilead/ssh2/sftp/ErrorCodes.java208
-rw-r--r--src/com/trilead/ssh2/sftp/OpenFlags.java446
-rw-r--r--src/com/trilead/ssh2/sftp/Packet.java86
-rw-r--r--src/com/trilead/ssh2/signature/DSAPrivateKey.java58
-rw-r--r--src/com/trilead/ssh2/signature/DSAPublicKey.java45
-rw-r--r--src/com/trilead/ssh2/signature/DSASHA1Verify.java465
-rw-r--r--src/com/trilead/ssh2/signature/DSASignature.java31
-rw-r--r--src/com/trilead/ssh2/signature/ECDSASHA2Verify.java487
-rw-r--r--src/com/trilead/ssh2/signature/RSAPrivateKey.java43
-rw-r--r--src/com/trilead/ssh2/signature/RSAPublicKey.java31
-rw-r--r--src/com/trilead/ssh2/signature/RSASHA1Verify.java465
-rw-r--r--src/com/trilead/ssh2/signature/RSASignature.java27
-rw-r--r--src/com/trilead/ssh2/transport/ClientServerHello.java250
-rw-r--r--src/com/trilead/ssh2/transport/KexManager.java1328
-rw-r--r--src/com/trilead/ssh2/transport/KexParameters.java48
-rw-r--r--src/com/trilead/ssh2/transport/KexState.java65
-rw-r--r--src/com/trilead/ssh2/transport/MessageHandler.java28
-rw-r--r--src/com/trilead/ssh2/transport/NegotiateException.java24
-rw-r--r--src/com/trilead/ssh2/transport/NegotiatedParameters.java44
-rw-r--r--src/com/trilead/ssh2/transport/TransportConnection.java679
-rw-r--r--src/com/trilead/ssh2/transport/TransportManager.java1552
-rw-r--r--src/com/trilead/ssh2/util/TimeoutService.java298
-rw-r--r--src/com/trilead/ssh2/util/Tokenizer.java102
-rw-r--r--src/de/mud/terminal/vt320.java11
-rw-r--r--src/net/sourceforge/jsocks/Authentication.java68
-rw-r--r--src/net/sourceforge/jsocks/AuthenticationNone.java34
-rw-r--r--src/net/sourceforge/jsocks/Proxy.java808
-rw-r--r--src/net/sourceforge/jsocks/ProxyMessage.java218
-rw-r--r--src/net/sourceforge/jsocks/ProxyServer.java1182
-rw-r--r--src/net/sourceforge/jsocks/Socks4Message.java342
-rw-r--r--src/net/sourceforge/jsocks/Socks4Proxy.java214
-rw-r--r--src/net/sourceforge/jsocks/Socks5DatagramSocket.java920
-rw-r--r--src/net/sourceforge/jsocks/Socks5Message.java584
-rw-r--r--src/net/sourceforge/jsocks/Socks5Proxy.java462
-rw-r--r--src/net/sourceforge/jsocks/SocksException.java160
-rw-r--r--src/net/sourceforge/jsocks/SocksServerSocket.java328
-rw-r--r--src/net/sourceforge/jsocks/SocksSocket.java582
-rw-r--r--src/net/sourceforge/jsocks/UDPEncapsulation.java58
-rw-r--r--src/net/sourceforge/jsocks/UDPRelayServer.java424
-rw-r--r--src/net/sourceforge/jsocks/server/ServerAuthenticator.java240
-rw-r--r--src/net/sourceforge/jsocks/server/ServerAuthenticatorNone.java338
-rw-r--r--src/org/connectbot/ActionBarWrapper.java83
-rw-r--r--src/org/connectbot/ColorsActivity.java87
-rw-r--r--src/org/connectbot/ConsoleActivity.java138
-rw-r--r--src/org/connectbot/GeneratePubkeyActivity.java96
-rw-r--r--src/org/connectbot/HostEditorActivity.java13
-rw-r--r--src/org/connectbot/HostListActivity.java58
-rw-r--r--src/org/connectbot/PubkeyListActivity.java77
-rw-r--r--src/org/connectbot/StrictModeSetup.java23
-rw-r--r--src/org/connectbot/TerminalView.java180
-rw-r--r--src/org/connectbot/bean/HostBean.java10
-rw-r--r--src/org/connectbot/bean/PubkeyBean.java59
-rw-r--r--src/org/connectbot/service/ConnectionNotifier.java37
-rw-r--r--src/org/connectbot/service/ConnectivityReceiver.java4
-rw-r--r--src/org/connectbot/service/FontSizeChangedListener.java6
-rw-r--r--src/org/connectbot/service/KeyEventUtil.java149
-rw-r--r--src/org/connectbot/service/Relay.java5
-rw-r--r--src/org/connectbot/service/TerminalBridge.java107
-rw-r--r--src/org/connectbot/service/TerminalKeyListener.java461
-rw-r--r--src/org/connectbot/service/TerminalManager.java69
-rw-r--r--src/org/connectbot/transport/Local.java5
-rw-r--r--src/org/connectbot/transport/SSH.java72
-rw-r--r--src/org/connectbot/transport/Telnet.java4
-rw-r--r--src/org/connectbot/util/HostDatabase.java87
-rw-r--r--src/org/connectbot/util/PreferenceConstants.java22
-rw-r--r--src/org/connectbot/util/PubkeyDatabase.java3
-rw-r--r--src/org/connectbot/util/PubkeyUtils.java216
-rw-r--r--src/org/connectbot/util/UberColorPickerDialog.java235
-rw-r--r--src/org/connectbot/util/UpdateHelper.java225
-rw-r--r--src/org/keyczar/jce/EcCore.java679
-rw-r--r--tests/.classpath3
-rw-r--r--tests/.gitignore1
-rw-r--r--tests/.settings/org.eclipse.jdt.core.prefs294
-rw-r--r--tests/.settings/org.eclipse.jdt.ui.prefs3
-rw-r--r--tests/AndroidManifest.xml4
-rw-r--r--tests/ant.properties21
-rw-r--r--tests/build.properties15
-rw-r--r--tests/build.xml171
-rw-r--r--tests/proguard.cfg40
-rw-r--r--tests/project.properties (renamed from tests/default.properties)4
-rw-r--r--tests/src/org/connectbot/HostListActivityTest.java20
-rw-r--r--tests/src/org/connectbot/TerminalBridgeTest.java158
-rw-r--r--tests/src/org/connectbot/util/PubkeyUtilsTest.java345
285 files changed, 29738 insertions, 26110 deletions
diff --git a/.classpath b/.classpath
index 79a8481..e69f104 100644
--- a/.classpath
+++ b/.classpath
@@ -3,5 +3,6 @@
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry kind="src" path="gen"/>
- <classpathentry kind="output" path="bin"/>
+ <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
+ <classpathentry kind="output" path="bin/classes"/>
</classpath>
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..0e2132e
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,10 @@
+# Set default behaviour, in case users don't have core.autocrlf set.
+* text=auto
+
+# Explicitly declare text files we want to always be normalized and converted
+# to native line endings on checkout.
+*.java text
+
+# Denote all files that are truly binary and should not be modified.
+*.png binary
+*.jpg binary
diff --git a/.gitignore b/.gitignore
index c1f0eb8..485e248 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,6 @@
bin
gen
launchpad-*.tar.gz
+local.properties
+*~
+*.sublime-workspace
diff --git a/.project b/.project
index 47534a4..2a174d0 100644
--- a/.project
+++ b/.project
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
- <name>ConnectBot</name>
+ <name>connectbot</name>
<comment></comment>
<projects>
</projects>
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
index b8fb425..5e57e8a 100644
--- a/.settings/org.eclipse.jdt.core.prefs
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -1,5 +1,284 @@
-#Wed Nov 14 13:33:03 CST 2007
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
org.eclipse.jdt.core.compiler.compliance=1.5
org.eclipse.jdt.core.compiler.source=1.5
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
+org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
+org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
+org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=true
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=100
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=tab
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_on_off_tags=false
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
+org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
diff --git a/.settings/org.eclipse.jdt.ui.prefs b/.settings/org.eclipse.jdt.ui.prefs
index 16019b1..e777683 100644
--- a/.settings/org.eclipse.jdt.ui.prefs
+++ b/.settings/org.eclipse.jdt.ui.prefs
@@ -1,4 +1,3 @@
-#Sat Jan 24 08:09:15 CST 2009
cleanup.add_default_serial_version_id=true
cleanup.add_generated_serial_version_id=false
cleanup.add_missing_annotations=true
@@ -54,7 +53,7 @@ cleanup_settings_version=2
eclipse.preferences.version=1
editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
formatter_profile=_ConnectBot
-formatter_settings_version=11
+formatter_settings_version=12
org.eclipse.jdt.ui.ignorelowercasenames=true
org.eclipse.jdt.ui.importorder=java;javax;org;com;
org.eclipse.jdt.ui.javadoc=true
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index f9684d2..2c9c325 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1,10 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ConnectBot: simple, powerful, open-source SSH client for Android
+ Copyright 2007 Kenny Root, Jeffrey Sharkey
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.connectbot"
- android:versionName="1.7.1"
- android:versionCode="334"
+ android:versionName="1.8.3"
+ android:versionCode="373"
android:installLocation="auto">
+ <uses-sdk android:targetSdkVersion="15" android:minSdkVersion="4" />
+
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.VIBRATE" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
+
+ <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
+
+ <supports-screens />
+
<application
android:icon="@drawable/icon"
android:label="@string/app_name"
@@ -47,7 +75,7 @@
<activity android:name=".ConsoleActivity" android:configChanges="keyboardHidden|orientation"
android:theme="@style/NoTitle" android:windowSoftInputMode="stateAlwaysVisible|adjustResize"
- android:launchMode="singleTop">
+ android:launchMode="singleTop" android:hardwareAccelerated="false">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
@@ -61,14 +89,8 @@
</intent-filter>
</activity>
- </application>
-
- <uses-sdk android:targetSdkVersion="8" android:minSdkVersion="3" />
-
- <uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.VIBRATE" />
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <meta-data android:name="com.google.android.backup.api_key"
+ android:value="AEdPqrEAAAAIDlFz9nSUr2g0gSytW0t2cNnYAGHDkptlVohsBA" />
- <supports-screens />
+ </application>
</manifest>
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..06e089d
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,31 @@
+# Change Log
+All notable changes to this project will be documented in this file.
+This project adheres to [Semantic Versioning](http://semver.org/).
+
+## [Unreleased][unreleased]
+### Fixed
+- Key exchange and host key algorithm preference order was not being
+ respected.
+- ECDH would sometimes fail because the shared secret would be encoded
+ as a negative integer.
+- DSA host key support was broken from the beginning of the v1.8 series.
+- Connections would sometimes close when leaving ConnectBot.
+- Telnet port range too high will no longer cause crashes.
+
+### Added
+- More context is given for failures to connect via SSH which should
+ reveal why a host might be incompatible with ConnectBot.
+- SSH key exchange algorithm will now be printed upon connection.
+- SSH: all addresses for a particular host will be tried when
+ connecting. This includes IPv6.
+
+## [1.8.3][1.8.3] - 2014-04-02
+### Fixed
+- Only enable EC support when the device supports it.
+- Default font size scales with the device display density.
+- Color picker scales correctly depending on device density.
+- Color picker color numbers are now localized
+
+
+[unreleased]: https://github.com/connectbot/connectbot/compare/v1.8.3...HEAD
+[1.8.3]: https://github.com/connectbot/connectbot/compare/v1.8.2...v1.8.3
diff --git a/build.properties b/ant.properties
index fe0ba02..95af6f4 100644
--- a/build.properties
+++ b/ant.properties
@@ -5,11 +5,9 @@
# This file is only used by the Ant script.
-# The name of the source folder.
-source.dir=src
-
-# The name of the output folder.
-out.dir=bin
+# You can use this to override default values such as
+# 'source.dir' for the location of your java source folder and
+# 'out.dir' for the location of your output folder.
# You can also use it define how the release builds are signed by declaring
# the following properties:
@@ -17,3 +15,5 @@ out.dir=bin
# 'key.alias' for the name of the key to use.
# The password will be asked during the build when you use the 'release' target.
+source.dir=src
+out.dir=bin
diff --git a/build.xml b/build.xml
index 9de0ea8..aebe4f9 100644
--- a/build.xml
+++ b/build.xml
@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
+<!-- vim: set ts=4 sw=4 et: -->
<project name="ConnectBot" default="help">
-<!-- The local.properties file is created and updated by the 'android'
- tool.
- It contains the path to the SDK. It should *NOT* be checked into
- Version Control Systems. -->
- <property file="local.properties" />
+ <!-- The local.properties file is created and updated by the 'android' tool.
+ It contains the path to the SDK. It should *NOT* be checked into
+ Version Control Systems. -->
+ <loadproperties srcFile="local.properties" />
- <!-- The build.properties file can be created by you and is never touched
- by the 'android' tool. This is the place to change some of the
- default property values used by the Ant rules.
+ <!-- The ant.properties file can be created by you. It is only edited by the
+ 'android' tool to add properties to it.
+ This is the place to change some Ant specific build properties.
Here are some properties you may want to change/update:
source.dir
@@ -17,6 +17,9 @@
out.dir
The name of the output directory. Default is 'bin'.
+ For other overridable properties, look at the beginning of the rules
+ files in the SDK, at tools/ant/build.xml
+
Properties related to the SDK location or the project target should
be updated using the 'android' tool with the 'update' action.
@@ -24,80 +27,70 @@
application and should be checked into Version Control Systems.
-->
- <property file="build.properties" />
+ <property file="ant.properties" />
- <!-- The default.properties file is created and updated by the 'android'
+ <!-- The project.properties file is created and updated by the 'android'
tool, as well as ADT.
This file is an integral part of the build system for your
application and should be checked into Version Control Systems. -->
- <property file="default.properties" />
+ <loadproperties srcFile="project.properties" />
- <!-- Custom Android task to deal with the project target, and import the
- proper rules.
- This requires ant 1.6.0 or above. -->
- <path id="android.antlibs">
- <pathelement path="${sdk.dir}/tools/lib/anttasks.jar" />
- <pathelement path="${sdk.dir}/tools/lib/sdklib.jar" />
- <pathelement path="${sdk.dir}/tools/lib/androidprefs.jar" />
- </path>
+ <!-- quick check on sdk.dir -->
+ <fail
+ message="sdk.dir is missing. Make sure to generate local.properties using 'android update project'"
+ unless="sdk.dir"
+ />
-<!-- Begin custom ConnectBot stuff -->
- <property name="proguard.out.dir" value="${out.dir}/proguard.out"/>
- <property name="out.dex.input.absolute.dir" value="${proguard.out.dir}"/>
+<!-- extension targets. Uncomment the ones where you want to do custom work
+ in between standard targets -->
+<!--
+ <target name="-pre-build">
+ </target>
+ <target name="-pre-compile">
+ </target>
+
+ /* This is typically used for code obfuscation.
+ Compiled code location: ${out.classes.absolute.dir}
+ If this is not done in place, override ${out.dex.input.absolute.dir} */
+ <target name="-post-compile">
+ </target>
+-->
+
+ <!-- Import the actual build file.
+
+ To customize existing targets, there are two options:
+ - Customize only one target:
+ - copy/paste the target into this file, *before* the
+ <import> task.
+ - customize it to your needs.
+ - Customize the whole content of build.xml
+ - copy/paste the content of the rules files (minus the top node)
+ into this file, replacing the <import> task.
+ - customize to your needs.
+
+ ***********************
+ ****** IMPORTANT ******
+ ***********************
+ In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
+ in order to avoid having your file be overridden by tools such as "android update project"
+ -->
+ <!-- version-tag: custom -->
+ <import file="${sdk.dir}/tools/ant/build.xml" />
+
+<!-- Begin custom ConnectBot stuff -->
<!-- Output directory for .po files. -->
<property name="locale.dir" value="locale" />
<!-- Default args to pass to a2po for .po generation. -->
- <property name="a2po.args" value="--groups strings --template fortune/fortune.pot --layout 'po/fortune/fortune-%(locale)s.po'"/>
+ <property name="a2po.args" value="--groups strings --template fortune/fortune.pot --layout 'fortune/fortune-%(locale)s.po'"/>
<!-- File names for launchpad translations. -->
<property name="launchpad.export.file" value="launchpad-export.tar.gz"/>
<property name="launchpad.import.file" value="launchpad-import.tar.gz"/>
- <target name="check-proguard">
- <available file="tools/proguard.jar" property="have.proguard"/>
- </target>
-
- <target name="proguard" depends="check-proguard">
- <fail unless="have.proguard">You requested ProGuard, but you don't have the JAR available! See README</fail>
- </target>
-
- <target name="-pre-build" depends="create-out-dir, link-out-dir, update-version"/>
-
- <target name="-post-compile" depends="proguard-execute"/>
-
- <target name="remove-out-symlink">
- <symlink action="delete" link="${proguard.out.dir}" failonerror="false"/>
- </target>
-
- <target name="check-out-exists" depends="remove-out-symlink">
- <available file="${proguard.out.dir}" property="proguard.out.is.dir"/>
- </target>
-
- <target name="delete-out-dir" depends="check-out-exists" if="proguard.out.is.dir">
- <delete dir="${proguard.out.dir}"/>
- </target>
-
- <target name="create-out-dir" depends="delete-out-dir" if="have.proguard">
- <mkdir dir="${proguard.out.dir}"/>
- </target>
-
- <target name="link-out-dir" depends="delete-out-dir" unless="have.proguard">
- <symlink link="${proguard.out.dir}" resource="${out.classes.dir}"/>
- </target>
-
- <target name="proguard-execute" if="have.proguard">
- <mkdir dir="${out.dex.input.absolute.dir}"/>
- <taskdef resource="proguard/ant/task.properties"
- classpath="tools/proguard.jar" />
- <proguard configuration="proguard.flags" printusage="${out.dir}/proguard.usage">
- <injar path="${out.classes.dir}"/>
- <outjar path="${proguard.out.dir}"/>
- <libraryjar path="${android.jar}"/>
- </proguard>
- </target>
+ <target name="-pre-build" depends="update-version"/>
<target name="help">
<!-- displays starts at col 13
@@ -115,9 +108,11 @@
<echo> signatures must match.</echo>
<echo> uninstall: Uninstalls the application from a running emulator or</echo>
<echo> device.</echo>
- <echo> proguard: use before build statements like "debug" and "release"</echo>
- <echo> to enable proguard dead code removal. NOTE: You must</echo>
- <echo> have tools/proguard.jar available. See the README.</echo>
+ <echo> translations-import: Import translations from a Launchpad-style</echo>
+ <echo> Rosetta translation.</echo>
+ <echo> translations-export: Export translations to a Launchpad-style</echo>
+ <echo> Rosetta translation.</echo>
+
</target>
<target name="update-version" description="Updates the Version.java file with current Git revision">
@@ -131,9 +126,9 @@
<xpath input="AndroidManifest.xml" expression="/manifest/@android:versionName"
output="manifest.version.name" />
- <!-- checkout notrans.xml so we can ignore it in our index -->
+ <!-- tell git to ignore changes to the version xml -->
<exec executable="git">
- <arg line="checkout res/values/notrans.xml"/>
+ <arg line="update-index --assume-unchanged res/values/version.xml"/>
</exec>
<!-- find out the description of the current Git commit -->
@@ -141,46 +136,16 @@
<arg line="describe --match 'v[0-9]*' --dirty"/>
</exec>
- <replaceregexp file="${resource.absolute.dir}/values/notrans.xml" encoding="utf8" match='(\x3Cstring name="msg_version">)[^\x3C]*(\x3C/string>)'
- replace='\1${ant.project.name} ${manifest.version.name} (${git.revision} ${build.date})\2' />
+ <!-- write out to res/values/version.xml -->
+ <echo file="${resource.absolute.dir}/values/version.xml" encoding="utf8"><![CDATA[<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="msg_version" translatable="false">${ant.project.name} ${manifest.version.name} (${git.revision} ${build.date})</string>
+</resources>
+]]></echo>
<echo>Updated "msg_version" to: ${ant.project.name} ${manifest.version.name} (${git.revision} ${build.date})</echo>
</target>
- <target name="clean"
- description="Clean up the result of the build process">
- <delete dir="${out.absolute.dir}"/>
- <delete dir="${gen.absolute.dir}"/>
- <exec executable="ant" failonerror="true">
- <arg value="-f" />
- <arg value="tests/build.xml" />
- <arg value="clean"/>
- </exec>
- </target>
-
- <target name="tests" depends="install">
- <echo>Building and installing tests...</echo>
- <exec executable="ant" failonerror="true">
- <arg value="-f" />
- <arg value="tests/build.xml" />
- <arg value="install"/>
- </exec>
- <echo>Running test cases...</echo>
- <exec executable="${adb}">
- <arg value="shell" />
- <arg value="am" />
- <arg value="instrument" />
- <arg value="-w" />
- <arg value="${application-package}.tests/android.test.InstrumentationTestRunner" />
- <redirector outputproperty="test.results"/>
- </exec>
- <fail message="Some unit tests failed:${line.separator}${test.results}">
- <condition>
- <contains string="${test.results}" substring="FAILURES"/>
- </condition>
- </fail>
- </target>
-
<!-- Translations come from launchpad.net and are placed in the
locale/ subdirectory. -->
<target name="translations-import">
@@ -204,11 +169,41 @@
includes="**/*.pot **/*.po" />
</target>
-<!-- End custom ConnectBot stuff -->
+ <target name="-check-ndk">
+ <fail
+ message="ndk.dir is missing. Make sure to put it in local.properties"
+ unless="ndk.dir"
+ />
+ </target>
+
+ <target name="native-build" depends="-check-ndk">
+ <exec executable="${ndk.dir}/ndk-build" failonerror="true" />
+ </target>
+
+ <target name="native-clean" depends="-check-ndk">
+ <exec executable="${ndk.dir}/ndk-build" failonerror="true">
+ <arg value="clean" />
+ </exec>
+ </target>
+
+ <property name="lint" location="${android.tools.dir}/lint${bat}" />
+
+ <target name="lint-xml">
+ <exec executable="${lint}">
+ <arg value="--xml" />
+ <arg value="lint-results.xml" />
+ <arg path="${basedir}" />
+ </exec>
+ </target>
+
+ <target name="lint-html">
+ <exec executable="${lint}">
+ <arg value="--html" />
+ <arg value="lint-results.html" />
+ <arg path="${basedir}" />
+ </exec>
+ </target>
- <taskdef name="setup"
- classname="com.android.ant.SetupTask"
- classpathref="android.antlibs" />
+<!-- End custom ConnectBot stuff -->
- <setup />
</project>
diff --git a/connectbot.sublime-project b/connectbot.sublime-project
new file mode 100644
index 0000000..e35ca4e
--- /dev/null
+++ b/connectbot.sublime-project
@@ -0,0 +1,36 @@
+{
+ "folders":
+ [
+ {
+ "path": ".",
+ "folder_exclude_patterns": ["bin", "gen"]
+ }
+ ],
+ "build_systems":
+ [
+ {
+ "name": "Android Debug",
+ "cmd": ["ant", "debug"],
+ "file_regex": "^ *\\[javac\\] (.+):([0-9]+):() (.*)$",
+ "working_dir": "${project_path:${folder}}",
+ "selector": "source.java",
+
+ "windows":
+ {
+ "cmd": ["ant.bat", "debug"]
+ },
+
+ "variants":
+ [
+ {
+ "name": "Run",
+ "cmd": ["ant", "debug", "install"],
+ "windows":
+ {
+ "cmd": ["ant.bat", "debug", "install"]
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/icons/icon14.png b/icons/icon14.png
new file mode 100644
index 0000000..8bfdcba
--- /dev/null
+++ b/icons/icon14.png
Binary files differ
diff --git a/icons/icon64.png b/icons/icon64.png
new file mode 100644
index 0000000..d35240c
--- /dev/null
+++ b/icons/icon64.png
Binary files differ
diff --git a/jni/Application.mk b/jni/Application.mk
new file mode 100644
index 0000000..0a352f9
--- /dev/null
+++ b/jni/Application.mk
@@ -0,0 +1,2 @@
+# Build both ARMv5TE and x86-32 machine code.
+APP_ABI := armeabi x86
diff --git a/jni/Exec/Android.mk b/jni/Exec/Android.mk
index 7abfdf5..505b7cb 100644
--- a/jni/Exec/Android.mk
+++ b/jni/Exec/Android.mk
@@ -3,6 +3,7 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := com_google_ase_Exec
+LOCAL_CFLAGS := -Werror
LOCAL_SRC_FILES := com_google_ase_Exec.cpp
LOCAL_LDLIBS := -llog
diff --git a/libs/armeabi/libcom_google_ase_Exec.so b/libs/armeabi/libcom_google_ase_Exec.so
index 20fd499..eca95a6 100755
--- a/libs/armeabi/libcom_google_ase_Exec.so
+++ b/libs/armeabi/libcom_google_ase_Exec.so
Binary files differ
diff --git a/libs/x86/libcom_google_ase_Exec.so b/libs/x86/libcom_google_ase_Exec.so
new file mode 100755
index 0000000..55f602d
--- /dev/null
+++ b/libs/x86/libcom_google_ase_Exec.so
Binary files differ
diff --git a/proguard.cfg b/proguard.cfg
new file mode 100644
index 0000000..28ff286
--- /dev/null
+++ b/proguard.cfg
@@ -0,0 +1,46 @@
+#-keepattributes SourceFile,LineNumberTable
+#-optimizationpasses 5
+-dontobfuscate
+-dontoptimize
+-dontusemixedcaseclassnames
+-dontskipnonpubliclibraryclasses
+-dontpreverify
+-verbose
+-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
+
+-keep public class * extends android.app.Activity
+-keep public class * extends android.app.Application
+-keep public class * extends android.app.Service
+-keep public class * extends android.content.BroadcastReceiver
+-keep public class * extends android.content.ContentProvider
+-keep public class * extends android.app.backup.BackupAgentHelper
+-keep public class * extends android.preference.Preference
+
+-keepclasseswithmembernames class * {
+ native <methods>;
+}
+
+-keepclasseswithmembers class * {
+ public <init>(android.content.Context, android.util.AttributeSet);
+}
+
+-keepclasseswithmembers class * {
+ public <init>(android.content.Context, android.util.AttributeSet, int);
+}
+
+-keepclassmembers class * extends android.app.Activity {
+ public void *(android.view.View);
+}
+
+-keepclassmembers enum * {
+ public static **[] values();
+ public static ** valueOf(java.lang.String);
+}
+
+-keep class * implements android.os.Parcelable {
+ public static final android.os.Parcelable$Creator *;
+}
+
+-keep class org.connectbot.**
+-keep public class com.trilead.ssh2.compression.*
+-keep public class com.trilead.ssh2.crypto.*
diff --git a/proguard.flags b/proguard.flags
deleted file mode 100644
index 7b840ae..0000000
--- a/proguard.flags
+++ /dev/null
@@ -1,9 +0,0 @@
--dontskipnonpubliclibraryclasses
--dontobfuscate
--dontoptimize
-
--keep public class * extends android.app.Activity
--keep public class * extends android.app.Service
--keep class org.connectbot.**
--keep public class com.trilead.ssh2.compression.*
--keep public class com.trilead.ssh2.crypto.*
diff --git a/default.properties b/project.properties
index 8cebe7d..7892870 100644
--- a/default.properties
+++ b/project.properties
@@ -4,11 +4,11 @@
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system use,
-# "build.properties", and override values to adapt the script to your
+# "ant.properties", and override values to adapt the script to your
# project structure.
# Indicates whether an apk should be generated for each density.
split.density=false
-
+proguard.config=proguard.cfg
# Project target.
-target=android-8
+target=android-15
diff --git a/res/drawable-hdpi/icon.png b/res/drawable-hdpi/icon.png
index f5154b3..a0e8560 100644
--- a/res/drawable-hdpi/icon.png
+++ b/res/drawable-hdpi/icon.png
Binary files differ
diff --git a/res/drawable-hdpi/notification_icon.png b/res/drawable-hdpi/notification_icon.png
index 518f990..ecc44a3 100644
--- a/res/drawable-hdpi/notification_icon.png
+++ b/res/drawable-hdpi/notification_icon.png
Binary files differ
diff --git a/res/drawable-mdpi-v6/icon.png b/res/drawable-mdpi-v6/icon.png
deleted file mode 100644
index e701f14..0000000
--- a/res/drawable-mdpi-v6/icon.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/icon.png b/res/drawable-mdpi/icon.png
index 8a7202d..46d97d3 100644
--- a/res/drawable-mdpi/icon.png
+++ b/res/drawable-mdpi/icon.png
Binary files differ
diff --git a/res/drawable-mdpi/notification_icon.png b/res/drawable-mdpi/notification_icon.png
index 01f6dca..118d838 100644
--- a/res/drawable-mdpi/notification_icon.png
+++ b/res/drawable-mdpi/notification_icon.png
Binary files differ
diff --git a/res/drawable-xhdpi/icon.png b/res/drawable-xhdpi/icon.png
new file mode 100644
index 0000000..891f59e
--- /dev/null
+++ b/res/drawable-xhdpi/icon.png
Binary files differ
diff --git a/res/drawable-xhdpi/notification_icon.png b/res/drawable-xhdpi/notification_icon.png
new file mode 100644
index 0000000..3b79360
--- /dev/null
+++ b/res/drawable-xhdpi/notification_icon.png
Binary files differ
diff --git a/res/drawable-xxhdpi/icon.png b/res/drawable-xxhdpi/icon.png
new file mode 100644
index 0000000..9bc6c3e
--- /dev/null
+++ b/res/drawable-xxhdpi/icon.png
Binary files differ
diff --git a/res/drawable-xxhdpi/notification_icon.png b/res/drawable-xxhdpi/notification_icon.png
new file mode 100644
index 0000000..0d8b9e1
--- /dev/null
+++ b/res/drawable-xxhdpi/notification_icon.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/icon.png b/res/drawable-xxxhdpi/icon.png
new file mode 100644
index 0000000..226eee9
--- /dev/null
+++ b/res/drawable-xxxhdpi/icon.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/notification_icon.png b/res/drawable-xxxhdpi/notification_icon.png
new file mode 100644
index 0000000..2bafbb9
--- /dev/null
+++ b/res/drawable-xxxhdpi/notification_icon.png
Binary files differ
diff --git a/res/drawable/icon_older.png b/res/drawable/icon_older.png
deleted file mode 100644
index 323b99f..0000000
--- a/res/drawable/icon_older.png
+++ /dev/null
Binary files differ
diff --git a/res/layout-land/item_host.xml b/res/layout-land/item_host.xml
index 4dd23a3..9cde624 100644
--- a/res/layout-land/item_host.xml
+++ b/res/layout-land/item_host.xml
@@ -28,12 +28,13 @@
<ImageView
android:id="@android:id/icon"
android:src="@drawable/connected"
+ android:contentDescription="@string/image_description_connected"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:paddingTop="5dip"
/>
-
+
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
diff --git a/res/layout-port/item_host.xml b/res/layout-port/item_host.xml
index f8b9e85..24d34ba 100644
--- a/res/layout-port/item_host.xml
+++ b/res/layout-port/item_host.xml
@@ -32,6 +32,7 @@
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:paddingTop="5dip"
+ android:contentDescription="@string/image_description_connected"
/>
<TextView
diff --git a/res/layout/act_colors.xml b/res/layout/act_colors.xml
index a703a8d..3bda649 100644
--- a/res/layout/act_colors.xml
+++ b/res/layout/act_colors.xml
@@ -23,36 +23,34 @@
android:layout_width="fill_parent"
android:layout_height="fill_parent">
- <TextView
- android:text="FG:"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:id="@+id/fg_label"
- android:paddingTop="12dp"
- />
-
- <Spinner
- android:id="@+id/fg"
- android:layout_width="wrap_content"
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:layout_toRightOf="@+id/fg_label"
- />
+ android:id="@+id/spinners">
- <Spinner
- android:id="@+id/bg"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignTop="@+id/fg"
- android:layout_alignParentRight="true"
- />
+ <Spinner
+ android:id="@+id/fg"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:minHeight="60dp"
+ android:layout_weight="1"
+ android:gravity="center"
+ />
- <TextView
- android:text="BG:"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:layout_toLeftOf="@+id/bg"
- android:paddingTop="12dp"
- />
+ <Spinner
+ android:id="@+id/bg"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@id/fg"
+ android:layout_alignParentRight="true"
+ android:layout_toRightOf="@id/fg"
+ android:minHeight="60dp"
+ android:layout_weight="1"
+ android:gravity="center"
+ />
+ </LinearLayout>
<GridView
android:id="@+id/color_grid"
@@ -65,7 +63,8 @@
android:columnWidth="60dp"
android:stretchMode="columnWidth"
android:gravity="center"
- android:layout_below="@+id/fg"
- android:stackFromBottom="true" android:minHeight="60dp"/>
+ android:minHeight="60dp"
+ android:layout_below="@+id/spinners"
+ />
</RelativeLayout>
diff --git a/res/layout/act_console.xml b/res/layout/act_console.xml
index 7d0cfee..f7db787 100644
--- a/res/layout/act_console.xml
+++ b/res/layout/act_console.xml
@@ -134,6 +134,7 @@
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:src="@+drawable/button_keyboard"
+ android:contentDescription="@string/image_description_show_keyboard"
/>
<ImageView
@@ -144,6 +145,7 @@
android:layout_alignParentLeft="true"
android:layout_alignParentBottom="true"
android:src="@+drawable/button_ctrl"
+ android:contentDescription="@string/image_description_toggle_control_character"
/>
<ImageView
@@ -154,6 +156,7 @@
android:layout_toRightOf="@+id/button_ctrl"
android:layout_alignParentBottom="true"
android:src="@+drawable/button_esc"
+ android:contentDescription="@string/image_description_send_escape_character"
/>
</RelativeLayout>
diff --git a/res/layout/act_generatepubkey.xml b/res/layout/act_generatepubkey.xml
index f11fa05..171ffd8 100644
--- a/res/layout/act_generatepubkey.xml
+++ b/res/layout/act_generatepubkey.xml
@@ -67,13 +67,20 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="RSA"
- android:paddingRight="30dip" />
+ android:paddingRight="3dip"/>
<RadioButton
android:id="@+id/dsa"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="DSA" />
+ android:text="DSA"
+ android:paddingRight="3dip"/>
+
+ <RadioButton
+ android:id="@+id/ec"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="EC" />
</RadioGroup>
</TableRow>
@@ -86,9 +93,9 @@
<EditText
android:id="@+id/bits"
+ android:inputType="number"
android:layout_height="wrap_content"
android:text="1024"
- android:numeric="integer"
android:singleLine="true"
android:layout_weight="1" />
</TableRow>
@@ -125,7 +132,7 @@
android:layout_weight="1" />
</TableRow>
- <TableRow>
+ <TableRow android:paddingBottom="6dip">
<LinearLayout
android:paddingRight="10dip"
android:orientation="vertical"
@@ -172,6 +179,7 @@
android:layout_height="wrap_content"
android:id="@+id/save"
android:text="@string/pubkey_generate"
- android:enabled="false" />
+ android:enabled="false"
+ android:layout_marginTop="6dip"/>
</TableLayout>
</ScrollView>
diff --git a/res/layout/act_hostlist.xml b/res/layout/act_hostlist.xml
index 80d1583..3c6f4b1 100644
--- a/res/layout/act_hostlist.xml
+++ b/res/layout/act_hostlist.xml
@@ -25,30 +25,41 @@
android:layout_height="fill_parent"
>
- <Spinner
- android:id="@+id/transport_selection"
+ <RelativeLayout
+ android:id="@+id/quickconnect"
+ android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:layout_width="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
- />
+ >
+
+ <Spinner
+ android:id="@+id/transport_selection"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_alignParentLeft="true"
+ />
+
+ <EditText
+ android:id="@+id/front_quickconnect"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:hint="username@hostname:port"
+ android:layout_toRightOf="@+id/transport_selection"
+ android:layout_alignBaseline="@+id/transport_selection"
+ android:inputType="textEmailAddress"
+ android:maxLines="1"
+ android:ellipsize="end"
+ android:focusableInTouchMode="true"
+ android:singleLine="true"/>
+ </RelativeLayout>
- <EditText
- android:id="@+id/front_quickconnect"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:hint="username@hostname:port"
- android:layout_toRightOf="@+id/transport_selection"
- android:layout_alignTop="@+id/transport_selection"
- android:inputType="textEmailAddress"
- android:layout_alignBottom="@+id/transport_selection"
- />
<ListView
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
- android:layout_above="@+id/transport_selection"
+ android:layout_above="@+id/quickconnect"
/>
<TextView
@@ -58,7 +69,7 @@
android:text="@string/list_host_empty"
android:textAppearance="?android:attr/textAppearanceMedium"
android:gravity="center"
- android:layout_above="@+id/transport_selection"
+ android:layout_above="@+id/quickconnect"
/>
</RelativeLayout>
diff --git a/res/layout/item_pubkey.xml b/res/layout/item_pubkey.xml
index a0d49ba..7f4fa5f 100644
--- a/res/layout/item_pubkey.xml
+++ b/res/layout/item_pubkey.xml
@@ -29,8 +29,10 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/pubkey"
+ android:contentDescription="@string/image_description_key_is_locked"
android:layout_alignParentRight="true"
- android:layout_alignParentTop="true" android:layout_marginLeft="10dip"/>
+ android:layout_alignParentTop="true"
+ android:layout_marginLeft="10dip"/>
<TextView
android:id="@android:id/text1"
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 8605093..5fb33d6 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -39,4 +39,7 @@
<string name="color_green">أخضر</string>
<string name="color_blue">أزرق</string>
<string name="color_gray">رمادي</string>
+ <string name="image_description_connected">متصل</string>
+ <string name="image_description_key_is_locked">المفتاح مقفل</string>
+ <string name="image_description_show_keyboard">عرض لوحة المفاتيح</string>
</resources>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 32028fa..f87787b 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -48,9 +48,6 @@
<string name="alert_disconnect_msg">Връзката изгубена</string>
<string name="msg_copyright">Запазени права © 2007-2008 Kenny Root http://the-b.org/, Jeffrey Sharkey http://jsharkey.org/</string>
<string name="list_keymode_none">Забраняване</string>
- <string name="list_update_daily">Ежедневно</string>
- <string name="list_update_weekly">Ежеседмично</string>
- <string name="list_update_never">Никога</string>
<string name="console_copy_done">Копирани %1$d байта в клипборда</string>
<string name="console_menu_close">Затваряне</string>
<string name="console_menu_copy">Копиране</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 7c7c81d..7b3baa4 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -8,6 +8,7 @@
<string name="title_host_editor">Edició del Servidor</string>
<string name="title_help">Ajuda</string>
<string name="title_colors">Colors</string>
+ <string name="title_color_picker">Trieu un color</string>
<string name="resolve_connect">Connecta</string>
<string name="resolve_entropy">Recull Entropia</string>
<string name="menu_insert">Afegeix servidor</string>
@@ -77,10 +78,9 @@
<string name="pref_rotation_summary">Com canvia la orientació quan el teclat s\'obre o es tanca</string>
<string name="pref_fullscreen_title">Pantalla completa</string>
<string name="pref_fullscreen_summary">Amaga la barra d\'estat en consola</string>
+ <string name="pref_keyboard_category">Teclat</string>
<string name="pref_memkeys_title">Recorda les claus en memòria</string>
<string name="pref_memkeys_summary">Manté les claus descarregadesen memòria fins que el servei finalitzi</string>
- <string name="pref_update_title">Comprova actualització</string>
- <string name="pref_update_summary">Escull la freqüència màxima de comprovació d\'actualitzacions de ConnectBot</string>
<string name="pref_conn_persist_title">Connexions persistents</string>
<string name="pref_conn_persist_summary">Manté les connexions mentre l\'aplicació és en segon pla</string>
<string name="pref_keymode_title">Drecera de carpetes</string>
@@ -104,9 +104,6 @@
<string name="list_keymode_none">Deshabilita</string>
<string name="list_pubkeyids_none">No usis claus</string>
<string name="list_pubkeyids_any">Fes servir qualsevol clau desbloquejada</string>
- <string name="list_update_daily">Diàriament</string>
- <string name="list_update_weekly">Setmanalment</string>
- <string name="list_update_never">Mai</string>
<string name="hostpref_nickname_title">Sobrenom</string>
<string name="hostpref_color_title">Color de la categoria</string>
<string name="hostpref_fontsize_title">Mida de la lletra (pt)</string>
@@ -129,9 +126,6 @@
<string name="hostpref_hostname_title">Servidor</string>
<string name="hostpref_port_title">Port</string>
<string name="bind_never">Mai connectat</string>
- <string name="bind_minutes">Fa %1$s minuts</string>
- <string name="bind_hours">Fa %1$s hores</string>
- <string name="bind_days">Fa %1$s dies</string>
<string name="console_copy_done">Copiats %1$d bytes al porta-retalls</string>
<string name="console_copy_start">Toca i arrosega\no fes servir les tecles de direcció\nper escollir l\'àrea a copiar</string>
<string name="console_menu_close">Tanca</string>
@@ -201,9 +195,6 @@
<string name="terminal_enable_portfoward">Activa el redireccionament de ports: %1$s</string>
<string name="local_shell_unavailable">Fallada! El shell local no està disponible en aquest telèfon.</string>
<string name="notification_text">%1$s necessita la teva atenció.</string>
- <string name="upgrade">Nova versió</string>
- <string name="upgrade_pos">Sí, actualitza</string>
- <string name="upgrade_neg">No ara</string>
<string name="no">No</string>
<string name="with_confirmation">Amb confirmació</string>
<string name="yes">Sí</string>
@@ -214,4 +205,5 @@
<string name="color_green">verd</string>
<string name="color_blue">blau</string>
<string name="color_gray">gris</string>
+ <string name="image_description_connected">Connectat.</string>
</resources>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index a1834d8..e210502 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -1,6 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="app_desc">Jednoduchý, všestranný, open-source SSH klient</string>
+ <string name="service_desc">Správa SSH spojení a nahraných klíčů</string>
<string name="title_hosts_list">Hostitelé</string>
<string name="title_pubkey_list">Veřejné klíče</string>
<string name="title_port_forwards_list">Přesměrování portu</string>
@@ -21,20 +22,21 @@
<string name="pubkey_gather_entropy">Získávání entropie</string>
<string name="pubkey_touch_prompt">Dotkněte se plochy pro získání náhodnosti: %1$d%% hotovo</string>
<string name="pubkey_touch_hint">Pro zajištění náhodnosti během generování klíče pohybujte prstem náhodně po ploše níže.</string>
- <string name="pubkey_generating">Generování párování klíčem...</string>
+ <string name="pubkey_generating">Generování páru klíčů..</string>
<string name="pubkey_copy_private">Kopírovat soukromý klíč</string>
<string name="pubkey_copy_public">Kopírovat veřejný klíč</string>
- <string name="pubkey_list_empty">Stiskněte Menu pro získákní\nnebo vytvoření páru klíčů.</string>
+ <string name="pubkey_list_empty">Zvolte \"Menu\" pro vytvoření\nnebo import párů klíčů.</string>
<string name="pubkey_unknown_format">Neznámý formát</string>
<string name="pubkey_change_password">Změnit heslo</string>
<string name="pubkey_list_pick">Vyberte z /sdcard</string>
- <string name="pubkey_import_parse_problem">Problém s importací soukromého klíče</string>
- <string name="pubkey_unlock">Odblokovat klíč</string>
+ <string name="pubkey_import_parse_problem">Problém s importem soukromého klíče</string>
+ <string name="pubkey_unlock">Odemknout klíč</string>
+ <string name="pubkey_failed_add">Špatné heslo pro klíč \'%1$s\'. Ověření selhalo.</string>
<string name="pubkey_memory_load">Nahrát do paměti</string>
<string name="pubkey_memory_unload">Odstranit z paměti</string>
<string name="pubkey_load_on_start">Nahrát klíč při startu</string>
<string name="pubkey_confirm_use">Potvrdit před použitím</string>
- <string name="portforward_list_empty">Stiskněte Menu pro vytvoření\npřesměrování portů.</string>
+ <string name="portforward_list_empty">Zvolte \"Menu\" pro vytvoření\npřesměrování portů.</string>
<string name="portforward_edit">Upravit přesměrování portů</string>
<string name="portforward_delete">Smazat přesměrování portů</string>
<string name="prompt_nickname">Přezdívka:</string>
@@ -47,11 +49,14 @@
<string name="prompt_type">Typ:</string>
<string name="prompt_password_can_be_blank">Poznámka: heslo může být prázdné</string>
<string name="prompt_bits">Počet bitů:</string>
+ <string name="prompt_pubkey_password">Heslo pro klíč \'%1$s\'</string>
+ <string name="prompt_allow_agent_to_use_key">Povolit vzdálenému počítači\npoužít klíč \'%1$s\'?</string>
<string name="host_verification_failure_warning_header">VAROVÁNÍ: IDENTIFIKACE VZDÁLENÉHO HOSTA SE ZMĚNILA!</string>
- <string name="host_verification_failure_warning">JE MOŽNÉ ŽE NĚKDO PROVÁDÍ NĚCO NEKALÉHO!\nNěkdo vás právě může odposlouchávat (útok muže uprostřed)!\nTaké je možné že se byl klíč hosta právě změněn.</string>
+ <string name="host_verification_failure_warning">JE MOŽNÉ ŽE NĚKDO PROVÁDÍ NĚCO NEKALÉHO!\nNěkdo vás může odposlouchávat (man-in-the-middle)!\nTaké je možné že se klíč počítače právě změnil.</string>
<string name="prompt_host_disconnected">Host se odpojil.\nUkončit sezení?</string>
<string name="prompt_continue_connecting">Opravdu chcete\npokračovat v připojování?</string>
- <string name="host_fingerprint">Otisk prstu hosta %1$s je %2$s</string>
+ <string name="host_authenticity_warning">Nelze ověřit autenticitu počítače \'%1$s\'.</string>
+ <string name="host_fingerprint">Otisk klíče počítače %1$s je %2$s</string>
<string name="alert_passwords_do_not_match_msg">Hesla se neshodují!</string>
<string name="alert_wrong_password_msg">Špatné heslo!</string>
<string name="alert_key_corrupted_msg">Soukromý klíč vypadá poškozeně!</string>
@@ -61,7 +66,8 @@
<string name="button_generate">Generovat klíč</string>
<string name="button_resize">Změnit velikost</string>
<string name="alert_disconnect_msg">Spojení ztraceno</string>
- <string name="pref_emulation_category">Terminálová emulace</string>
+ <string name="msg_copyright">Copyright © 2007-2008 Kenny Root http://the-b.org/, Jeffrey Sharkey http://jsharkey.org/</string>
+ <string name="pref_emulation_category">Emulace terminálu</string>
<string name="pref_emulation_title">Režim emulace</string>
<string name="pref_emulation_summary">Režim emulace terminálu použitý při PTY spojeních</string>
<string name="pref_scrollback_title">Velikost historie</string>
@@ -73,21 +79,20 @@
<string name="pref_fullscreen_summary">Skrýt stavový řádek když je zobrazena konzole</string>
<string name="pref_memkeys_title">Uchovávat klíče v paměti</string>
<string name="pref_memkeys_summary">Ponechat odemčené klíče v paměti dokud není ukončena služba na pozadí</string>
- <string name="pref_update_title">Kontrolovat aktualizace</string>
- <string name="pref_update_summary">Nastavit maximální frekvenci pro kontrolu aktualizací ConnectBota</string>
<string name="pref_conn_persist_title">Trvalá spojení</string>
<string name="pref_conn_persist_summary">Vynutit spojení i v pozadí</string>
<string name="pref_keymode_title">Zkratky adresářů</string>
+ <string name="pref_keymode_summary">Volba použití kláves Alt pro znak \"/\" a Shift pro tabulátor</string>
<string name="pref_camera_title">Zkratka fotoaparátu</string>
<string name="pref_camera_summary">Vyberte kterou zkratku použít když je stisknuto tlačítko fotoaparátu</string>
<string name="pref_keepalive_title">Ponechat zapnutou obrazovku</string>
<string name="pref_keepalive_summary">Zabránit obrazovce ve vypnutí při práci v konzoli</string>
<string name="pref_wifilock_title">Ponechat Wi-Fi aktivní</string>
<string name="pref_wifilock_summary">Zabránit Wi-Fi ve vypnutí když je sezení aktivní</string>
- <string name="pref_bumpyarrows_title">Hrbolaté šipky</string>
+ <string name="pref_bumpyarrows_title">Hmatová odezva šipek</string>
<string name="pref_bumpyarrows_summary">Vibrovat při posílání kurzorových kláves z trackballu; užitečná pro špatná spojení</string>
<string name="pref_bell_category">Zvonek terminálu</string>
- <string name="pref_bell_title">slyšitelný zvonek</string>
+ <string name="pref_bell_title">Zvukové upozornění</string>
<string name="pref_bell_volume_title">Hlasitost zvonku</string>
<string name="pref_bell_vibrate_title">Vibrovat při zvonku</string>
<string name="pref_bell_notification_title">Upozornění v pozadí</string>
@@ -97,9 +102,6 @@
<string name="list_keymode_none">Vypnout</string>
<string name="list_pubkeyids_none">Nepoužívat klíče</string>
<string name="list_pubkeyids_any">Použít jakýkoliv odemčený klíč</string>
- <string name="list_update_daily">Denně</string>
- <string name="list_update_weekly">Týdně</string>
- <string name="list_update_never">Nikdy</string>
<string name="hostpref_nickname_title">Přezdívka</string>
<string name="hostpref_color_title">Barevná kategorie</string>
<string name="hostpref_fontsize_title">Velikost fontu (pt)</string>
@@ -120,9 +122,6 @@
<string name="hostpref_connection_category">Nastavení připojení</string>
<string name="hostpref_username_title">Uživatelské jméno</string>
<string name="bind_never">Nikdy nepřipojen</string>
- <string name="bind_minutes">Před %1$s minutami</string>
- <string name="bind_hours">Před %1$s hodinami</string>
- <string name="bind_days">Před %1$s dny</string>
<string name="console_copy_done">Zkopírováno %1$d bajtů do schránky</string>
<string name="console_copy_start">Stiskněte a táhněte\nnebo použijte směrový ovladač\npro výběr oblasti ke zkopírování</string>
<string name="console_menu_close">Zavřít</string>
@@ -139,9 +138,11 @@
<string name="portforward_pos">Vytvořit přesměrování portu</string>
<string name="portforward_done">Přesměrování portu úspěšně vytvořeno</string>
<string name="portforward_menu_add">Přidat přesměrování portu</string>
+ <string name="hint_userhost">user\@hostname</string>
<string name="list_format_error">Použijte formát %1$s</string>
<string name="format_username">uživatelské jméno</string>
<string name="format_hostname">jméno hosta</string>
+ <string name="format_port">port</string>
<string name="list_menu_pubkeys">Spravovat veřejné klíče</string>
<string name="list_menu_sortcolor">Seřadit podle barvy</string>
<string name="list_menu_sortname">Seřadit podle jména</string>
@@ -156,7 +157,13 @@
<string name="list_rotation_port">Vynutit na výšku</string>
<string name="list_rotation_auto">Automaticky</string>
<string name="list_camera_ctrlaspace">Ctrl+A potom mezerník</string>
+ <string name="list_camera_ctrla">Ctrl+A</string>
+ <string name="list_camera_esc">Esc</string>
+ <string name="list_camera_esc_a">Esc+A</string>
<string name="list_camera_none">Nic</string>
+ <string name="list_delkey_backspace">Backspace</string>
+ <string name="list_delkey_del">Delete</string>
+ <string name="delete_message">Určitě chcete smazat \'%1$s\'?</string>
<string name="delete_pos">Ano, smazat</string>
<string name="delete_neg">Zrušit</string>
<string name="wizard_agree">Souhlasím</string>
@@ -174,9 +181,6 @@
<string name="terminal_enable_portfoward">Povolit přesměrování portu: %1$s</string>
<string name="local_shell_unavailable">Selhání! Místní shell není na tomto telefonu podporován</string>
<string name="notification_text">%1$s žádá pozornost.</string>
- <string name="upgrade">Nová verze</string>
- <string name="upgrade_pos">Ano, povýšit</string>
- <string name="upgrade_neg">Nyní ne</string>
<string name="no">Ne</string>
<string name="with_confirmation">S potvrzením</string>
<string name="yes">Ano</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 792f249..d66550a 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -1,9 +1,10 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="app_desc">Enkel, kraftfuld, open source SSH klient</string>
+ <string name="service_desc">Opretholder SSH forbindelser og indlæste offentlige nøgler</string>
<string name="title_hosts_list">Værter</string>
<string name="title_pubkey_list">Offentlige nøgler</string>
- <string name="title_port_forwards_list">Port videresendelser</string>
+ <string name="title_port_forwards_list">Port-videresendelser</string>
<string name="title_host_editor">Rediger vært</string>
<string name="title_help">Hjælp</string>
<string name="title_colors">Farver</string>
@@ -15,16 +16,16 @@
<string name="help_intro">Vælg et emne nedenfor for yderligere information.</string>
<string name="help_about">Om ConnectBot</string>
<string name="help_keyboard">Tastatur</string>
- <string name="pubkey_generate">Generer</string>
+ <string name="pubkey_generate">Opret</string>
<string name="pubkey_import">Importér</string>
<string name="pubkey_delete">Slet nøgle</string>
<string name="pubkey_gather_entropy">Indsamler entropi</string>
<string name="pubkey_touch_prompt">Berør denne boks for at generere tilfældighed: %1$d%% færdig</string>
- <string name="pubkey_touch_hint">For at garantere tilfældig data til skabelsen af nøglen, skal du flytte din finger tilfældigt indenfor boksen nedenfor.</string>
- <string name="pubkey_generating">Generer nøglepar...</string>
+ <string name="pubkey_touch_hint">For at garantere tilfældig data til oprettelse af nøglen, skal du flytte din finger tilfældigt indenfor boksen nedenfor.</string>
+ <string name="pubkey_generating">Opretter nøglepar...</string>
<string name="pubkey_copy_private">Kopier privat nøgle</string>
<string name="pubkey_copy_public">Kopier offentlig nøgle</string>
- <string name="pubkey_list_empty">Tryk Menu for at skabe\neller importere nøglepar.</string>
+ <string name="pubkey_list_empty">Tryk Menu for at oprette\nog importere nøglepar.</string>
<string name="pubkey_unknown_format">Ukendt format</string>
<string name="pubkey_change_password">Ændre adgangskode</string>
<string name="pubkey_list_pick">Vælg fra /sdcard</string>
@@ -34,18 +35,20 @@
<string name="pubkey_memory_load">Indlæs i hukommelse</string>
<string name="pubkey_memory_unload">Fjern fra hukommelse</string>
<string name="pubkey_load_on_start">Indlæs nøgle ved start</string>
- <string name="pubkey_confirm_use">Konfirmer før brug</string>
+ <string name="pubkey_confirm_use">Bekræft før brug</string>
<string name="portforward_list_empty">Tryk Menu for at oprette\nport-videresendelser.</string>
<string name="portforward_edit">Rediger port-videresendelse</string>
<string name="portforward_delete">Slet port-videresendelse</string>
- <string name="prompt_nickname">Øgenavn:</string>
+ <string name="prompt_nickname">Kaldenavn:</string>
<string name="prompt_nickname_hint_pubkey">Min arbejdsnøgle</string>
<string name="prompt_source_port">Kildeport:</string>
+ <string name="prompt_destination">Destination:</string>
<string name="prompt_old_password">Gammel adgangskode:</string>
<string name="prompt_password">Adgangskode:</string>
<string name="prompt_again">(igen)</string>
- <string name="prompt_type">Tast:</string>
+ <string name="prompt_type">Type:</string>
<string name="prompt_password_can_be_blank">Bemærk: Adgangskoden kan være blank</string>
+ <string name="prompt_bits">Bit:</string>
<string name="prompt_pubkey_password">Adgangskode for nøglen \'%1$s\'</string>
<string name="prompt_allow_agent_to_use_key">Tillad fjernvært at\nbruge nøglen \'%1$s\'?</string>
<string name="host_verification_failure_warning_header">ADVARSEL: FJERNVÆRTENS IDENTIFIKATION ER ÆNDRET!</string>
@@ -60,34 +63,33 @@
<string name="alert_sdcard_absent">SD-kort er ikke sat i!</string>
<string name="button_add">Tilføj</string>
<string name="button_change">Ændre</string>
- <string name="button_generate">Generer nøgle</string>
+ <string name="button_generate">Opret nøgle</string>
<string name="button_resize">Ændre størrelse</string>
<string name="alert_disconnect_msg">Forbindelse tabt</string>
- <string name="pref_emulation_category">Stands emulation</string>
- <string name="pref_emulation_title">Emulations mode</string>
- <string name="pref_emulation_summary">Stands emulations mode for at bruge PTY forbindelse</string>
+ <string name="msg_copyright">Copyright © 2007-2008 Kenny Root http://the-b.org/, Jeffrey Sharkey http://jsharkey.org/</string>
+ <string name="pref_emulation_category">Terminal emulering</string>
+ <string name="pref_emulation_title">Emulerings metode</string>
+ <string name="pref_emulation_summary">Terminal emulerings metode til brug ved PTY forbindelser</string>
<string name="pref_scrollback_title">Rul-tilbage buffer</string>
<string name="pref_scrollback_summary">Størrelsen af rul-tilbage bufferen for hver konsol</string>
- <string name="pref_ui_category">Bruger interface</string>
- <string name="pref_rotation_title">Rotations mode</string>
+ <string name="pref_ui_category">Brugerflade</string>
+ <string name="pref_rotation_title">Rotations tilstand</string>
<string name="pref_rotation_summary">Hvordan skærmen roteres når keyboard skubbes ind/ud</string>
<string name="pref_fullscreen_title">Fuld skærm</string>
<string name="pref_fullscreen_summary">Skjul statusbar i konsol</string>
<string name="pref_memkeys_title">Husk nøgler i hukommelsen</string>
- <string name="pref_memkeys_summary">Gem oplåste nøgler i hukommelsen indtil backend servicen er afsluttet</string>
- <string name="pref_update_title">Opdaterings tjek</string>
- <string name="pref_update_summary">Maksimal tid mellem check for opdateringer til ConnectBot</string>
+ <string name="pref_memkeys_summary">Gem oplåste nøgler i hukommelsen indtil baggrunds servicen er afsluttet</string>
<string name="pref_conn_persist_title">Vedvarende forbindelser</string>
<string name="pref_conn_persist_summary">Tving forbindelser til at være aktive i baggrunden</string>
<string name="pref_keymode_title">Biblioteksgenveje</string>
<string name="pref_keymode_summary">Vælg hvordan Alt for \'/\' og Skift for Tabulater skal bruges</string>
<string name="pref_camera_title">Kamera genveje</string>
<string name="pref_camera_summary">Vælg genvej som kamera-knap skal aktivere</string>
- <string name="pref_keepalive_title">Hold skærm vågen</string>
- <string name="pref_keepalive_summary">Undgå skærm slukker når der arbejdes i konsol</string>
+ <string name="pref_keepalive_title">Hold skærm tændt</string>
+ <string name="pref_keepalive_summary">Undgå skærm slukker når der arbejdes i en konsol</string>
<string name="pref_wifilock_title">Hold Wi-Fi aktiv</string>
- <string name="pref_wifilock_summary">Undgå at WI-FI slår fra når en session er aktiv</string>
- <string name="pref_bumpyarrows_title">Knoldede pile</string>
+ <string name="pref_wifilock_summary">Undgå at Wi-Fi slår fra imens en session er aktiv</string>
+ <string name="pref_bumpyarrows_title">Vibrerende pile</string>
<string name="pref_bumpyarrows_summary">Vibrér når der afsendes piletaster fra trackballen; nyttig ved langsomme forbindelser</string>
<string name="pref_bell_category">Terminalklokke</string>
<string name="pref_bell_title">Hørbar klokke</string>
@@ -100,72 +102,72 @@
<string name="list_keymode_none">Slå fra</string>
<string name="list_pubkeyids_none">Brug ikke tasterne</string>
<string name="list_pubkeyids_any">Anvend enhver ulåst nøgle</string>
- <string name="list_update_daily">Dagligt</string>
- <string name="list_update_weekly">Ugenligt</string>
- <string name="list_update_never">Aldrig</string>
- <string name="hostpref_nickname_title">Øgenavn</string>
- <string name="hostpref_color_title">Farvekategorier</string>
+ <string name="hostpref_nickname_title">Kaldenavn</string>
+ <string name="hostpref_color_title">Farvekategori</string>
<string name="hostpref_fontsize_title">Skriftstørrelse (pt)</string>
<string name="hostpref_pubkeyid_title">Brug offentlig nøgleautorisering</string>
- <string name="hostpref_authagent_title">Brug SSH auth agent</string>
+ <string name="hostpref_authagent_title">Brug SSH godkendelses klient</string>
<string name="hostpref_postlogin_title">Post-login automatik</string>
<string name="hostpref_postlogin_summary">Udfør kommandoer på fjernserveren efter autorisering</string>
<string name="hostpref_compression_title">Komprimering</string>
<string name="hostpref_compression_summary">Dette kan hjælpe på langsomme netværk</string>
- <string name="hostpref_wantsession_summary">Deaktiver denne præference og brug kun portvideresendelser</string>
- <string name="hostpref_stayconnected_title">Forbliv forbundet</string>
- <string name="hostpref_stayconnected_summary">Genforbind hvis forbindelsen tabes</string>
- <string name="hostpref_delkey_title">DEL knap</string>
- <string name="hostpref_delkey_summary">Tast som sendes når DEL knappen trykkes</string>
+ <string name="hostpref_wantsession_title">Start konsol session</string>
+ <string name="hostpref_wantsession_summary">Deaktiver denne præference og brug kun port-videresendelser</string>
+ <string name="hostpref_stayconnected_title">Oprethold forbindelse</string>
+ <string name="hostpref_stayconnected_summary">Forbind igen hvis forbindelsen tabes</string>
+ <string name="hostpref_delkey_title">Slet tast</string>
+ <string name="hostpref_delkey_summary">Tast som sendes når slet tasten benyttes</string>
<string name="hostpref_encoding_title">Tekstkodning</string>
<string name="hostpref_encoding_summary">Tekstkodning for værten</string>
<string name="hostpref_connection_category">Forbindelsesindstillinger</string>
<string name="hostpref_username_title">Brugernavn</string>
<string name="hostpref_hostname_title">Vært</string>
+ <string name="hostpref_port_title">Port</string>
<string name="bind_never">Aldrig forbundet</string>
- <string name="bind_minutes">%1$s minutter siden</string>
- <string name="bind_hours">%1$s timer siden</string>
- <string name="bind_days">%1$s dage siden</string>
- <string name="console_copy_done">Kopier %1$d bytes til udklipsholder</string>
+ <string name="console_copy_done">Kopier %1$d byte til udklipsholder</string>
<string name="console_copy_start">Berør og flyt\neller brug piletasterne\nfor at vælge område at kopiere</string>
<string name="console_menu_close">Luk</string>
<string name="console_menu_copy">Kopier</string>
<string name="console_menu_paste">Indsæt</string>
- <string name="console_menu_portforwards">Portvideresendelser</string>
+ <string name="console_menu_portforwards">Port-videresendelser</string>
<string name="console_menu_resize">Tving størrelse</string>
- <string name="console_menu_urlscan">URL scan</string>
+ <string name="console_menu_urlscan">URL skan</string>
<string name="button_yes">Ja</string>
<string name="button_no">Nej</string>
<string name="portforward_local">Lokal</string>
<string name="portforward_remote">Fjern</string>
<string name="portforward_dynamic">Dynamisk (SOCKS)</string>
- <string name="portforward_pos">Opret portvideresendelse</string>
- <string name="portforward_done">Oprettede portvideresendelse</string>
- <string name="portforward_problem">Der opstod problemer ved oprettelsen af portvideresendelse. Måske bruger du en port under 1024, eller en port som allerede er i brug.</string>
- <string name="portforward_menu_add">Tilføj portvideresendelse</string>
- <string name="hint_userhost">bruger\@værtsnavn</string>
+ <string name="portforward_pos">Opret port-videresendelse</string>
+ <string name="portforward_done">Port-videresendelse oprettet</string>
+ <string name="portforward_problem">Der opstod problemer ved oprettelsen af port-videresendelse. Måske bruger du en port lavere end 1025, eller en port som allerede er i brug.</string>
+ <string name="portforward_menu_add">Tilføj port-videresendelse</string>
+ <string name="hint_userhost">brugernavn\@vært</string>
<string name="list_format_error">Anvend formatet %1$s</string>
<string name="format_username">brugernavn</string>
<string name="format_hostname">værtsnavn</string>
+ <string name="format_port">port</string>
<string name="list_menu_pubkeys">Administrer offentlige nøgler</string>
<string name="list_menu_sortcolor">Sortér efter farve</string>
<string name="list_menu_sortname">Sortér efter navn</string>
<string name="list_menu_settings">Indstillinger</string>
<string name="list_host_disconnect">Afbryd forbindelse</string>
<string name="list_host_edit">Redigér vært</string>
- <string name="list_host_portforwards">Redigér portvideresendelse</string>
+ <string name="list_host_portforwards">Redigér port-videresendelser</string>
<string name="list_host_delete">Slet vært</string>
- <string name="list_host_empty">Anvend hurtig-forbind boksen\nnedenfor for at oprette forbindelse\n til en vært.</string>
+ <string name="list_host_empty">Anvend hurtig boksen nedenfor\nfor at oprette forbindelse\ntil en vært.</string>
<string name="list_rotation_default">Standard</string>
<string name="list_rotation_land">Fremtving liggende</string>
<string name="list_rotation_port">Fremtving stående</string>
<string name="list_rotation_auto">Automatisk</string>
<string name="list_camera_ctrlaspace">Ctrl+A derefter mellemrum</string>
+ <string name="list_camera_ctrla">Ctrl+A</string>
+ <string name="list_camera_esc">Esc</string>
+ <string name="list_camera_esc_a">Esc+A</string>
<string name="list_camera_none">Ingen</string>
<string name="list_delkey_backspace">Slet tilbage</string>
<string name="list_delkey_del">Slet</string>
<string name="delete_message">Er du sikker på at du vil slette \'%1$s\'?</string>
- <string name="delete_pos">Ja slet</string>
+ <string name="delete_pos">Ja, slet</string>
<string name="delete_neg">Afbryd</string>
<string name="wizard_agree">Godkend</string>
<string name="wizard_next">Næste</string>
@@ -183,21 +185,18 @@
<string name="terminal_auth_pubkey_any">Forsøger \'publickey\'-autorisering med enhver offentlig nøgle i hukommelsen</string>
<string name="terminal_auth_pubkey_invalid">Valgte offentlige nøgle er ugyldig, prøv at vælge nøglen igen</string>
<string name="terminal_auth_pubkey_specific">Forsøger \'publickey\'-autorisering med valgte offentlige nøgle</string>
- <string name="terminal_auth_pubkey_fail">autentifikationsmetoden \'publickey\' med nøglen \'%1$s\' fejlede</string>
+ <string name="terminal_auth_pubkey_fail">Autentifikationsmetoden \'publickey\' med nøglen \'%1$s\' fejlede</string>
<string name="terminal_auth_ki">Forsøger \'keyboard-interactive\' autorisering</string>
<string name="terminal_auth_ki_fail">\'keyboard-interactive\'-autorisering fejlede</string>
<string name="terminal_auth_fail">[Din vært understøtter ikke \'password\' eller \'keyboard-interactive\' autorisering.]</string>
<string name="terminal_no_session">Sessionen kunne ikke startes pga. værtens preferencer</string>
- <string name="terminal_enable_portfoward">Aktiver port videreførsel: %1$s</string>
- <string name="local_shell_unavailable">Fejl! Lokal shell er ikke tilgængelig på denne telefon.</string>
+ <string name="terminal_enable_portfoward">Aktiver port-videresendelse: %1$s</string>
+ <string name="local_shell_unavailable">Fejl! Lokal konsol er ikke tilgængelig på denne telefon.</string>
<string name="notification_text">%1$s vil have din opmærksomhed.</string>
- <string name="upgrade">Ny version</string>
- <string name="upgrade_pos">Ja, opgradér</string>
- <string name="upgrade_neg">Ikke lige nu</string>
<string name="no">Nej</string>
<string name="with_confirmation">Med godkendelse</string>
<string name="yes">Ja</string>
- <string name="exceptions_submit_message">Tilsyneladende havde ConnectBot problemer sidste gang det blev brugt. Videresend fejlrapport til ConnectBot udviklerne?</string>
+ <string name="exceptions_submit_message">ConnectBot havde tilsyneladende problemer sidste gang det blev brugt. Send fejlrapport til ConnectBot udviklerne?</string>
<string name="menu_colors_reset">Nulstil</string>
<string name="app_is_running">ConnectBot kører</string>
<string name="color_red">rød</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index ebef634..eadad3b 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -1,10 +1,14 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="app_desc">Einfacher, mächtiger, open-source SSH client.</string>
+ <string name="service_desc">Hält SSH-Verbindungen und geladene öffentliche Schlüssel aufrecht</string>
+ <string name="title_hosts_list">Hosts</string>
+ <string name="title_pubkey_list">Öffentliche Schlüssel</string>
<string name="title_port_forwards_list">Port-Weiterleitungen</string>
<string name="title_host_editor">Host bearbeiten</string>
<string name="title_help">Hilfe</string>
<string name="title_colors">Farben</string>
+ <string name="title_color_picker">Farbe wählen</string>
<string name="resolve_connect">Verbinden</string>
<string name="resolve_entropy">Sammle Entropie</string>
<string name="menu_insert">Host hinzufügen</string>
@@ -22,19 +26,19 @@
<string name="pubkey_generating">Erzeuge Schlüsselpaar...</string>
<string name="pubkey_copy_private">Privaten Schlüssel kopieren</string>
<string name="pubkey_copy_public">Öffentlichen Schlüssel kopieren</string>
- <string name="pubkey_list_empty">Menu drücken, um Schlüsselpaare\nzu erzeugen oder importieren.</string>
+ <string name="pubkey_list_empty">\"Menu\" drücken, um Schlüsselpaare\nzu erzeugen oder zu importieren.</string>
<string name="pubkey_unknown_format">Unbekanntes Format</string>
<string name="pubkey_change_password">Passwort ändern</string>
<string name="pubkey_list_pick">Wähle von /sdcard</string>
- <string name="pubkey_import_parse_problem">Konnte importierten geheimen Schlüssel nicht parsen.</string>
- <string name="pubkey_unlock">Schlüssel entsperren.</string>
+ <string name="pubkey_import_parse_problem">Fehler beim Parsen des importierten, geheimen Schlüssels</string>
+ <string name="pubkey_unlock">Schlüssel entsperren</string>
<string name="pubkey_failed_add">Falsches Passwort für Schlüssel \'%1$s\'. Legitimation fehlgeschlagen.</string>
- <string name="pubkey_memory_load">In Speicher laden.</string>
- <string name="pubkey_memory_unload">Aus Speicher entfernen.</string>
+ <string name="pubkey_memory_load">In Speicher laden</string>
+ <string name="pubkey_memory_unload">Aus Speicher entfernen</string>
<string name="pubkey_load_on_start">Schlüssel beim Start laden</string>
<string name="pubkey_confirm_use">Bestätigen vor Benutzung</string>
- <string name="portforward_list_empty">Menu tippen, um Port-\nWeiterleitungen zu erzeugen.</string>
- <string name="portforward_edit">Port-Weiterleitung editieren.</string>
+ <string name="portforward_list_empty">Auf \"Menu\" tippen um Port-\nWeiterleitungen zu erstellen.</string>
+ <string name="portforward_edit">Port-Weiterleitung bearbeiten.</string>
<string name="portforward_delete">Port-Weiterleitung löschen.</string>
<string name="prompt_nickname">Spitzname:</string>
<string name="prompt_nickname_hint_pubkey">Mein Schlüssel</string>
@@ -45,6 +49,7 @@
<string name="prompt_again">(nochmal)</string>
<string name="prompt_type">Typ:</string>
<string name="prompt_password_can_be_blank">Hinweis: Passwort kann leer bleiben</string>
+ <string name="prompt_bits">Bits:</string>
<string name="prompt_pubkey_password">Passwort für Schlüssel \'%1$s\'</string>
<string name="prompt_allow_agent_to_use_key">Remote Host die Benutzung\ndes Schlüssels \'%1$s\' erlauben?</string>
<string name="host_verification_failure_warning_header">WARNUNG: IDENTIFIKATION DES ENTFERNTEN HOSTS HAT SICH GEÄNDERT!</string>
@@ -62,6 +67,7 @@
<string name="button_generate">Schlüssel erzeugen</string>
<string name="button_resize">Größe ändern</string>
<string name="alert_disconnect_msg">Die Verbindung wurde getrennt.</string>
+ <string name="msg_copyright">Copyright © 2007-2008 Kenny Root http://the-b.org/, Jeffrey Sharkey http://jsharkey.org/</string>
<string name="pref_emulation_category">Terminalemulation</string>
<string name="pref_emulation_title">Emulationsmodus</string>
<string name="pref_emulation_summary">Terminalemulationsmodus für PTY Verbindungen</string>
@@ -72,10 +78,9 @@
<string name="pref_rotation_summary">Wie soll die Rotation beim Ein-/Ausklappen der Tastatur verändert werden</string>
<string name="pref_fullscreen_title">Vollbild</string>
<string name="pref_fullscreen_summary">Statuszeile in der Konsole verbergen</string>
+ <string name="pref_keyboard_category">Tastatur</string>
<string name="pref_memkeys_title">Schlüssel im Speicher behalten</string>
<string name="pref_memkeys_summary">Entsperrte Schlüssel im Speicher halten, bis der Hintergrunddienst beendet wurde</string>
- <string name="pref_update_title">Aktualisierungen suchen</string>
- <string name="pref_update_summary">Die maximale Frequenz einstellen, mit der nach ConnectBot Aktualisierungen gesucht werden soll</string>
<string name="pref_conn_persist_title">Persistente Verbindungen</string>
<string name="pref_conn_persist_summary">Erhalte Verbindungen auch im Hintergrund</string>
<string name="pref_keymode_title">Verzeichniskürzel</string>
@@ -86,7 +91,7 @@
<string name="pref_keepalive_summary">Verhindern, dass beim Arbeiten an der Konsole der Bildschirm abgeschaltet wird</string>
<string name="pref_wifilock_title">WLAN aktiviert lassen</string>
<string name="pref_wifilock_summary">Verhindern, dass während einer aktiven Sitzung WLAN deaktiviert wird</string>
- <string name="pref_bumpyarrows_title">Holprige Pfeile</string>
+ <string name="pref_bumpyarrows_title">Vibrierende Pfeiltasten</string>
<string name="pref_bumpyarrows_summary">Vibirieren, wenn Pfeiltasten vom Trackball gesendet werden; nützlich bei laggenden Verbindungen</string>
<string name="pref_bell_category">Terminalglocke</string>
<string name="pref_bell_title">Hörbare Glocke</string>
@@ -99,10 +104,7 @@
<string name="list_keymode_none">Ausschalten</string>
<string name="list_pubkeyids_none">Keine Schlüssel verwenden</string>
<string name="list_pubkeyids_any">Beliebigen entsperrten Schlüssel verwenden</string>
- <string name="list_update_daily">Täglich</string>
- <string name="list_update_weekly">Wöchentlich</string>
- <string name="list_update_never">Niemals</string>
- <string name="hostpref_nickname_title">Spitzname</string>
+ <string name="hostpref_nickname_title">Nutzername</string>
<string name="hostpref_color_title">Farbkategorie</string>
<string name="hostpref_fontsize_title">Fontgröße (pt)</string>
<string name="hostpref_pubkeyid_title">Verwende pubkey Authentisierung</string>
@@ -117,13 +119,13 @@
<string name="hostpref_stayconnected_summary">Verbindung bei Verbindungsabbruch wiederherstellen</string>
<string name="hostpref_delkey_title">DEL Taste</string>
<string name="hostpref_delkey_summary">Zu sendender Tastencode wenn DEL gedrückt wird</string>
+ <string name="hostpref_encoding_title">Zeichensatz</string>
<string name="hostpref_encoding_summary">Zeichen-Encoding für den Host</string>
<string name="hostpref_connection_category">Verbindungseinstellungen</string>
<string name="hostpref_username_title">Benutzername</string>
+ <string name="hostpref_hostname_title">Host</string>
+ <string name="hostpref_port_title">Port</string>
<string name="bind_never">Nie verbunden</string>
- <string name="bind_minutes">vor %1$s Minuten</string>
- <string name="bind_hours">vor %1$s Stunden</string>
- <string name="bind_days">vor %1$s Tagen</string>
<string name="console_copy_done">%1$d Byte in die Zwischenablage kopiert</string>
<string name="console_copy_start">Berühren und ziehen\noder Trackball verwenden\num zu kopierenden Bereich auszuwählen</string>
<string name="console_menu_close">Schließen</string>
@@ -131,6 +133,7 @@
<string name="console_menu_paste">Einfügen</string>
<string name="console_menu_portforwards">Port-Weiterleitungen</string>
<string name="console_menu_resize">Größe erzwingen</string>
+ <string name="console_menu_urlscan">URL Scan</string>
<string name="button_yes">Ja</string>
<string name="button_no">Nein</string>
<string name="portforward_local">Lokal</string>
@@ -140,6 +143,7 @@
<string name="portforward_done">Port-Weiterleitung angelegt.</string>
<string name="portforward_problem">Konnte Port-Weiterleitung nicht anlegen. Verwenden Sie Ports kleiner als 1024, oder wird der Port bereits verwendet?</string>
<string name="portforward_menu_add">Port-Weiterleitung hinzufügen</string>
+ <string name="hint_userhost">benutzer\@hostname</string>
<string name="list_format_error">Benutze das Format %1$s</string>
<string name="format_username">Benutzername</string>
<string name="format_hostname">Hostname</string>
@@ -159,7 +163,10 @@
<string name="list_rotation_auto">Automatisch</string>
<string name="list_camera_ctrlaspace">Strg+A dann Space</string>
<string name="list_camera_ctrla">Strg+A</string>
+ <string name="list_camera_esc">Esc</string>
+ <string name="list_camera_esc_a">Esc+A</string>
<string name="list_camera_none">Nichts</string>
+ <string name="list_delkey_backspace">Rücktaste</string>
<string name="list_delkey_del">Entfernen</string>
<string name="delete_message">Soll \'%1$s\' wirklich gelöscht werden?</string>
<string name="delete_pos">Ja, löschen</string>
@@ -188,9 +195,6 @@
<string name="terminal_enable_portfoward">Aktiviere Port Forwarding: %1$s</string>
<string name="local_shell_unavailable">Fehler! Lokale Kommandozeile ist auf diesem Telefon nicht verfügbar.</string>
<string name="notification_text">%1$s will ihre Aufmerksamkeit.</string>
- <string name="upgrade">Neue Version</string>
- <string name="upgrade_pos">Ja, aktualisieren</string>
- <string name="upgrade_neg">Nicht jetzt</string>
<string name="no">Nein</string>
<string name="with_confirmation">Mit Bestätigung</string>
<string name="yes">Ja</string>
@@ -201,4 +205,11 @@
<string name="color_green">grün</string>
<string name="color_blue">blau</string>
<string name="color_gray">grau</string>
+ <string name="colors_fg_label">SF: %1$d</string>
+ <string name="color_bg_label">HF: %1$d</string>
+ <string name="image_description_connected">Verbunden.</string>
+ <string name="image_description_key_is_locked">Schlüssel ist gesperrt.</string>
+ <string name="image_description_toggle_control_character">Steuerzeichen umschalten</string>
+ <string name="image_description_send_escape_character">Escape-Zeichen senden.</string>
+ <string name="image_description_show_keyboard">Tastatur anzeigen.</string>
</resources>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 94edc23..5b0e837 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -1,2 +1,6 @@
<?xml version='1.0' encoding='utf-8'?>
-<resources/>
+<resources>
+ <string name="title_help">Βοήθεια</string>
+ <string name="title_colors">Χρώματα</string>
+ <string name="resolve_connect">Σύνδεση</string>
+</resources>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..fabe684
--- /dev/null
+++ b/res/values-en-rGB/strings.xml
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='utf-8'?>
+<resources>
+ <string name="title_colors">Colours</string>
+ <string name="hostpref_color_title">Colour category</string>
+ <string name="list_menu_sortcolor">Sort by colour</string>
+ <string name="color_gray">grey</string>
+</resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index c57ee01..d8066a1 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -1,6 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="app_desc">Cliente de SSH sencillo, potente y open-source</string>
+ <string name="service_desc">Mantiene conexiones SSH y las claves públicas guardadas</string>
<string name="title_hosts_list">Máquinas</string>
<string name="title_pubkey_list">Claves Publicas</string>
<string name="title_port_forwards_list">Traducciones de puerto</string>
@@ -47,6 +48,7 @@
<string name="prompt_again">(de nuevo)</string>
<string name="prompt_type">Tipo:</string>
<string name="prompt_password_can_be_blank">Nota: la contraseña puede ser dejada en blanco</string>
+ <string name="prompt_bits">Bits:</string>
<string name="prompt_pubkey_password">Contraseña para clave \'%1$s\'</string>
<string name="prompt_allow_agent_to_use_key">¿Autorizar maquina remota\nde usar clave \'%1$s\'?</string>
<string name="host_verification_failure_warning_header">ATENCIÓN: ¡LA IDENTIFICACIÓN DE LA MAQUINA REMOTA HA CAMBIADO!</string>
@@ -77,8 +79,6 @@
<string name="pref_fullscreen_summary">Esconder barra de estatus cuando este en consola</string>
<string name="pref_memkeys_title">Recordar claves en memoria</string>
<string name="pref_memkeys_summary">Recordar claves en memoria hasta que el servicio en segundo plano termine</string>
- <string name="pref_update_title">Revisar por actualizaciones</string>
- <string name="pref_update_summary">Establecer la frequencia maxima para revisar actualizaciones de ConnectBot</string>
<string name="pref_conn_persist_title">Conexiones persistentes</string>
<string name="pref_conn_persist_summary">Mantener conexiones activas en segundo plano</string>
<string name="pref_keymode_title">Secuencia de teclas para directorios</string>
@@ -102,9 +102,6 @@
<string name="list_keymode_none">Desactivar</string>
<string name="list_pubkeyids_none">No usar teclas</string>
<string name="list_pubkeyids_any">Utilizar cualquier clave desbloqueada</string>
- <string name="list_update_daily">Diariamente</string>
- <string name="list_update_weekly">Semanalmente</string>
- <string name="list_update_never">Nunca</string>
<string name="hostpref_nickname_title">Apodo</string>
<string name="hostpref_color_title">Categoria de colores</string>
<string name="hostpref_fontsize_title">Tamaño de la fuente (pt)</string>
@@ -127,9 +124,6 @@
<string name="hostpref_hostname_title">Host (equipo anfitrión)</string>
<string name="hostpref_port_title">Puerto</string>
<string name="bind_never">Nunca fué conectado</string>
- <string name="bind_minutes">Hace %1$s minutos</string>
- <string name="bind_hours">Hace %1$s horas</string>
- <string name="bind_days">Hace %1$s días</string>
<string name="console_copy_done">%1$d bytes copiados al portapaleles</string>
<string name="console_copy_start">Toque y arrastre\no use la almohadilla direccional\npara seleccionar la zona a copiar</string>
<string name="console_menu_close">Cerrar</string>
@@ -139,6 +133,8 @@
<string name="console_menu_resize">Forzar un tamaño</string>
<string name="console_menu_urlscan">Escaneo de direcciones</string>
<string name="button_yes">Sí</string>
+ <string name="button_no">Denegar</string>
+ <string name="portforward_local">Local</string>
<string name="portforward_remote">Remoto</string>
<string name="portforward_dynamic">Dinámicos (SOCKS)</string>
<string name="portforward_pos">Crear reenvio de puerto</string>
@@ -164,6 +160,9 @@
<string name="list_rotation_port">Forzar retrato</string>
<string name="list_rotation_auto">Automático</string>
<string name="list_camera_ctrlaspace">Ctrl+A después espacio</string>
+ <string name="list_camera_ctrla">Ctrl+A</string>
+ <string name="list_camera_esc">Esc</string>
+ <string name="list_camera_esc_a">Esc+A</string>
<string name="list_camera_none">Ninguno</string>
<string name="list_delkey_backspace">Borrar</string>
<string name="list_delkey_del">Eliminar</string>
@@ -194,9 +193,7 @@
<string name="terminal_enable_portfoward">Activar redirección de puerto: %1$s</string>
<string name="local_shell_unavailable">¡Fallo! Su intérprete de órdenes local no está disponible en este teléfono.</string>
<string name="notification_text">%1$s requiere de tu atención.</string>
- <string name="upgrade">Nueva versión</string>
- <string name="upgrade_pos">Sí, actualizar</string>
- <string name="upgrade_neg">No en este momento</string>
+ <string name="no">Denegar</string>
<string name="with_confirmation">Con confirmación</string>
<string name="yes">Sí</string>
<string name="exceptions_submit_message">Parece que ConnectBot tuvo un problema en su última ejecución. Enviar reporte de error a los desarrolladores de ConnectBot?</string>
@@ -206,4 +203,6 @@
<string name="color_green">verde</string>
<string name="color_blue">azul</string>
<string name="color_gray">gris</string>
+ <string name="image_description_connected">Conectado</string>
+ <string name="image_description_show_keyboard">Mostrar teclado.</string>
</resources>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index ac3c8ce..0a5ab9b 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -1,26 +1,211 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
- <string name="app_desc">Erraza, boteretsua, sftware askeko SSH bezeroa</string>
+ <string name="app_desc">SSH bezero soil, ahaltsu eta librea</string>
+ <string name="service_desc">SSH konexio eta kargatutako gako publikoak mantentzen ditu</string>
<string name="title_hosts_list">Ostalariak</string>
- <string name="title_pubkey_list">Giltz publikoak</string>
- <string name="title_host_editor">Ostalaria</string>
+ <string name="title_pubkey_list">Gako publikoak</string>
+ <string name="title_port_forwards_list">Ataka birbidalketak</string>
+ <string name="title_host_editor">Editatu ostalaria</string>
<string name="title_help">Laguntza</string>
+ <string name="title_colors">Koloreak</string>
+ <string name="resolve_connect">Konektatu</string>
+ <string name="resolve_entropy">Bildu entropia</string>
<string name="menu_insert">Gehitu ostalaria</string>
<string name="menu_delete">Ezabatu ostalaria</string>
<string name="menu_preferences">Hobespenak</string>
- <string name="help_about">ConectBot-eri buruz</string>
+ <string name="help_intro">Mesedez, hautatu beheko gaietako bat informazio gehiago nahi izanez gero.</string>
+ <string name="help_about">ConnectBot-i buruz</string>
+ <string name="help_keyboard">Teklatua</string>
<string name="pubkey_generate">Sortu</string>
<string name="pubkey_import">Inportatu</string>
- <string name="pubkey_delete">Ezabatu giltza</string>
- <string name="pubkey_generating">Giltz parea sortzen...</string>
- <string name="pubkey_copy_private">Kopiatu giltz pribatua</string>
- <string name="pubkey_copy_public">Kopiatu giltz publikoa</string>
- <string name="pubkey_list_empty">Tap Menu to create\nor import key pairs.</string>
+ <string name="pubkey_delete">Ezabatu gakoa</string>
+ <string name="pubkey_gather_entropy">Entropia biltzen</string>
+ <string name="pubkey_touch_prompt">Ukitu kaxa hau ausazkotasuna lortzeko: %1$d%% eginda</string>
+ <string name="pubkey_touch_hint">Gakoa sortzeko beharrezko den ausazkotasuna lortzeko, mugitu hatza ausaz azpiko kaxaren gainean.</string>
+ <string name="pubkey_generating">Gako parea sortzen...</string>
+ <string name="pubkey_copy_private">Kopiatu gako pribatua</string>
+ <string name="pubkey_copy_public">Kopiatu gako publikoa</string>
+ <string name="pubkey_list_empty">Sakatu \"Menu\" gako pareak\nsortu edo inportatzeko</string>
<string name="pubkey_unknown_format">Formatu ezezaguna</string>
<string name="pubkey_change_password">Aldatu pasahitza</string>
- <string name="pubkey_list_pick">Aukeratu /sdcard-etik</string>
- <string name="portforward_list_empty">Tap Menu to create\nport forwards.</string>
- <string name="list_format_error">Use the format %1$s</string>
+ <string name="pubkey_list_pick">Hautatu /sdcard-etik</string>
+ <string name="pubkey_import_parse_problem">Arazoa inportatutako gako pribatua aztertzean</string>
+ <string name="pubkey_unlock">Desblokeatu gakoa</string>
+ <string name="pubkey_failed_add">Pasahitz okerra \'%1$s\' gakoarentzat. Autentifikazioak huts egin du.</string>
+ <string name="pubkey_memory_load">Kargatu memoriara</string>
+ <string name="pubkey_memory_unload">Deskargatu memoriatik</string>
+ <string name="pubkey_load_on_start">Kargatu gakoa abiaraztean</string>
+ <string name="pubkey_confirm_use">Baieztatu erabili aurretik</string>
+ <string name="portforward_list_empty">Sakatu \"Menua\" ataka\nbirbidalketak sortzeko.</string>
+ <string name="portforward_edit">Editatu ataka birbidalketa</string>
+ <string name="portforward_delete">Ezabatu ataka birbidalketa</string>
+ <string name="prompt_nickname">Goitizena:</string>
+ <string name="prompt_nickname_hint_pubkey">Nire laneko gakoa</string>
+ <string name="prompt_source_port">Iturburuko ataka:</string>
+ <string name="prompt_destination">Helburua:</string>
+ <string name="prompt_old_password">Pasahitz zaharra:</string>
+ <string name="prompt_password">Pasahitza:</string>
+ <string name="prompt_again">(berriro)</string>
+ <string name="prompt_type">Mota:</string>
+ <string name="prompt_password_can_be_blank">Oharra: pasahitza hutsik utz daiteke</string>
+ <string name="prompt_bits">Bitak:</string>
+ <string name="prompt_pubkey_password">\'%1$s\' gakoaren pasahitza</string>
+ <string name="prompt_allow_agent_to_use_key">Urruneko ostalariak \'%1$s\'\ngakoa erabil dezan onartu?</string>
+ <string name="host_verification_failure_warning_header">KONTUZ: URRUNEKO OSTALARIAREN IDENTIFIKAZIOA ALDATU DA!</string>
+ <string name="host_verification_failure_warning">POSIBLE DA NORBAIT ZERBAIT MALTZURRA EGITEN ARITZEA!\nNorbait ezkutuan entzuten aritu daiteke(man-in-the-middle erasoa)!\nBeste aukera ostalariaren gakoa aldatu izana da.</string>
+ <string name="prompt_host_disconnected">Ostalaria deskonektatu egin da.\nSaioa itxi nahi duzu?</string>
+ <string name="prompt_continue_connecting">Ziur zaude konektatzen\njarraitu nahi duzula?</string>
+ <string name="host_authenticity_warning">\'%1$s\' ostalariaren autentifikazioa ezin da ziurtatu.</string>
+ <string name="host_fingerprint">%1$s ostalariaren hatz-marka %2$s da</string>
+ <string name="alert_passwords_do_not_match_msg">Pasahitzak ez datoz bat!</string>
+ <string name="alert_wrong_password_msg">Pasahitz okerra!</string>
+ <string name="alert_key_corrupted_msg">Gako pribatua hondatuta dagoela dirudi!</string>
+ <string name="alert_sdcard_absent">Ez da aurkitu SD txartelik!</string>
+ <string name="button_add">Gehitu</string>
+ <string name="button_change">Aldatu</string>
+ <string name="button_generate">Sortu gakoa</string>
+ <string name="button_resize">Aldatu tamaina</string>
+ <string name="alert_disconnect_msg">Konexioa galdu da</string>
+ <string name="msg_copyright">Copyright-a © 2007-2008 Kenny Root http://the-b.org/, Jeffrey Sharkey http://jsharkey.org/</string>
+ <string name="pref_emulation_category">Terminal emulatzailea</string>
+ <string name="pref_emulation_title">Emulazio modua</string>
+ <string name="pref_emulation_summary">PTY konexioetarako terminal emulazio modua</string>
+ <string name="pref_scrollback_title">Korritze-barraren tamaina</string>
+ <string name="pref_scrollback_summary">Kontsola bakoitzarentzat memorian gordeko den korritze-barraren buffer tamaina.</string>
+ <string name="pref_ui_category">Erabiltzaile-interfazea</string>
+ <string name="pref_rotation_title">Biratze modua</string>
+ <string name="pref_rotation_summary">Nola aldatu biratzea teklatua bistaratzean/izkutatzean.</string>
+ <string name="pref_fullscreen_title">Pantaila osoa</string>
+ <string name="pref_fullscreen_summary">Ezkutatu egoera-barra kontsola erabiltzean</string>
+ <string name="pref_memkeys_title">Gorde gakoak memorian</string>
+ <string name="pref_memkeys_summary">Gogoratu desblokeatutako gakoak memorian zerbitzua amaitu arte</string>
+ <string name="pref_conn_persist_title">Konexio iraunkorrak</string>
+ <string name="pref_conn_persist_summary">Behartu konexioak aktibo mantentzera atzeko planoko daudenean</string>
+ <string name="pref_keymode_title">Direktorien laster-teklak</string>
+ <string name="pref_keymode_summary">Zehaztu nola erabili Alt \'/\'-entzat eta Shift Tab-entzat</string>
+ <string name="pref_camera_title">Kameraren laster-tekla</string>
+ <string name="pref_camera_summary">Zehaztu kameraren botoia sakatzean aktibatu nahi duzun laster-tekla</string>
+ <string name="pref_keepalive_title">Mantendu pantaila aktibo</string>
+ <string name="pref_keepalive_summary">Galarazi pantaila itzaltzea kontsola moduan lanean aritzean</string>
+ <string name="pref_wifilock_title">Mantendu WiFi-a aktibo</string>
+ <string name="pref_wifilock_summary">Galarazi WiFi-a itzaltzea saio aktibo bat dagoen bitartean</string>
+ <string name="pref_bumpyarrows_title">Bibraziodun geziak</string>
+ <string name="pref_bumpyarrows_summary">Bibratu trackball bidez erakusle-geziak bidaltzean; atzerapendun konexioentzat egokia</string>
+ <string name="pref_bell_category">Terminalaren txirrina</string>
+ <string name="pref_bell_title">Txirrin entzungarria</string>
+ <string name="pref_bell_volume_title">Txirrinaren bolumena</string>
+ <string name="pref_bell_vibrate_title">Bibratu txirrinarekin</string>
+ <string name="pref_bell_notification_title">Atzeko planoko jakinerazpenak</string>
+ <string name="pref_bell_notification_summary">Bidali jakinerazpena atzeko planoan dagoen terminal batek txirrina jotzean.</string>
+ <string name="list_keymode_right">Erabili eskuinaldeko teklak</string>
+ <string name="list_keymode_left">Erabili ezkerraldeko teklak</string>
+ <string name="list_keymode_none">Desgaitu</string>
+ <string name="list_pubkeyids_none">Ez erabili gakorik</string>
+ <string name="list_pubkeyids_any">Erabili desblokeatutako edozein gako</string>
+ <string name="hostpref_nickname_title">Goitizena</string>
+ <string name="hostpref_color_title">Kolore kategoria</string>
+ <string name="hostpref_fontsize_title">Letra-tamaina (pt)</string>
+ <string name="hostpref_pubkeyid_title">Erabili gako publiko bidezko autentifikazioa</string>
+ <string name="hostpref_authagent_title">Erabili SSH autentifikazio-eragilea</string>
+ <string name="hostpref_postlogin_title">Saio hasiera ondoko automatizazioa</string>
+ <string name="hostpref_postlogin_summary">Autentifikatu ondoren urruneko zerbitzarian exekutatu beharreko komandoak</string>
+ <string name="hostpref_compression_title">Konpresioa</string>
+ <string name="hostpref_compression_summary">Hau lagungarri izan daiteke konexio mantsoekin</string>
+ <string name="hostpref_wantsession_title">Hasi shell saioa</string>
+ <string name="hostpref_wantsession_summary">Desgaitu aukera hau ataka birbidalketak bakarrik erabiltzeko</string>
+ <string name="hostpref_stayconnected_title">Konektatuta mantendu</string>
+ <string name="hostpref_stayconnected_summary">Saiatu ostalarira birkonektatzen deskonektatzean</string>
+ <string name="hostpref_delkey_title">DEL tekla</string>
+ <string name="hostpref_delkey_summary">DEL tekla sakatzean bidaltzen den tekla-kodea</string>
+ <string name="hostpref_encoding_title">Kodeketa</string>
+ <string name="hostpref_encoding_summary">Ostalariarentzako karaktere kodeketa</string>
+ <string name="hostpref_connection_category">Konexio hobespenak</string>
+ <string name="hostpref_username_title">Erabiltzaile-izena</string>
+ <string name="hostpref_hostname_title">Ostalaria</string>
+ <string name="hostpref_port_title">Ataka</string>
+ <string name="bind_never">Inoiz ez da konexiorik ezarri</string>
+ <string name="console_copy_done">%1$d byte kopiatuta arbelera</string>
+ <string name="console_copy_start">Ukitu eta arrastatu\nedo erabili norabide kuxina\nkopiatu beharreko eremua hautatzeko</string>
+ <string name="console_menu_close">Itxi</string>
+ <string name="console_menu_copy">Kopiatu</string>
+ <string name="console_menu_paste">Itsatsi</string>
+ <string name="console_menu_portforwards">Ataka birbidalketak</string>
+ <string name="console_menu_resize">Behartu tamaina</string>
+ <string name="console_menu_urlscan">URL eskaneatzea</string>
+ <string name="button_yes">Bai</string>
+ <string name="button_no">Ez</string>
+ <string name="portforward_local">Lokala</string>
+ <string name="portforward_remote">Urrunekoa</string>
+ <string name="portforward_dynamic">Dinamikoa (SOCKS)</string>
+ <string name="portforward_pos">Sortu ataka birbidalketa</string>
+ <string name="portforward_done">Ataka birbidalketa ongi sortu da</string>
+ <string name="portforward_problem">Arazoa ataka birbidalketa sortzean, behar bada erabilitako ataka 1024 baino txikiagoa da edo dagoeneko erabiltzen den ataka bat da?</string>
+ <string name="portforward_menu_add">Gehitu ataka birbidalketa</string>
+ <string name="hint_userhost">erabiltzailea\@ostalaria</string>
+ <string name="list_format_error">Erabili %1$s formatua</string>
+ <string name="format_username">erabiltzaile-izena</string>
+ <string name="format_hostname">ostalaria</string>
+ <string name="format_port">ataka</string>
+ <string name="list_menu_pubkeys">Kudeatu gako publikoak</string>
+ <string name="list_menu_sortcolor">Antolatu kolorearen arabera</string>
+ <string name="list_menu_sortname">Antolatu izenaren arabera</string>
+ <string name="list_menu_settings">Ezarpenak</string>
+ <string name="list_host_disconnect">Deskonektatu</string>
+ <string name="list_host_edit">Editatu ostalaria</string>
+ <string name="list_host_portforwards">Editatu ataka birbidalketak</string>
+ <string name="list_host_delete">Ezabatu ostalaria</string>
+ <string name="list_host_empty">Erabili azpiko konexio-azkarreko kaxa\nostalari batetara konektatzeko</string>
+ <string name="list_rotation_default">Lehenetsia</string>
+ <string name="list_rotation_land">Behartu horizontala</string>
+ <string name="list_rotation_port">Behartu bertikala</string>
+ <string name="list_rotation_auto">Automatikoa</string>
+ <string name="list_camera_ctrlaspace">Ktrl+A ondoren zuriunea</string>
+ <string name="list_camera_ctrla">Ktrl+A</string>
+ <string name="list_camera_esc">Esc</string>
+ <string name="list_camera_esc_a">Esc+A</string>
+ <string name="list_camera_none">Ezer ez</string>
+ <string name="list_delkey_backspace">Atzera tekla</string>
+ <string name="list_delkey_del">Ezabatu</string>
+ <string name="delete_message">Ziur zaude \'%1$s\' ezabatu nahi duzula?</string>
+ <string name="delete_pos">Bai, ezabatu</string>
<string name="delete_neg">Utzi</string>
- <string name="exceptions_submit_message">It appears ConnectBot had a problem last time it ran. Submit error report to ConnectBot developers?</string>
+ <string name="wizard_agree">Ados</string>
+ <string name="wizard_next">Hurrengoa</string>
+ <string name="wizard_back">Atzera</string>
+ <string name="terminal_no_hosts_connected">Ez dago ostalaririk konektatuta une honetan</string>
+ <string name="terminal_connecting">%1$s:%2$d-ra konektatzen %3$s bidez</string>
+ <string name="terminal_sucess">Egiaztatutako ostalaria \'%1$s\' gakoa: %2$s</string>
+ <string name="terminal_failed">Ostalariaren gakoaren egiaztapenak huts egin du</string>
+ <string name="terminal_using_s2c_algorithm">Zerbitzari-bezero algoritmoa: %1$s %2$s</string>
+ <string name="terminal_using_c2s_algorithm">Bezero-zerbitzari algoritmoa: %1$s %2$s</string>
+ <string name="terminal_using_algorithm">Uneko algoritmoa: %1$s %2$s</string>
+ <string name="terminal_auth">Autentifikatzen saiatzen</string>
+ <string name="terminal_auth_pass">\'Pasahitz bidez\' autentifikatzen saiatzen</string>
+ <string name="terminal_auth_pass_fail">\'Pasahitz bidezko\' autentifikatzeak huts egin du</string>
+ <string name="terminal_auth_pubkey_any">\'Gako publiko bidezko\' autentifikatze saiakera memoriako gako publikoak erabiliz</string>
+ <string name="terminal_auth_pubkey_invalid">Hautatutako gako publikoa baliogabea da, saiatu berriz hautatzen ostalari editorean</string>
+ <string name="terminal_auth_pubkey_specific">Saiatu \'gako publiko bidez\' autentifikatzen gako publiko zehatz batekin</string>
+ <string name="terminal_auth_pubkey_fail">\'%1$s\' gakoa erabiliz egindako \'gako publiko bidezko\' autenfikazioak huts egin du</string>
+ <string name="terminal_auth_ki">\'Teklatu bidezko autentifikazio elkarreragilea\' saiatzen</string>
+ <string name="terminal_auth_ki_fail">\'Teklatu bidezko autentifikazio elkarreragileak\' huts egin du</string>
+ <string name="terminal_auth_fail">[Zure ostalariak ez du onartzen \'pasahitz bidezko\' edo \'teklatu bidezko autentifikazio elkarreragilerik\'.]</string>
+ <string name="terminal_no_session">Saiorik ez da hasiko ostalariaren hobespenak direla eta.</string>
+ <string name="terminal_enable_portfoward">Gaitu ataka birbidalketa: %1$s</string>
+ <string name="local_shell_unavailable">Errorea! Shell lokala ez dago eskuragarri telefono honetan.</string>
+ <string name="notification_text">%1$s-k zure arreta behar du.</string>
+ <string name="no">Ez</string>
+ <string name="with_confirmation">Berrespenarekin</string>
+ <string name="yes">Bai</string>
+ <string name="exceptions_submit_message">Dirudienez ConnectBot-ek arazo bat izan zuen azken exekuzioan. ConnectBot-en garatzaileei errore-txostena bidali?</string>
+ <string name="menu_colors_reset">Berrezarri</string>
+ <string name="app_is_running">ConnectBot martxan dago</string>
+ <string name="color_red">gorria</string>
+ <string name="color_green">berdea</string>
+ <string name="color_blue">urdina</string>
+ <string name="color_gray">grisa</string>
+ <string name="image_description_connected">Konektatuta.</string>
+ <string name="image_description_key_is_locked">Gakoa blokeatuta dago.</string>
+ <string name="image_description_toggle_control_character">Txandakatu kontrol karakterea.</string>
+ <string name="image_description_send_escape_character">Bidali ihes karakterea.</string>
+ <string name="image_description_show_keyboard">Erakutsi teklatua.</string>
</resources>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 8b4779a..39d2e66 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -1,6 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="app_desc">Yksinkertainen ja tehokas avoimen lähdekoodin SSH-asiakasohjelma.</string>
+ <string name="service_desc">Ylläpitää SSH-yhteydet ja ladatut julkiset avaimet</string>
<string name="title_hosts_list">Palvelimet</string>
<string name="title_pubkey_list">Julkiset avaimet</string>
<string name="title_port_forwards_list">Porttiohjaukset</string>
@@ -30,6 +31,7 @@
<string name="pubkey_list_pick">Hae sijainnista /sdcard</string>
<string name="pubkey_import_parse_problem">Ongelma käsiteltäessä tuotua avainparia</string>
<string name="pubkey_unlock">Poista avaimen lukitus</string>
+ <string name="pubkey_failed_add">Väärä salasana avaimelle \'%1$s\'. Tunnistus epäonnistui.</string>
<string name="pubkey_memory_load">Lataa muistiin</string>
<string name="pubkey_memory_unload">Poista muistista</string>
<string name="pubkey_load_on_start">Lataa avain käynnistyksen yhteydessä</string>
@@ -47,10 +49,13 @@
<string name="prompt_type">Tyyppi:</string>
<string name="prompt_password_can_be_blank">Huomio: salasana voi olla sisältämättä merkkejä</string>
<string name="prompt_bits">Bittejä:</string>
+ <string name="prompt_pubkey_password">Salasana avaimelle \'%1$s\'</string>
+ <string name="prompt_allow_agent_to_use_key">Hyväksy etäyhteys:\nkäytä avainta \'%1$s\'</string>
<string name="host_verification_failure_warning_header">VAROITUS: ETÄPÄÄN KONEEN TUNNISTE ON MUUTTUNUT!</string>
<string name="host_verification_failure_warning">JOKU SAATTAA OLLA TEKEMÄSSÄ PAHOJAAN!\nOn mahdollista, että yhteyttäsi salakuunnellaan (niin kutsuttu man-in-the-middle -hyökkäys)!\nOn toki myös mahdollista, että palvelimen avainta vain on muutettu.</string>
<string name="prompt_host_disconnected">Palvelin katkaisi yhteyden.\nSuljetaanko istunto?</string>
<string name="prompt_continue_connecting">Haluatko varmasti\njatkaa yhdistämistä?</string>
+ <string name="host_authenticity_warning">Palvelimen \'%1$s\' aitoutta ei voida varmentaa</string>
<string name="host_fingerprint">Palvelimen %1$s avaimen sormenjälki: %2$s</string>
<string name="alert_passwords_do_not_match_msg">Salasanat eivät täsmää!</string>
<string name="alert_wrong_password_msg">Väärä salasana!</string>
@@ -74,8 +79,6 @@
<string name="pref_fullscreen_summary">Piilota ilmoitusalue konsolikäytössä</string>
<string name="pref_memkeys_title">Pidä avaimet muistissa</string>
<string name="pref_memkeys_summary">Pitää avatut avaimet muistissa kunnes taustapalvelu suljetaan</string>
- <string name="pref_update_title">Päivitysten tarkistus</string>
- <string name="pref_update_summary">Kuinka usein ConnectBot enintään tarkistaa päivitykset</string>
<string name="pref_conn_persist_title">Ylläpidä yhteys</string>
<string name="pref_conn_persist_summary">Pakottaa yhteyden pysymään aktiivisena ohjelman ollessa taustalla</string>
<string name="pref_keymode_title">Pikanäppäimet</string>
@@ -98,9 +101,6 @@
<string name="list_keymode_none">Pois käytöstä</string>
<string name="list_pubkeyids_none">Älä käytä avaimia</string>
<string name="list_pubkeyids_any">Käytä mitä tahansa avattua avainta</string>
- <string name="list_update_daily">Päivittäin</string>
- <string name="list_update_weekly">Viikoittain</string>
- <string name="list_update_never">Ei koskaan</string>
<string name="hostpref_nickname_title">Lempinimi</string>
<string name="hostpref_color_title">Väri</string>
<string name="hostpref_fontsize_title">Kirjasimen koko (pistettä)</string>
@@ -123,9 +123,6 @@
<string name="hostpref_hostname_title">Palvelin</string>
<string name="hostpref_port_title">Portti</string>
<string name="bind_never">Ei koskaan yhdistetty</string>
- <string name="bind_minutes">%1$s minuuttia sitten</string>
- <string name="bind_hours">%1$s tuntia sitten</string>
- <string name="bind_days">%1$s päivää sitten</string>
<string name="console_copy_done">%1$d tavua kopioitu leikepöydälle</string>
<string name="console_copy_start">Maalaa koskettamalla\ntai käytä nuolinäppäimiä\nvalitaksesi kopioitavan alueen</string>
<string name="console_menu_close">Sulje</string>
@@ -141,6 +138,7 @@
<string name="portforward_dynamic">Dynaaminen (SOCKS)</string>
<string name="portforward_pos">Luo porttiohjaus</string>
<string name="portforward_done">Porttiohjaus luotu onnistuneesti</string>
+ <string name="portforward_problem">Ongelma luotaessa porttiohjausta. Ehkä käytät porttia joka on alle 1024 tai portti on jo käytössä?</string>
<string name="portforward_menu_add">Lisää porttiohjaus</string>
<string name="hint_userhost">tunnus\@palvelin</string>
<string name="list_format_error">Käytä muotoa %1$s</string>
@@ -161,8 +159,13 @@
<string name="list_rotation_port">Pakota pystysuuntaan</string>
<string name="list_rotation_auto">Automaattinen</string>
<string name="list_camera_ctrlaspace">Ctrl+A ja välilyönti</string>
+ <string name="list_camera_ctrla">Ctrl+A</string>
+ <string name="list_camera_esc">Esc</string>
+ <string name="list_camera_esc_a">Esc+A</string>
<string name="list_camera_none">Ei mitään</string>
<string name="list_delkey_backspace">Askelpalautin</string>
+ <string name="list_delkey_del">Delete</string>
+ <string name="delete_message">Oletko varma että haluat poistaa: \'%1$s\'?</string>
<string name="delete_pos">Kyllä, poista</string>
<string name="delete_neg">Peru</string>
<string name="wizard_agree">Hyväksy</string>
@@ -170,19 +173,25 @@
<string name="wizard_back">Edellinen</string>
<string name="terminal_no_hosts_connected">Yhteenkään palvelimeen ei ole yhdistetty</string>
<string name="terminal_connecting">Muodostetaan %3$s-yhteys palvelimeen %1$s:%2$d</string>
+ <string name="terminal_sucess">Varmennettiin palvelin \'%1$s\' avain: %2$s</string>
<string name="terminal_failed">Palvelimen avaimen varmistaminen epäonnistui</string>
<string name="terminal_using_s2c_algorithm">Salausalgoritmi palvelimelta asiakkaalle: %1$s %2$s</string>
<string name="terminal_using_c2s_algorithm">Salausalgoritmi asiakkaalta palvelimelle: %1$s %2$s</string>
<string name="terminal_using_algorithm">Käytetään salausalgoritmia %1$s %2$s</string>
<string name="terminal_auth">Kirjaudutaan</string>
+ <string name="terminal_auth_pass">Yritetään \'salasana\' tunnistusta</string>
+ <string name="terminal_auth_pass_fail">Varmennusmenetelmä \'password\' epäonnistui</string>
+ <string name="terminal_auth_pubkey_any">Yritetään \'publickey\' varmennusta millä tahansa muistissa olevalla julkisella avaimella</string>
<string name="terminal_auth_pubkey_invalid">Valittu julkinen avain ei kelpaa, kokeile valita toinen avain palvelimen asetuksista</string>
+ <string name="terminal_auth_pubkey_specific">Yritetään \'publickey\' varmennusta tietyllä julkisella avaimella</string>
+ <string name="terminal_auth_pubkey_fail">Varmennusmenetelmä \'publickey\' avaimella \'%1$s\' epäonnistui</string>
+ <string name="terminal_auth_ki">Yritetään \'keyboard-interactive\' varmennusta</string>
+ <string name="terminal_auth_ki_fail">Varmennusmenetelmä \'keyboard-interactive\' epäonnistui</string>
+ <string name="terminal_auth_fail">[Palvelin ei tue \'password\' tai \'keyboard-interactive\' varmennusmenetelmiä.]</string>
<string name="terminal_no_session">Istuntoa ei aloiteta palvelimen asetuksista johtuen</string>
<string name="terminal_enable_portfoward">Ota käyttöön porttiohjaus %1$s</string>
<string name="local_shell_unavailable">Virhe! Paikallista komentoriviä ei ole käytettävissä</string>
<string name="notification_text">%1$s kaipaa huomiotasi.</string>
- <string name="upgrade">Uusi versio</string>
- <string name="upgrade_pos">Kyllä, päivitä</string>
- <string name="upgrade_neg">Ei juuri nyt</string>
<string name="no">Ei</string>
<string name="with_confirmation">Kysy varmistus</string>
<string name="yes">Kyllä</string>
@@ -193,4 +202,7 @@
<string name="color_green">vihreä</string>
<string name="color_blue">sininen</string>
<string name="color_gray">harmaa</string>
+ <string name="image_description_connected">Yhdistetty.</string>
+ <string name="image_description_send_escape_character">Lähetä escape.</string>
+ <string name="image_description_show_keyboard">Näytä näppäimistö</string>
</resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 397f7d1..efc4f96 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -1,16 +1,18 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="app_desc">Un client SSH simple, open-source et puissant.</string>
+ <string name="service_desc">Maintient les connexions SSH et les clefs publiques chargées</string>
<string name="title_hosts_list">Serveurs</string>
<string name="title_pubkey_list">Clés publiques</string>
<string name="title_port_forwards_list">Redirections de ports</string>
<string name="title_host_editor">Modifier le serveur</string>
<string name="title_help">Aide</string>
<string name="title_colors">Couleurs</string>
+ <string name="title_color_picker">Choisissez une couleur</string>
<string name="resolve_connect">Connexion</string>
- <string name="resolve_entropy">Génération d\'aléatoire</string>
+ <string name="resolve_entropy">Générer de l\'entropie</string>
<string name="menu_insert">Ajouter un serveur</string>
- <string name="menu_delete">Supprimmer le serveur</string>
+ <string name="menu_delete">Supprimer le serveur</string>
<string name="menu_preferences">Préférences</string>
<string name="help_intro">Sélectionnez un sujet ci-dessous pour plus d\'informations.</string>
<string name="help_about">À propos de ConnectBot</string>
@@ -18,8 +20,8 @@
<string name="pubkey_generate">Générer</string>
<string name="pubkey_import">Importer</string>
<string name="pubkey_delete">Supprimer la clé</string>
- <string name="pubkey_gather_entropy">Génération d\'aléatoire</string>
- <string name="pubkey_touch_prompt">Touchez cette boîte pour récupérer un nombre aléatoire : %1$d%% fait</string>
+ <string name="pubkey_gather_entropy">Génération d\'entropie</string>
+ <string name="pubkey_touch_prompt">Touchez cette boîte pour générer de l\'entropie : %1$d%% effectué</string>
<string name="pubkey_touch_hint">Afin d\'assurer la qualité des nombres aléatoires utilisés pendant la génération de la clé, merci de bouger votre doigt de façon aléatoire sur la boîte ci-dessous.</string>
<string name="pubkey_generating">Génération de la paire de clés en cours...</string>
<string name="pubkey_copy_private">Copier la clé privée</string>
@@ -28,63 +30,71 @@
<string name="pubkey_unknown_format">Format inconnu</string>
<string name="pubkey_change_password">Changer le mot de passe</string>
<string name="pubkey_list_pick">Récupérer depuis /sdcard</string>
- <string name="pubkey_import_parse_problem">Problème lors de l\'import de la clé privée</string>
+ <string name="pubkey_import_parse_problem">Problème lors du parsage de la clé privée importée</string>
<string name="pubkey_unlock">Déverrouiller la clé</string>
- <string name="pubkey_failed_add">Mot de passe incorrect pour la clef \'%1$s\'. Authentification incorrecte.</string>
- <string name="pubkey_memory_load">Chargée en mémoire</string>
- <string name="pubkey_memory_unload">Effacée de la mémoire</string>
+ <string name="pubkey_failed_add">Mot de passe incorrect pour la clé \'%1$s\'. L\'authentification a échoué.</string>
+ <string name="pubkey_memory_load">Charger en mémoire</string>
+ <string name="pubkey_memory_unload">Décharger de la mémoire</string>
<string name="pubkey_load_on_start">Charger la clé au démarrage</string>
<string name="pubkey_confirm_use">Confirmer avant utilisation</string>
<string name="portforward_list_empty">Appuyez sur Menu pour créer\ndes redirections de port.</string>
- <string name="portforward_edit">Editez la redirection de port</string>
- <string name="portforward_delete">Effacez la redirection de port</string>
- <string name="prompt_nickname">Pseudo</string>
+ <string name="portforward_edit">Éditer la redirection de port</string>
+ <string name="portforward_delete">Effacer la redirection de port</string>
+ <string name="prompt_nickname">Pseudonyme :</string>
<string name="prompt_nickname_hint_pubkey">Ma clé de travail</string>
- <string name="prompt_source_port">Port source:</string>
+ <string name="prompt_source_port">Port source :</string>
<string name="prompt_destination">Destination :</string>
- <string name="prompt_old_password">Ancien mot de passe:</string>
- <string name="prompt_password">Mot de passe:</string>
+ <string name="prompt_old_password">Ancien mot de passe :</string>
+ <string name="prompt_password">Mot de passe :</string>
<string name="prompt_again">(encore)</string>
<string name="prompt_type">Type :</string>
- <string name="prompt_password_can_be_blank">Note: le mot de passe peux être vide</string>
+ <string name="prompt_password_can_be_blank">Note : le mot de passe peut être vide</string>
<string name="prompt_bits">Bits :</string>
<string name="prompt_pubkey_password">Mot de passe pour la clé \'%1$s\'</string>
<string name="prompt_allow_agent_to_use_key">Autoriser l\'hôte distant à\nutiliser la clé \'%1$s\' ?</string>
- <string name="host_verification_failure_warning_header">ATTENTION : L\'IDENTIFICATION DE L\'HOTE DISTANT A CHANGEE !</string>
+ <string name="host_verification_failure_warning_header">ATTENTION : L\'IDENTIFICATION DE L\'HÔTE DISTANT A CHANGÉ !</string>
<string name="host_verification_failure_warning">IL EST POSSIBLE QUE QUELQU\'UN FASSE QUELQUE CHOSE DE NUISIBLE !\nQuelqu\'un pourrait vous écouter en ce moment (attaque de l\'homme du milieu) !\nIl est aussi possible que la clé du serveur ait juste changée.</string>
- <string name="prompt_host_disconnected">L\'hôte s\'est deconnecté.\nFermer la session?</string>
- <string name="prompt_continue_connecting">Etes-vous sur de vouloir\ncontinuer à vous connecter ?</string>
- <string name="host_authenticity_warning">L\'autenthicité du serveur \'%1$s\' ne peut être établie.</string>
+ <string name="prompt_host_disconnected">L\'hôte s\'est déconnecté.\nFermer la session ?</string>
+ <string name="prompt_continue_connecting">Êtes-vous sûr de vouloir\ncontinuer à vous connecter ?</string>
+ <string name="host_authenticity_warning">L\'authenticité du serveur \'%1$s\' ne peut être établie</string>
<string name="host_fingerprint">L\'empreinte de la clé du serveur %1$s est %2$s</string>
- <string name="alert_passwords_do_not_match_msg">Les mots de passe diffèrent!</string>
- <string name="alert_wrong_password_msg">Mauvais mot de passe</string>
- <string name="alert_key_corrupted_msg">La clé privée semble corrompue!</string>
+ <string name="alert_passwords_do_not_match_msg">Les mots de passe sont différents !</string>
+ <string name="alert_wrong_password_msg">Mauvais mot de passe !</string>
+ <string name="alert_key_corrupted_msg">La clé privé semble corrompue !</string>
<string name="alert_sdcard_absent">Pas de carte SD dans le lecteur !</string>
<string name="button_add">Ajouter</string>
<string name="button_change">Changer</string>
- <string name="button_generate">Générer clé</string>
- <string name="button_resize">Redimensionner</string>
+ <string name="button_generate">Générer une clé</string>
+ <string name="button_resize">Retailler</string>
<string name="alert_disconnect_msg">Connexion perdue</string>
- <string name="pref_emulation_category">Emulation du mode terminal</string>
- <string name="pref_emulation_title">Mode d\'emulation</string>
- <string name="pref_emulation_summary">Mode d\'emulation du terminal à utiliser pour les connexions PTY</string>
- <string name="pref_scrollback_title">Taille de la mémoire de parcours arrière</string>
- <string name="pref_scrollback_summary">Taille de la mémoire de parcours arrière à garder en mémoire pour chaque console</string>
+ <string name="msg_copyright">Copyright © 2007-2008 Kenny Root http://the-b.org/, Jeffrey Sharkey http://jsharkey.org/</string>
+ <string name="pref_emulation_category">Émulation du mode terminal</string>
+ <string name="pref_emulation_title">Mode d\'émulation</string>
+ <string name="pref_emulation_summary">Mode d\'émulation de terminal à utiliser pour les connexions PTY</string>
+ <string name="pref_scrollback_title">Taille de l\'historique</string>
+ <string name="pref_scrollback_summary">Taille de l\'historique à garder en mémoire pour chaque console</string>
<string name="pref_ui_category">Interface utilisateur</string>
<string name="pref_rotation_title">Mode de rotation</string>
- <string name="pref_rotation_summary">Comment changer l\'orientation de l\'écran quand le clavier est deplié ou non</string>
+ <string name="pref_rotation_summary">Comment changer l\'orientation de l\'écran suivant si le clavier est déplié</string>
+ <string name="pref_titlebarhide_title">Masquer automatiquement la barre de titre</string>
+ <string name="pref_titlebarhide_summary">Appuyez sur console pour montrer la barre de titre et accèder au menu</string>
<string name="pref_fullscreen_title">Plein écran</string>
- <string name="pref_fullscreen_summary">Cacher la barre de status lorsque la console est active</string>
+ <string name="pref_fullscreen_summary">Cacher la barre de notifications lorsque la console est active</string>
+ <string name="pref_keyboard_category">Clavier</string>
+ <string name="pref_shiftfkeys_title">\"Shift+Numéro\" sont les touches de \"F1-F10\"</string>
+ <string name="pref_shiftfkeys_summary">Sur les claviers matériels, les touches des chiffres envoient F1-F10 avec la touche Shift</string>
+ <string name="pref_ctrlfkeys_title">Ctrl+Numéro sont les touches de \"F1-F10\"</string>
+ <string name="pref_ctrlfkeys_summary">Sur les claviers logiciels, les touches des chiffres envoient F1-F10 avec la touche Ctrl</string>
+ <string name="pref_volumefont_title">Les touches volume changent la taille du texte</string>
+ <string name="pref_volumefont_summary">La taille du texte peu aussi être changé dans les paramètres par hôte</string>
<string name="pref_memkeys_title">Garder les clés en mémoire</string>
- <string name="pref_memkeys_summary">Garder les clés débloquées en mémoire tant que le service tourne en arrière plan</string>
- <string name="pref_update_title">Vérifier les mises à jour</string>
- <string name="pref_update_summary">Régler la fréquence de vérification pour les mise à jour de ConnectBot</string>
+ <string name="pref_memkeys_summary">Garder les clés débloquées en mémoire tant que le service tourne en arrière-plan</string>
<string name="pref_conn_persist_title">Connexions persistantes</string>
- <string name="pref_conn_persist_summary">Forcer les connexions à rester connectées en arrière plan</string>
+ <string name="pref_conn_persist_summary">Forcer les connexions à rester connectées en arrière-plan</string>
<string name="pref_keymode_title">Raccourcis de navigation</string>
- <string name="pref_keymode_summary">Définir quel couple de touche Alt et Shift utiliser pour \'/\' et Tab</string>
- <string name="pref_camera_title">Raccourci du bouton Camera</string>
- <string name="pref_camera_summary">Sélectionnez le raccourci à exécuter quand le bouton camera est pressé</string>
+ <string name="pref_keymode_summary">Définir quel couple de touches Alt et Shift utiliser pour \'/\' et Tab</string>
+ <string name="pref_camera_title">Raccourci du bouton Appareil photo</string>
+ <string name="pref_camera_summary">Sélectionnez le raccourci à exécuter quand le bouton caméra est pressé</string>
<string name="pref_keepalive_title">Garder l\'écran allumé</string>
<string name="pref_keepalive_summary">Empêcher la mise en veille de l\'écran pendant une session console</string>
<string name="pref_wifilock_title">Garder le Wi-Fi actif</string>
@@ -95,41 +105,37 @@
<string name="pref_bell_title">Alerte sonore</string>
<string name="pref_bell_volume_title">Volume de l\'alerte</string>
<string name="pref_bell_vibrate_title">Vibrer en cas d\'alerte</string>
- <string name="pref_bell_notification_title">Notification en arrière plan</string>
- <string name="pref_bell_notification_summary">Envoyer une notification lorsqu\'un terminal génère une alerte</string>
- <string name="list_keymode_right">Utiliser les touches du coté droit du clavier</string>
- <string name="list_keymode_left">Utiliser les touches du coté gauche du clavier</string>
- <string name="list_keymode_none">Désactiver</string>
- <string name="list_pubkeyids_none">Ne pas utiliser les clefs de cryptage publiques</string>
+ <string name="pref_bell_notification_title">Notifications en arrière-plan</string>
+ <string name="pref_bell_notification_summary">Envoyer une notification lorsqu\'un terminal en arrière-plan génère une alerte</string>
+ <string name="list_keymode_right">Utiliser les touches du côté droit du clavier</string>
+ <string name="list_keymode_left">Utiliser les touches du côté gauche du clavier</string>
+ <string name="list_keymode_none">Désactivé</string>
+ <string name="list_pubkeyids_none">Ne pas utiliser les clés publiques</string>
<string name="list_pubkeyids_any">Utiliser une clé pour l\'authentification</string>
- <string name="list_update_daily">Journalière</string>
- <string name="list_update_weekly">Hebdomadaire</string>
- <string name="list_update_never">Jamais</string>
<string name="hostpref_nickname_title">Surnom</string>
<string name="hostpref_color_title">Catégorie de couleur</string>
<string name="hostpref_fontsize_title">Taille de police (pt)</string>
- <string name="hostpref_pubkeyid_title">Utiliser l\'autentification par clef de cryptage publique</string>
+ <string name="hostpref_pubkeyid_title">Utiliser l\'authentification par clé publique</string>
<string name="hostpref_authagent_title">Utiliser l\'agent d\'authentification SSH</string>
<string name="hostpref_postlogin_title">Commande à exécuter automatiquement après l\'authentification</string>
- <string name="hostpref_postlogin_summary">Commandes a éffectuer sur le serveur distant après s\'êtres autentifier</string>
- <string name="hostpref_compression_summary">Cela peut aider pour des reseaux lent</string>
+ <string name="hostpref_postlogin_summary">Commandes à lancer sur le serveur distant après s\'être authentifié</string>
+ <string name="hostpref_compression_title">Compression</string>
+ <string name="hostpref_compression_summary">Cela peut aider pour les réseaux lents</string>
<string name="hostpref_wantsession_title">Démarrer une session en ligne de commande</string>
- <string name="hostpref_wantsession_summary">Décochez cette préférence pour n\'utiliser que le mode port forwards</string>
+ <string name="hostpref_wantsession_summary">Décochez cette case pour n\'utiliser que les redirections de port</string>
<string name="hostpref_stayconnected_title">Rester connecté</string>
- <string name="hostpref_stayconnected_summary">Essayer de se reconnecter à l\'hôte après déconnexion</string>
+ <string name="hostpref_stayconnected_summary">Essayer de se reconnecter à l\'hôte si on est déconnecté</string>
<string name="hostpref_delkey_title">Touche SUPPR</string>
<string name="hostpref_delkey_summary">Le code de touche envoyé quand la touche SUPPR est appuyée.</string>
- <string name="hostpref_encoding_title">cryptage</string>
- <string name="hostpref_encoding_summary">codage de charactère a utiliser pour le serveur</string>
- <string name="hostpref_connection_category">Paramètre de connexion</string>
+ <string name="hostpref_encoding_title">Encodage</string>
+ <string name="hostpref_encoding_summary">Jeu de caractères à utiliser pour le serveur</string>
+ <string name="hostpref_connection_category">Paramètres de connexion</string>
<string name="hostpref_username_title">Nom d\'utilisateur</string>
<string name="hostpref_hostname_title">Serveur</string>
- <string name="bind_never">jamais connecté</string>
- <string name="bind_minutes">Il ya %1$s minutes</string>
- <string name="bind_hours">Il y a %1$s heures</string>
- <string name="bind_days">Il y a %1$s jours</string>
- <string name="console_copy_done">%1$d octets copié dans le presse-papier</string>
- <string name="console_copy_start">Toucher et tirer\nou utiliser les touches directionelles\npour selectioner la region à copier</string>
+ <string name="hostpref_port_title">Port</string>
+ <string name="bind_never">Jamais connecté</string>
+ <string name="console_copy_done">%1$d octets copiés dans le presse-papier</string>
+ <string name="console_copy_start">Touchez et tirez, ou utilisez\nles touches directionnelles\npour sélectionner la région\nà copier</string>
<string name="console_menu_close">Fermer</string>
<string name="console_menu_copy">Copier</string>
<string name="console_menu_paste">Coller</string>
@@ -138,34 +144,37 @@
<string name="console_menu_urlscan">Lister les URL</string>
<string name="button_yes">Oui</string>
<string name="button_no">Non</string>
+ <string name="portforward_local">Local</string>
<string name="portforward_remote">Distant</string>
<string name="portforward_dynamic">Dynamique (SOCKS)</string>
<string name="portforward_pos">Créer la redirection de port</string>
<string name="portforward_done">Redirection de port créée avec succès</string>
- <string name="portforward_problem">Problème de création de la redirection de port, peut-être utilisez vous un port sous le 1024 ou un déjà utilisé ?</string>
+ <string name="portforward_problem">Problème de création de la redirection de port, peut-être utilisez-vous un port sous le 1024 ou un déjà utilisé ?</string>
<string name="portforward_menu_add">Ajouter une redirection de port</string>
<string name="hint_userhost">utilisateur\@serveur</string>
- <string name="list_format_error">Utilisez le format %1$s</string>
+ <string name="list_format_error">Utilisez le format \"%1$s\"</string>
<string name="format_username">utilisateur</string>
- <string name="format_hostname">serveur</string>
+ <string name="format_hostname">nom d\'hôte</string>
+ <string name="format_port">port</string>
<string name="list_menu_pubkeys">Gérer les clés publiques</string>
<string name="list_menu_sortcolor">Trier par couleur</string>
<string name="list_menu_sortname">Trier par nom</string>
<string name="list_menu_settings">Réglages</string>
<string name="list_host_disconnect">Déconnexion</string>
- <string name="list_host_edit">Editer le serveur</string>
- <string name="list_host_portforwards">Editer les redirections de port</string>
+ <string name="list_host_edit">Éditer le serveur</string>
+ <string name="list_host_portforwards">Éditer les redirections de port</string>
<string name="list_host_delete">Effacer le serveur</string>
- <string name="list_host_empty">Utilisez la boite de connection rapide en bas pour vous connecter a un serveur</string>
+ <string name="list_host_empty">Utilisez la boîte de connexion rapide en bas\npour vous connecter à un serveur</string>
<string name="list_rotation_default">Par défaut</string>
<string name="list_rotation_land">Forcer le mode paysage</string>
<string name="list_rotation_port">Forcer le mode portrait</string>
<string name="list_rotation_auto">Automatique</string>
<string name="list_camera_ctrlaspace">Ctrl+A puis Espace</string>
- <string name="list_camera_esc">Echap</string>
+ <string name="list_camera_ctrla">Ctrl+A</string>
+ <string name="list_camera_esc">Échap</string>
<string name="list_camera_esc_a">Echap+A</string>
- <string name="list_camera_none">aucun</string>
- <string name="list_delkey_backspace">Retour arriére</string>
+ <string name="list_camera_none">Aucun</string>
+ <string name="list_delkey_backspace">Retour arrière</string>
<string name="list_delkey_del">Supprimer</string>
<string name="delete_message">Êtes-vous sûr de vouloir supprimer \'%1$s\' ?</string>
<string name="delete_pos">Oui, supprimer</string>
@@ -179,32 +188,36 @@
<string name="terminal_failed">Authentification par clé échouée</string>
<string name="terminal_using_s2c_algorithm">Algorithme serveur vers client : %1$s %2$s</string>
<string name="terminal_using_c2s_algorithm">Algorithme client vers serveur : %1$s %2$s</string>
- <string name="terminal_using_algorithm">Utilisation de l\'algorithme de cryptage : %1$s %2$s</string>
+ <string name="terminal_using_algorithm">Utilisation de l\'algorithme de chiffrement : %1$s %2$s</string>
<string name="terminal_auth">Tentative d\'authentification</string>
<string name="terminal_auth_pass">Tentative d\'authentification par mot de passe</string>
<string name="terminal_auth_pass_fail">L\'authentification par mot de passe a échoué</string>
<string name="terminal_auth_pubkey_any">Tentative d\'authentification avec une clé publique en mémoire</string>
<string name="terminal_auth_pubkey_invalid">La clé publique sélectionnée est invalide, essayez de sélectionner une clé dans les préférences de l\'hôte</string>
<string name="terminal_auth_pubkey_specific">Tentative d\'authentification par clé publique avec une clé publique spécifique</string>
- <string name="terminal_auth_pubkey_fail">La méthode d\'authentification par clé publique avec la clé \'%1$s\' a échouée.</string>
+ <string name="terminal_auth_pubkey_fail">La méthode d\'authentification par clé publique avec la clé \'%1$s\' a échoué.</string>
<string name="terminal_auth_ki">Tentative d\'authentification par clavier</string>
- <string name="terminal_auth_ki_fail">La méthode d\'authentification par clavier a échouée</string>
+ <string name="terminal_auth_ki_fail">La méthode d\'authentification par clavier a échoué</string>
<string name="terminal_auth_fail">[L\'hôte ne supporte pas l\'authentification par mot de passe ou par clavier.]</string>
<string name="terminal_no_session">La session ne sera pas démarrée à cause des préférences de l\'hôte.</string>
<string name="terminal_enable_portfoward">Activer la redirection de port : %1$s</string>
- <string name="local_shell_unavailable">Echec ! Le shell local est indisponible sur ce téléphone.</string>
+ <string name="local_shell_unavailable">Échec ! Le shell local est indisponible pour ce téléphone.</string>
<string name="notification_text">%1$s requiert votre attention.</string>
- <string name="upgrade">Nouvelle version</string>
- <string name="upgrade_pos">Oui, mettre à jour</string>
- <string name="upgrade_neg">Pas maintenant</string>
<string name="no">Non</string>
<string name="with_confirmation">Après confirmation</string>
<string name="yes">Oui</string>
- <string name="exceptions_submit_message">Il semblerait que ConnectBot ait eu un problème lors de sa dernière utilisation. Voulez vous envoyer un rapport d\'erreur aux développeurs de ConnectBot ?</string>
+ <string name="exceptions_submit_message">Il semblerait que ConnectBot ait eu un problème lors de sa dernière utilisation. Voulez-vous envoyer un rapport d\'erreur aux développeurs de ConnectBot ?</string>
<string name="menu_colors_reset">Réinitialiser</string>
<string name="app_is_running">ConnectBot est en cours</string>
<string name="color_red">rouge</string>
<string name="color_green">vert</string>
<string name="color_blue">bleu</string>
<string name="color_gray">gris</string>
+ <string name="colors_fg_label">PP: %1$d</string>
+ <string name="color_bg_label">AP: %1$d</string>
+ <string name="image_description_connected">Connecté</string>
+ <string name="image_description_key_is_locked">Clef verrouillée</string>
+ <string name="image_description_toggle_control_character">Activer/désactiver le caractère \"contrôle\"</string>
+ <string name="image_description_send_escape_character">Envoyer \"échap\"</string>
+ <string name="image_description_show_keyboard">Afficher le clavier.</string>
</resources>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 7563050..75b4769 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -72,8 +72,6 @@
<string name="pref_fullscreen_summary">Esconder a barra de estado cando estea na consola</string>
<string name="pref_memkeys_title">Recordar chaves en memoria</string>
<string name="pref_memkeys_summary">Manter chaves desbloqueadas en memoria ata que o servicio de backend teña terminado</string>
- <string name="pref_update_title">Comprobar actualizacións</string>
- <string name="pref_update_summary">Establecer a frecuencia máxima para comprobar as actualizacións de ConnectBot</string>
<string name="pref_conn_persist_title">Conexións persistentes</string>
<string name="pref_conn_persist_summary">Manter conexións activas en segundo plano</string>
<string name="pref_keymode_title">Atallos de teclado para directorios</string>
@@ -91,9 +89,6 @@
<string name="list_keymode_none">Deshabilitar</string>
<string name="list_pubkeyids_none">Non usar chaves</string>
<string name="list_pubkeyids_any">Usar calquera chave desbloqueada</string>
- <string name="list_update_daily">Diariamente</string>
- <string name="list_update_weekly">Semanalmente</string>
- <string name="list_update_never">Nunca</string>
<string name="hostpref_fontsize_title">Tamaño da fonte (pt)</string>
<string name="hostpref_postlogin_summary">Comandos a executar no servidor remoto unha vez autenticado</string>
<string name="hostpref_compression_title">Compresión</string>
@@ -106,9 +101,6 @@
<string name="hostpref_hostname_title">Máquina</string>
<string name="hostpref_port_title">Porto</string>
<string name="bind_never">Nunca conectado</string>
- <string name="bind_minutes">Hai %1$s minutos</string>
- <string name="bind_hours">Hai %1$s horas</string>
- <string name="bind_days">Hai %1$s días</string>
<string name="console_copy_done">Copiados %1$d bytes ao portapapeis</string>
<string name="console_copy_start">Tocar e arrastrar\nou usar o cursor direccional\npara seleccionar a área a copiar</string>
<string name="console_menu_close">Pechar</string>
diff --git a/res/values-he/strings.xml b/res/values-he/strings.xml
new file mode 100644
index 0000000..429146e
--- /dev/null
+++ b/res/values-he/strings.xml
@@ -0,0 +1,214 @@
+<?xml version='1.0' encoding='utf-8'?>
+<resources>
+ <string name="app_desc">לקוח SSH פשוט, עצמתי ובקוד פתוח.</string>
+ <string name="service_desc">מתפעל חיבורי SSH ומפתחות ציבוריים שנטענים</string>
+ <string name="title_hosts_list">מארחים</string>
+ <string name="title_pubkey_list">מפתחות ציבוריים</string>
+ <string name="title_port_forwards_list">הפניית פתחות</string>
+ <string name="title_host_editor">עריכת מארח</string>
+ <string name="title_help">עזרה</string>
+ <string name="title_colors">צבעים</string>
+ <string name="title_color_picker">בחירת צבע</string>
+ <string name="resolve_connect">התחברות</string>
+ <string name="resolve_entropy">נאסף גיבוב</string>
+ <string name="menu_insert">הוספת מארח</string>
+ <string name="menu_delete">מחיקת מארח</string>
+ <string name="menu_preferences">העדפות</string>
+ <string name="help_intro">נא לבחור נושא להלן לקבל פרטים נוספים בנוגע לנושא מסוים.</string>
+ <string name="help_about">על אודות ConnectBot</string>
+ <string name="help_keyboard">מקלדת</string>
+ <string name="pubkey_generate">יצירה</string>
+ <string name="pubkey_import">יבוא</string>
+ <string name="pubkey_delete">מחיקת מפתח</string>
+ <string name="pubkey_gather_entropy">נאסף גיבוב</string>
+ <string name="pubkey_touch_prompt">יש לגעת בתיבה זו כדי לאסוף נתונים אקראיים: %1$d%% הושלמו</string>
+ <string name="pubkey_touch_hint">כדי להבטיח את האקראיות במהלך יצירת המפתח, עליך להעביר את האצבע באקראיות על התיבה שלהלן.</string>
+ <string name="pubkey_generating">צמד המפתחות נוצר...</string>
+ <string name="pubkey_copy_private">העתקת המפתח הפרטי</string>
+ <string name="pubkey_copy_public">העתקת המפתח הציבורי</string>
+ <string name="pubkey_list_empty">יש לגעת ב„תפריט“ כדי ליצור\nאו לייבא צמדי מפתחות.</string>
+ <string name="pubkey_unknown_format">מבנה בלתי ידוע</string>
+ <string name="pubkey_change_password">החלפת הססמה</string>
+ <string name="pubkey_list_pick">בחירה מ־/sdcard</string>
+ <string name="pubkey_import_parse_problem">אירעה תקלה בעת ניתוח המפתח הפרטי שייובא</string>
+ <string name="pubkey_unlock">שחרור המפתח</string>
+ <string name="pubkey_failed_add">הססמה עבור המפתח \'%1$s\' שגויה. האימות נכשל.</string>
+ <string name="pubkey_memory_load">טעינה לזיכרון</string>
+ <string name="pubkey_memory_unload">פריקה מהזיכרון</string>
+ <string name="pubkey_load_on_start">טעינת המפתח עם ההפעלה</string>
+ <string name="pubkey_confirm_use">לאמת לפני השימוש</string>
+ <string name="portforward_list_empty">יש לגעת ב„תפריט“ כדי ליצור\nהפניית פתחות.</string>
+ <string name="portforward_edit">עריכת הפניית הפתחות</string>
+ <string name="portforward_delete">מחיקת הפניית הפתחות</string>
+ <string name="prompt_nickname">כינוי:</string>
+ <string name="prompt_nickname_hint_pubkey">המפתח בעבודה שלי</string>
+ <string name="prompt_source_port">פתחת המקור:</string>
+ <string name="prompt_destination">יעד:</string>
+ <string name="prompt_old_password">ססמה ישנה:</string>
+ <string name="prompt_password">ססמה:</string>
+ <string name="prompt_again">(שוב)</string>
+ <string name="prompt_type">סוג:</string>
+ <string name="prompt_password_can_be_blank">הערה: הססמה יכולה להישאר ריקה</string>
+ <string name="prompt_bits">סיביות:</string>
+ <string name="prompt_pubkey_password">ססמה עבור המפתח \'%1$s\'</string>
+ <string name="prompt_allow_agent_to_use_key">האם לאפשר למארח המרוחק\nלהשתמש במפתח \'%1$s\'?</string>
+ <string name="host_verification_failure_warning_header">אזהרה: זיהוי המארח המרוחק השתנה!</string>
+ <string name="host_verification_failure_warning">יתכן שמישהו עושה טריק מלוכלך!\nיכול להיות שמישהו מנסה לצוטט לך ברגעים אלה (מתקפת סוכן אמצע)!\nיכול גם להיות שמפתח המארח הוחלף.</string>
+ <string name="prompt_host_disconnected">המארח התנתק.\nלסגור את החלון?</string>
+ <string name="prompt_continue_connecting">האם אכן\nלהמשיך בהליך החיבור?</string>
+ <string name="host_authenticity_warning">לא ניתן לאמת את אותנטיות המארח\'%1$s\'.</string>
+ <string name="host_fingerprint">טביעת האצבע של המפתח שלהמארח %1$s היא %2$s</string>
+ <string name="alert_passwords_do_not_match_msg">הססמאות אינן תואמות!</string>
+ <string name="alert_wrong_password_msg">ססמה שגויה!</string>
+ <string name="alert_key_corrupted_msg">המפתח הפרטי נראה פגום!</string>
+ <string name="alert_sdcard_absent">לא הוכנס כרטיס SD!</string>
+ <string name="button_add">הוספה</string>
+ <string name="button_change">החלפה</string>
+ <string name="button_generate">יצירת מפתח</string>
+ <string name="button_resize">שינוי גודל</string>
+ <string name="alert_disconnect_msg">החיבור אבד</string>
+ <string name="msg_copyright">כל הזכויות שמורות © 2007‎-2008 ל־Kenny Root http://the-b.org/, Jeffrey Sharkey http://jsharkey.org/</string>
+ <string name="pref_emulation_category">הדמיית מסוף</string>
+ <string name="pref_emulation_title">מצב הדמיה</string>
+ <string name="pref_emulation_summary">מצב הדמיית מסוף לשימוש בחיבורי PTY</string>
+ <string name="pref_scrollback_title">גודל גלילה אחורה</string>
+ <string name="pref_scrollback_summary">גודל מטמון הגלילה שיישאר בזיכרון בכל מסוף</string>
+ <string name="pref_ui_category">מנשק משתמש</string>
+ <string name="pref_rotation_title">מצב סיבוב</string>
+ <string name="pref_rotation_summary">כיצד לשנות את הסיבוב כאשר המקלדת עולה/יורדת</string>
+ <string name="pref_fullscreen_title">מסך מלא</string>
+ <string name="pref_fullscreen_summary">להסתיר את שורת המצב בעת השימוש במסוף</string>
+ <string name="pref_memkeys_title">לשמור את המפתחות בזיכרון</string>
+ <string name="pref_memkeys_summary">להשאיר את המפתחות לאחר השחרור בזיכרון עד ששירות המנגנון מושבת</string>
+ <string name="pref_conn_persist_title">חיבורים עיקשים</string>
+ <string name="pref_conn_persist_summary">לשמור על החיבורים פעילים ברקע</string>
+ <string name="pref_keymode_title">קיצורי ספריות</string>
+ <string name="pref_keymode_summary">ניתן לבחור כיצד להשתמש ב־Alt לטובת \'/\' וב־Shift לטובת Tab</string>
+ <string name="pref_camera_title">קיצור דרך מצלמה</string>
+ <string name="pref_camera_summary">ניתן לבחור איזה קיצור דרך יופעל בעת לחיצה על לחצן המצלמה</string>
+ <string name="pref_keepalive_title">להשאיר את המסך פעיל</string>
+ <string name="pref_keepalive_summary">למנוע את כיבוי המסך בעת העבודה עם המסוף</string>
+ <string name="pref_wifilock_title">להשאיר את הרשת האלחוטית פעילה</string>
+ <string name="pref_wifilock_summary">למנוע את כיבוי מתאם הרשת האלחוטית כאשר ישנו חלון פעיל</string>
+ <string name="pref_bumpyarrows_title">חצים עם רטט</string>
+ <string name="pref_bumpyarrows_summary">המכשיר ירטוט בעת שליחת מקשי חצים מכדור הגלילה; שימוש לחיבורים אטיים</string>
+ <string name="pref_bell_category">פעמון מסוף</string>
+ <string name="pref_bell_title">פעמון עם צליל</string>
+ <string name="pref_bell_volume_title">עצמת הפעמון</string>
+ <string name="pref_bell_vibrate_title">רטט עם הפעמון</string>
+ <string name="pref_bell_notification_title">התרעות ברקע</string>
+ <string name="pref_bell_notification_summary">שליחת התרעה כאשר מסוף שפעיל ברקע מצלצל בפעמון.</string>
+ <string name="list_keymode_right">שימוש במקשים מימין</string>
+ <string name="list_keymode_left">שימוש במקשים משמאל</string>
+ <string name="list_keymode_none">נטרול</string>
+ <string name="list_pubkeyids_none">לא להשתמש במפתחות</string>
+ <string name="list_pubkeyids_any">שימוש במפתח כלשהו שאינו נעול</string>
+ <string name="hostpref_nickname_title">כינוי</string>
+ <string name="hostpref_color_title">קטגוריית הצבע</string>
+ <string name="hostpref_fontsize_title">גודל הגופן (נק׳)</string>
+ <string name="hostpref_pubkeyid_title">שימוש באימות מפתח ציבורי</string>
+ <string name="hostpref_authagent_title">שימוש בסוכן אימות SSH</string>
+ <string name="hostpref_postlogin_title">אוטומציה שלאחר הכניסה</string>
+ <string name="hostpref_postlogin_summary">פקודות להפעלה בשרת המרוחק לאחר האימות</string>
+ <string name="hostpref_compression_title">דחיסה</string>
+ <string name="hostpref_compression_summary">אפשרות זו עשויה להועיל במקרה של רשתות אטיות</string>
+ <string name="hostpref_wantsession_title">הפעלת חלון מעטפת</string>
+ <string name="hostpref_wantsession_summary">ניתן לנטרל אפשרות זו כדי להשתמש בהפניית פתחות בלבד</string>
+ <string name="hostpref_stayconnected_title">להשאיר את החיבור פעיל</string>
+ <string name="hostpref_stayconnected_summary">לנסות להתחבר שוב למארח לאחר ניתוק</string>
+ <string name="hostpref_delkey_title">מקש DEL</string>
+ <string name="hostpref_delkey_summary">קוד המקש בעת לחיצה על מקש ה־DEL</string>
+ <string name="hostpref_encoding_title">קידוד</string>
+ <string name="hostpref_encoding_summary">קידוד תווים בצד המארח</string>
+ <string name="hostpref_connection_category">הגדרות חיבור</string>
+ <string name="hostpref_username_title">שם משתמש</string>
+ <string name="hostpref_hostname_title">מארח</string>
+ <string name="hostpref_port_title">פתחה</string>
+ <string name="bind_never">לא חובר מעולם</string>
+ <string name="console_copy_done">הועתקו %1$d בתים ללוח הגזירים</string>
+ <string name="console_copy_start">יש לגעת ולגרור\nאו להשתמש בלוח כיוונים\nכדי לבחור אזור להעתקה</string>
+ <string name="console_menu_close">סגירה</string>
+ <string name="console_menu_copy">העתקה</string>
+ <string name="console_menu_paste">הדבקה</string>
+ <string name="console_menu_portforwards">הפניית פתחות</string>
+ <string name="console_menu_resize">אילוץ גודל</string>
+ <string name="console_menu_urlscan">סריקת כתובות</string>
+ <string name="button_yes">כן</string>
+ <string name="button_no">לא</string>
+ <string name="portforward_local">מקומי</string>
+ <string name="portforward_remote">מרוחק</string>
+ <string name="portforward_dynamic">דינמי (SOCKS)</string>
+ <string name="portforward_pos">יצירת הפניית פתחות</string>
+ <string name="portforward_done">הפניית הפתחות נוצרה בהצלחה</string>
+ <string name="portforward_problem">אירעה שגיאה ביצירת הפניית פתחות, יתכן שניסית להשתמש בפתחות מתחת ל־1024 או שהפתחה כבר בשימוש?</string>
+ <string name="portforward_menu_add">הוספת הפניית פתחות</string>
+ <string name="hint_userhost">user\@hostname</string>
+ <string name="list_format_error">יש להשתמש בצורה „%1$s“</string>
+ <string name="format_username">username</string>
+ <string name="format_hostname">hostname</string>
+ <string name="format_port">port</string>
+ <string name="list_menu_pubkeys">ניהול מפתחות ציבוריים</string>
+ <string name="list_menu_sortcolor">סידור לפי צבע</string>
+ <string name="list_menu_sortname">סידור לפי שם</string>
+ <string name="list_menu_settings">הגדרות</string>
+ <string name="list_host_disconnect">ניתוק</string>
+ <string name="list_host_edit">עריכת מארח</string>
+ <string name="list_host_portforwards">עריכת הפניית פתחות</string>
+ <string name="list_host_delete">מחיקת מארח</string>
+ <string name="list_host_empty">יש להשתמש בתיבת ההתחברות המהירה\nשלהלן כדי להתחבר למארח.</string>
+ <string name="list_rotation_default">בררת מחדל</string>
+ <string name="list_rotation_land">אילוץ מצב אופקי</string>
+ <string name="list_rotation_port">אילוץ מצב אנכי</string>
+ <string name="list_rotation_auto">אוטומטי</string>
+ <string name="list_camera_ctrlaspace">Ctrl+A ואז רווח</string>
+ <string name="list_camera_ctrla">Ctrl+A</string>
+ <string name="list_camera_esc">Esc</string>
+ <string name="list_camera_esc_a">Esc+A</string>
+ <string name="list_camera_none">ללא</string>
+ <string name="list_delkey_backspace">Backspace</string>
+ <string name="list_delkey_del">Delete</string>
+ <string name="delete_message">האם אכן למחוק את \'%1$s\'?</string>
+ <string name="delete_pos">כן, למחוק</string>
+ <string name="delete_neg">ביטול</string>
+ <string name="wizard_agree">מוסכם</string>
+ <string name="wizard_next">הבא</string>
+ <string name="wizard_back">הקודם</string>
+ <string name="terminal_no_hosts_connected">אין מארחים מחוברים כרגע</string>
+ <string name="terminal_connecting">מתבצעת התחברות אל %1$s:%2$d דרך %3$s</string>
+ <string name="terminal_sucess">מפתח של המארח המאומת \'%1$s\'‏: %2$s</string>
+ <string name="terminal_failed">אימות מפתח המארח נכשל.</string>
+ <string name="terminal_using_s2c_algorithm">אלגוריתם שרת ללקוח: %1$s %2$s</string>
+ <string name="terminal_using_c2s_algorithm">אלגוריתם לקוח לשרת: %1$s %2$s</string>
+ <string name="terminal_using_algorithm">אלגוריתם בשימוש: %1$s %2$s</string>
+ <string name="terminal_auth">מתבצע ניסיון אימות</string>
+ <string name="terminal_auth_pass">מתבצע ניסיון לאימות \'ססמה\'</string>
+ <string name="terminal_auth_pass_fail">שיטת האימות \'ססמה\' נכשלה</string>
+ <string name="terminal_auth_pubkey_any">מתבצע ניסיון לאימות \'מפתח ציבורי\' עם מפתחות ציבוריים כלשהם מהזיכרון</string>
+ <string name="terminal_auth_pubkey_invalid">המפתח הציבורי שנבחר שגוי, כדאי לנסות לבחור מחדש מפתח בעורך המארחים</string>
+ <string name="terminal_auth_pubkey_specific">מתבצע ניסיון לאימות \'מפתח ציבורי\' עם מפתח ציבורי מסוים</string>
+ <string name="terminal_auth_pubkey_fail">שיטת האימות \'מפתח ציבורי\' עם המפתח \'%1$s\' נכשלה</string>
+ <string name="terminal_auth_ki">מתבצע ניסיון לאימות \'פעילות-מקלדת\'</string>
+ <string name="terminal_auth_ki_fail">שיטת האימות \'פעילות-מקלדת\' נכשלה</string>
+ <string name="terminal_auth_fail">[המארח שלך אינו תומך באימות \'ססמה\' או \'פעילות-מקלדת\'.]</string>
+ <string name="terminal_no_session">ההפעלה לא תתחיל עקב העדפות המארח.</string>
+ <string name="terminal_enable_portfoward">הפעלת הפניית פתחות: %1$s</string>
+ <string name="local_shell_unavailable">תקלה! המעטפת המקומית אינה זמינה במכשיר זה.</string>
+ <string name="notification_text">ל־%1$s נדרשת תשומת הלב שלך.</string>
+ <string name="no">לא</string>
+ <string name="with_confirmation">עם אימות</string>
+ <string name="yes">כן</string>
+ <string name="exceptions_submit_message">מתברר כי חלה תקלה ב־ConnectBot במהלך ההפעלה האחרונה. האם לשלוח את הדיווח למפתחים של ConnectBot?</string>
+ <string name="menu_colors_reset">איפוס</string>
+ <string name="app_is_running">ConnectBot פעיל</string>
+ <string name="color_red">אדום</string>
+ <string name="color_green">ירוק</string>
+ <string name="color_blue">כחול</string>
+ <string name="color_gray">אפור</string>
+ <string name="colors_fg_label">קדמה:%1$d</string>
+ <string name="color_bg_label">רקע:%1$d</string>
+ <string name="image_description_connected">יש חיבור.</string>
+ <string name="image_description_key_is_locked">המפתח נעול.</string>
+ <string name="image_description_toggle_control_character">החלפת מצב תווי בקרה.</string>
+ <string name="image_description_send_escape_character">שליחת תו סליקה.</string>
+ <string name="image_description_show_keyboard">הצגת מקלדת.</string>
+</resources>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
new file mode 100644
index 0000000..269a58b
--- /dev/null
+++ b/res/values-hr/strings.xml
@@ -0,0 +1,92 @@
+<?xml version='1.0' encoding='utf-8'?>
+<resources>
+ <string name="app_desc">Jednostavan, moćan, open.source SSH klijent.</string>
+ <string name="service_desc">Pamti SSH konekcije i učitane javne ključeve</string>
+ <string name="title_hosts_list">Računala</string>
+ <string name="title_pubkey_list">Javni ključevi</string>
+ <string name="title_port_forwards_list">Port forwards</string>
+ <string name="title_host_editor">Uredi Računalo</string>
+ <string name="title_help">Pomoć</string>
+ <string name="title_colors">Boje</string>
+ <string name="resolve_connect">Spoji se</string>
+ <string name="resolve_entropy">Očitaj Entropiju</string>
+ <string name="menu_insert">Dodaj računalo</string>
+ <string name="menu_delete">Obriši računalo</string>
+ <string name="menu_preferences">Postavke</string>
+ <string name="help_intro">Molim Vas za više informacija odaberite temu ispod.</string>
+ <string name="help_about">O ConnectBot-u</string>
+ <string name="help_keyboard">Tipkovnica</string>
+ <string name="pubkey_generate">Stvori</string>
+ <string name="pubkey_import">Uvoz</string>
+ <string name="pubkey_delete">Obriši ključ</string>
+ <string name="pubkey_gather_entropy">Sakupljanje Entropije</string>
+ <string name="pubkey_touch_prompt">Dotakni ovaj prozor za prikupljanje slučajnosti: %1$d%% gotovo</string>
+ <string name="pubkey_touch_hint">Da bi osigurali slučajnost, pomičite prst u različitim smjerovima unutar prozora dolje.</string>
+ <string name="pubkey_generating">Izrada para ključeva</string>
+ <string name="pubkey_copy_private">Kopiraj privatni ključ</string>
+ <string name="pubkey_copy_public">Kopiraj javni ključ</string>
+ <string name="pubkey_list_empty">Dotakni \"Meni\" za izradu\nili uvoz parova ključeva</string>
+ <string name="pubkey_unknown_format">Nepoznati format</string>
+ <string name="pubkey_change_password">Promijeni zaporku</string>
+ <string name="pubkey_list_pick">Odaberi iz /sdcard</string>
+ <string name="pubkey_import_parse_problem">Problem prilikom tumačenja uvezenog privatnog ključa</string>
+ <string name="pubkey_unlock">Otključaj ključ</string>
+ <string name="pubkey_failed_add">Pogrešna zaporka za ključ \'%1$s\'. Neuspješna autentikacija.</string>
+ <string name="pubkey_memory_load">Učitaj u memoriju</string>
+ <string name="pubkey_memory_unload">Isprazni iz memorije</string>
+ <string name="pubkey_load_on_start">Učitaj kluč na početku</string>
+ <string name="pubkey_confirm_use">Potvrda prije upotrebe</string>
+ <string name="portforward_list_empty">Dotakni \"Meni\" za izradu\nport forward</string>
+ <string name="portforward_edit">Uredi port forward</string>
+ <string name="portforward_delete">Obriši port forward</string>
+ <string name="prompt_nickname">Nadimak:</string>
+ <string name="prompt_nickname_hint_pubkey">Moj poslovni ključ</string>
+ <string name="prompt_source_port">Source port:</string>
+ <string name="prompt_destination">Destination:</string>
+ <string name="prompt_old_password">Stara zaporka:</string>
+ <string name="prompt_password">Zaporka</string>
+ <string name="prompt_again">(ponovo)</string>
+ <string name="prompt_type">Type:</string>
+ <string name="prompt_password_can_be_blank">Uputa: zaporka može biti prazna</string>
+ <string name="prompt_bits">Bits:</string>
+ <string name="prompt_pubkey_password">Zaporka za ključ \'%1$s\'</string>
+ <string name="prompt_allow_agent_to_use_key">Dopusti udaljenom računalu da\nkoristi ključ \'%1$s\'?</string>
+ <string name="host_verification_failure_warning_header">UPOZORENJE: IDENTIFIKACIJA UDALJENOG RAČUNALA SE PROMIJENILA!</string>
+ <string name="host_verification_failure_warning">MOGUĆE JE DA NETKO RADI NEŠTO JAKO OPASNO!\nNetko vas možda prisluškuje upravo sada (man-in-the-middle attack!)\nTakođer je moguće da se ključ računala upravo promijenio.</string>
+ <string name="prompt_host_disconnected">Računalo se odspojilo.\nZatvori sesiju?</string>
+ <string name="prompt_continue_connecting">Jeste li sigurni da želite\nnastaviti sa spajanjem?</string>
+ <string name="host_authenticity_warning">Autentičnost računala \'%1$s\' se ne može provjeriti.</string>
+ <string name="host_fingerprint">%1$s ključ računala je %2$s</string>
+ <string name="alert_passwords_do_not_match_msg">Zaporke nisu iste!</string>
+ <string name="alert_wrong_password_msg">Kriva zaporka!</string>
+ <string name="alert_key_corrupted_msg">Privatni ključ je oštećen!</string>
+ <string name="alert_sdcard_absent">SD kartica nije umetnuta!</string>
+ <string name="button_add">Dodaj</string>
+ <string name="button_change">Promijeni</string>
+ <string name="button_generate">Izradi ključ</string>
+ <string name="button_resize">Promijeni veličinu</string>
+ <string name="alert_disconnect_msg">Veza prekinuta</string>
+ <string name="msg_copyright">Copyright © 2007-2008 Kenny Root http://the-b.org/, Jeffrey Sharkey http://jsharkey.org/</string>
+ <string name="pref_emulation_category">Emulacija Terminala</string>
+ <string name="pref_emulation_title">Način emulacije</string>
+ <string name="pref_emulation_summary">Način emulacije terminala korišten za PTY veze</string>
+ <string name="pref_scrollback_title">Veličina ScrollBack-a</string>
+ <string name="pref_scrollback_summary">Veličina ScrollBack-a koja se čuva u memoriji za svaku konzolu</string>
+ <string name="pref_ui_category">Korisničko sučelje</string>
+ <string name="pref_rotation_title">Način rotacije</string>
+ <string name="pref_rotation_summary">Kako promijeniti rotaciju kada se tipkovnica pojavi/nestane</string>
+ <string name="pref_fullscreen_title">Cijeli zaslon</string>
+ <string name="pref_fullscreen_summary">Sakrij statusnu liniju u konzoli</string>
+ <string name="pref_memkeys_title">Zapamti ključeve u memoriji</string>
+ <string name="pref_memkeys_summary">Čuvaj otključane ključeve u memoriji dok se ne zatvori pozadinski servis</string>
+ <string name="pref_conn_persist_title">Održavaj konekcije</string>
+ <string name="pref_conn_persist_summary">Prisili konekcije da ostanu spojene u pozadini</string>
+ <string name="pref_keymode_title">Prečaci direktorija</string>
+ <string name="pref_keymode_summary">Izaberi kako koristiti Alt kao \'/\' i Shift kao Tab</string>
+ <string name="pref_camera_title">Prečac kamere</string>
+ <string name="pref_camera_summary">Izaberi koji prečac pokrenuti kada se stisne dugme kamere</string>
+ <string name="pref_keepalive_title">Održi zaslon budnim</string>
+ <string name="pref_keepalive_summary">Spriječi gašenje zaslona dok se koristi konzola</string>
+ <string name="pref_wifilock_title">Održi Wi-Fi aktivnim</string>
+ <string name="pref_wifilock_summary">Spriječi gašenje Wi-Fi-a dok je aktivna sesija</string>
+</resources>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 195f758..66c9947 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -1,6 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="app_desc">Egyszerű, hatékony, nyílt-forráskódú SSH kliens.</string>
+ <string name="service_desc">Fenntartja az SSH kapcsolatokat és a betöltött nyilvános kulcsokat</string>
<string name="title_hosts_list">Kiszolgálók</string>
<string name="title_pubkey_list">Publikus kulcsok</string>
<string name="title_port_forwards_list">Port átiránytások</string>
@@ -78,8 +79,6 @@
<string name="pref_fullscreen_summary">Rejtett állapotsor a konzol használata során</string>
<string name="pref_memkeys_title">Kulcsok megörzése a memóriában</string>
<string name="pref_memkeys_summary">Tartsa nyitva a memóriában lévő kulcsokat amíg a háttérszolgáltatás meg nem szűnik</string>
- <string name="pref_update_title">Frissítés ellenőrzése</string>
- <string name="pref_update_summary">ConnectBot frissítés ellenőrzésének maximális gyakorisága</string>
<string name="pref_conn_persist_title">Kapcsolatok fenntartása</string>
<string name="pref_conn_persist_summary">A háttérben tartsa fenn a kapcsolatokat</string>
<string name="pref_keymode_title">Könyvtár hivatkozások</string>
@@ -103,9 +102,6 @@
<string name="list_keymode_none">Tiltás</string>
<string name="list_pubkeyids_none">Kulcsok használatának tiltása</string>
<string name="list_pubkeyids_any">Bármely nyilvános kulcs használata</string>
- <string name="list_update_daily">Naponta</string>
- <string name="list_update_weekly">Hetente</string>
- <string name="list_update_never">Soha</string>
<string name="hostpref_nickname_title">Becenév</string>
<string name="hostpref_color_title">Szín-kategória</string>
<string name="hostpref_fontsize_title">Betűtipus mérete (pt)</string>
@@ -126,10 +122,8 @@
<string name="hostpref_connection_category">Kapcsolat beállításai</string>
<string name="hostpref_username_title">Felhasználónév</string>
<string name="hostpref_hostname_title">Kiszolgáló</string>
+ <string name="hostpref_port_title">Port</string>
<string name="bind_never">Még nem csatlakozott</string>
- <string name="bind_minutes">%1$s perce</string>
- <string name="bind_hours">%1$s órája</string>
- <string name="bind_days">%1$s napja</string>
<string name="console_copy_done">%1$d bájt a vágólapra másolva</string>
<string name="console_copy_start">Érintéssel és húzással\nvagy a hanyattegér használatával\njelölje ki a másolandó területet.</string>
<string name="console_menu_close">Bezárás</string>
@@ -151,6 +145,7 @@
<string name="list_format_error">A %1$s formátum használata</string>
<string name="format_username">felhasználónév</string>
<string name="format_hostname">gépnév</string>
+ <string name="format_port">port</string>
<string name="list_menu_pubkeys">Publikus kulcsok kezelése</string>
<string name="list_menu_sortcolor">Rendezés szín szerint</string>
<string name="list_menu_sortname">Rendezés név szerint</string>
@@ -165,7 +160,12 @@
<string name="list_rotation_port">Álló mód kényszerítése</string>
<string name="list_rotation_auto">Automatikus</string>
<string name="list_camera_ctrlaspace">Ctrl+A majd Space</string>
+ <string name="list_camera_ctrla">Ctrl+A</string>
+ <string name="list_camera_esc">Esc</string>
+ <string name="list_camera_esc_a">Esc+A</string>
<string name="list_camera_none">Semmit</string>
+ <string name="list_delkey_backspace">Backspace</string>
+ <string name="list_delkey_del">Delete</string>
<string name="delete_message">Biztosan törli a \'%1$s\'-t?</string>
<string name="delete_pos">Igen, törlés</string>
<string name="delete_neg">Mégsem</string>
@@ -193,9 +193,6 @@
<string name="terminal_enable_portfoward">Port átirányítás engedélyezése: %1$s</string>
<string name="local_shell_unavailable">Hiba! Nincs lokális shell ezen a telefonon.</string>
<string name="notification_text">%1$s a figyelmére szorul.</string>
- <string name="upgrade">Új verzió</string>
- <string name="upgrade_pos">Igen, frissítés</string>
- <string name="upgrade_neg">Most nem</string>
<string name="no">Nem</string>
<string name="with_confirmation">Csak beleegyezéssel</string>
<string name="yes">Igen</string>
@@ -206,4 +203,9 @@
<string name="color_green">zöld</string>
<string name="color_blue">kék</string>
<string name="color_gray">szürke</string>
+ <string name="image_description_connected">Kapcsolódva</string>
+ <string name="image_description_key_is_locked">A Kulcs zárolva.</string>
+ <string name="image_description_toggle_control_character">Vezérlő jel i/n</string>
+ <string name="image_description_send_escape_character">Kilépő jel küldés</string>
+ <string name="image_description_show_keyboard">Billentyűzet megmutatása</string>
</resources>
diff --git a/res/values-id/strings.xml b/res/values-id/strings.xml
index b43b88b..4a9407d 100644
--- a/res/values-id/strings.xml
+++ b/res/values-id/strings.xml
@@ -1,16 +1,19 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
- <string name="app_desc">Sederhana, kuat, klien kode terbuka SSH</string>
+ <string name="app_desc">Sederhana, tangguh, klien SSH dengan kode terbuka.</string>
+ <string name="service_desc">"Mengelola koneksi SSH dan kunci publik yang terrekam"</string>
<string name="title_hosts_list">Host</string>
<string name="title_pubkey_list">Kunci Publik</string>
<string name="title_host_editor">Ubah Host</string>
<string name="title_help">Bantuan</string>
+ <string name="title_colors">Warna</string>
+ <string name="title_color_picker">Pilih warna</string>
<string name="resolve_connect">Hubungi</string>
<string name="resolve_entropy">Kumpulkan entropy</string>
<string name="menu_insert">Tambahkan host</string>
<string name="menu_delete">Hapus Host</string>
<string name="menu_preferences">Kesukaan</string>
- <string name="help_intro">Mohon pilih sebuah topik dibawah untuk informasi lebih lanjut di subjek tertentu.</string>
+ <string name="help_intro">Silahkan pilih sebuah topik dibawah untuk informasi lebih lanjut di suatu subjek tertentu.</string>
<string name="help_about">Mengenai ConnectBot</string>
<string name="help_keyboard">Papan Ketik</string>
<string name="pubkey_generate">Buat</string>
@@ -18,129 +21,150 @@
<string name="pubkey_delete">Tombol Hapus</string>
<string name="pubkey_gather_entropy">Kumpulkan Entropy</string>
<string name="pubkey_touch_prompt">Sentuh boks ini untuk mengumpulkan ke tidak tentuan: %1$d%% selesai</string>
- <string name="pubkey_touch_hint">Untuk menyakinkan ketidak tentuan selama pembuatan kunci, gerakan tangan anda secara tidak menentu diatas kotak dibawah.</string>
- <string name="pubkey_generating">Membuat pasangan kunci</string>
- <string name="pubkey_copy_private">Menyalin kunci pribadi</string>
+ <string name="pubkey_touch_hint">Untuk memastikan ketidaktentuan selama pembuatan kunci, gerakkan tangan anda secara acak diatas kotak dibawah ini.</string>
+ <string name="pubkey_generating">Membuat pasangan kunci...</string>
+ <string name="pubkey_copy_private">Salin kunci pribadi</string>
<string name="pubkey_copy_public">Salin kunci publik</string>
- <string name="pubkey_list_empty">Sentuh Menu untuk membuat/atau mengimpor pasangan kunci</string>
+ <string name="pubkey_list_empty">Sentuh Menu untuk membuat/atau mengimpor pasangan kunci.</string>
<string name="pubkey_unknown_format">Format tidak diketahui</string>
<string name="pubkey_change_password">Ubah kata sandi</string>
<string name="pubkey_list_pick">Ambil dari /sdcard</string>
<string name="pubkey_import_parse_problem">Masalah dalam mengambil kunci pribadi yang terimpor</string>
<string name="pubkey_unlock">Buka kunci</string>
- <string name="pubkey_failed_add">Kata sandi buruk untuk kunci \'%1$s\'. Authentifikasi gagal.</string>
+ <string name="pubkey_failed_add">Kata sandi salah untuk kunci \'%1$s\'. Authentifikasi gagal.</string>
<string name="pubkey_memory_load">Muat ke memori</string>
<string name="pubkey_memory_unload">Hapus dari memori</string>
- <string name="pubkey_load_on_start">Muat kunci pada waktu memulai</string>
+ <string name="pubkey_load_on_start">Muat kunci pada saat memulai</string>
+ <string name="pubkey_confirm_use">Konfirmasi sebelum digunakan</string>
<string name="portforward_list_empty">Sentu Menu untuk membuat\nport forwards.</string>
<string name="portforward_edit">Ubah port forward</string>
<string name="portforward_delete">Hapus port forward</string>
<string name="prompt_nickname">Nama panggilan:</string>
<string name="prompt_nickname_hint_pubkey">Kunci kerja saya</string>
- <string name="prompt_source_port">Port sumber</string>
+ <string name="prompt_source_port">Port asal</string>
<string name="prompt_destination">Tujuan:</string>
<string name="prompt_old_password">Kata sandi sebelumnya:</string>
<string name="prompt_password">Kata sandi:</string>
<string name="prompt_again">(sekali lagi)</string>
- <string name="prompt_type">Ketik:</string>
- <string name="prompt_password_can_be_blank">Catatan: kata sandi tidak boleh kosong</string>
+ <string name="prompt_type">Tipe:</string>
+ <string name="prompt_password_can_be_blank">Catatan: sandi dapat dikosongkan</string>
+ <string name="prompt_bits">Jumlah bit:</string>
<string name="prompt_pubkey_password">Kata sandi untuk kunci \'%1$s\'</string>
+ <string name="prompt_allow_agent_to_use_key">Ijinkan remote host untuk\nmenggunakan kunci \'%1$s\'?</string>
<string name="host_verification_failure_warning_header">PERINGATAN: IDENTIFIKASI REMOTE HOST TELAH BERUBAH!</string>
- <string name="host_verification_failure_warning">INI MEMUNGKINKAN BAHWA SESEORANG TELAH MELAKUKAN PERBUATAN KOTOR!\nSeseorang dapat saja melakukan penyadapan kepada anda saay ini (serangan orang-di-tengah)!\nlt ini juga memungkinkan jika kunci host telah berubah.</string>
- <string name="prompt_host_disconnected">Host telah terputus.\nTutup sesi ini?</string>
- <string name="prompt_continue_connecting">Apakah anda yakin anda ingin\n melanjutkan untuk menghubungi?</string>
+ <string name="host_verification_failure_warning">KEMUNGKINAN SESEORANG SEDANG BERBUAT KOTOR!\nSeseorang mungkin tengah menyadap anda saat ini (serangan man-in-the-middle)!\nMungkin juga kunci host baru saja diubah.</string>
+ <string name="prompt_host_disconnected">Sambungan dengan host telah terputus.\nTutup sesi ini?</string>
+ <string name="prompt_continue_connecting">Apakah Anda yakin ingin\nterus mencoba menyambungkan?</string>
<string name="host_authenticity_warning">Authentifikasi dari host \'%1$s\' tidak dapat dibuat.</string>
<string name="host_fingerprint">Host %1$s tanda tangan kunci adalah %2$s</string>
<string name="alert_passwords_do_not_match_msg">Kata sandi tidak cocok!</string>
<string name="alert_wrong_password_msg">Kata sandi salah!</string>
- <string name="alert_key_corrupted_msg">Tombol kunci telah terkorupsi!</string>
+ <string name="alert_key_corrupted_msg">Kunci pribadi nampaknya telah rusak!</string>
<string name="alert_sdcard_absent">SD card belum dimasukan!</string>
<string name="button_add">Tambahkan</string>
<string name="button_change">Ubah</string>
<string name="button_generate">Buat Kunci</string>
<string name="button_resize">Ubah ukuran</string>
- <string name="alert_disconnect_msg">Koneksi Hilang</string>
+ <string name="alert_disconnect_msg">Koneksi terputus</string>
<string name="msg_copyright">Hak Cipta © 2007-2008 Kenny Root http://the-b.org/, Jeffrey Sharkey http://jsharkey.org/</string>
<string name="pref_emulation_category">Terminal Emulasi</string>
<string name="pref_emulation_title">Mode Emulasi</string>
- <string name="pref_emulation_summary">Mode emulasi terminal yang digunakan untuk koneksi PTY</string>
+ <string name="pref_emulation_summary">Mode terminal emulasi digunakan untuk koneksi PTY</string>
<string name="pref_scrollback_title">Ukuran Scrollback</string>
- <string name="pref_scrollback_summary">Ukuran dari penyangga scrollback untuk menjaga memori disetiap konsol</string>
+ <string name="pref_scrollback_summary">Ukuran penyangga scrollback yang harus tersimpan di memori pada setiap konsol</string>
<string name="pref_ui_category">Antarmuka pengguna</string>
<string name="pref_rotation_title">Mode rotasi</string>
<string name="pref_rotation_summary">Bagaimana mengubah rotasi ketika papan ketik menyembul masuk/keluar</string>
<string name="pref_fullscreen_title">Layar penuh</string>
<string name="pref_fullscreen_summary">Sembunyikan bar status ketika dalam konsol</string>
+ <string name="pref_keyboard_category">Papan tik</string>
<string name="pref_memkeys_title">Ingat kunci dalam memori</string>
- <string name="pref_memkeys_summary">Jaga kunci tidak terkunci dalam memori ketika backend service berakhir</string>
- <string name="pref_update_title">Perbarui pemeriksaan</string>
- <string name="pref_update_summary">Set frekuensi maksimal untuk memeriksa untuk memperbarui ConnectBot</string>
- <string name="pref_keymode_title">Direktori jalan pintas</string>
+ <string name="pref_memkeys_summary">Simpan kunci yang terbuka kedalam memori hingga layanan backend berakhir</string>
+ <string name="pref_conn_persist_title">Pertahankan koneksi</string>
+ <string name="pref_conn_persist_summary">Paksa koneksi untuk tetap terhubung ketika bekerja di belakang layar</string>
+ <string name="pref_keymode_title">Jalan pintas direktori</string>
<string name="pref_keymode_summary">Pilih bagaimana menggunakan Alt untuk \'/\' dan Shift untuk Tab</string>
<string name="pref_camera_title">Jalan pintas kamera</string>
- <string name="pref_camera_summary">Pilih jalan pintas mana untuk menyalakan ketika tombol kamera ditekan</string>
- <string name="pref_keepalive_title">Jaga layar terjaga</string>
- <string name="pref_keepalive_summary">Jaga layar dari padam ketika bekerja dalam sebuah konsol</string>
- <string name="pref_wifilock_title">Jaga Wi-Fi aktif</string>
- <string name="pref_wifilock_summary">Jaga Wi-Fi supaya tidak padam ketika sesi aktif</string>
+ <string name="pref_camera_summary">Pilih jalan pintas yang akan diaktifkan saat tombol kamera ditekan</string>
+ <string name="pref_keepalive_title">Jaga layar tetap terbuka</string>
+ <string name="pref_keepalive_summary">Mencegah layar menjadi padam ketika bekerja dalam sebuah konsol</string>
+ <string name="pref_wifilock_title">Wi-Fi selalu aktif</string>
+ <string name="pref_wifilock_summary">Jaga Wi-Fi agar tidak padam ketika sesi aktif</string>
<string name="pref_bumpyarrows_title">Panah belok</string>
- <string name="pref_bumpyarrows_summary">Bergetar ketika mengirim tombol panah dari trackball; berguna untuk koneksi lambat</string>
+ <string name="pref_bumpyarrows_summary">Getarkan saat mengirim tombol panah dari trackball, berguna untuk koneksi lambat</string>
+ <string name="pref_bell_category">Bel terminal</string>
+ <string name="pref_bell_title">Bel berdering</string>
+ <string name="pref_bell_volume_title">Volume dering</string>
+ <string name="pref_bell_vibrate_title">Getarkan saat berdering</string>
+ <string name="pref_bell_notification_title">Pemberitahuan di belakang layar</string>
+ <string name="pref_bell_notification_summary">Kirim pemberitahuan ketika sebuah terminal berjalan di belakang dengan membunyikan sebuah nada dering</string>
<string name="list_keymode_right">Gunakan tombol sisi kanan</string>
<string name="list_keymode_left">Gunakan tombol sisi-kiri</string>
<string name="list_keymode_none">Tidak aktif</string>
- <string name="list_pubkeyids_none">Jangan gunakan tombol</string>
- <string name="list_pubkeyids_any">Gunakan tombol apapun yang tidak terkunci</string>
- <string name="list_update_daily">Setiap hari</string>
- <string name="list_update_weekly">Setiap minggu</string>
- <string name="list_update_never">Tidak pernah</string>
+ <string name="list_pubkeyids_none">Jangan gunakan kunci</string>
+ <string name="list_pubkeyids_any">Gunakan kunci apapun yang tidak terkunci</string>
<string name="hostpref_nickname_title">Nama panggilan</string>
<string name="hostpref_color_title">Kategori warna</string>
+ <string name="hostpref_fontsize_title">Ukuran huruf (pt)</string>
<string name="hostpref_pubkeyid_title">Gunakan otentifikasi pubkey</string>
+ <string name="hostpref_authagent_title">Gunakan SSH auth agent</string>
<string name="hostpref_postlogin_title">otomasi setelah login</string>
<string name="hostpref_postlogin_summary">Perintah yang dijalankan diremote server sekali terotentifikasi</string>
<string name="hostpref_compression_title">Kompresi</string>
<string name="hostpref_compression_summary">Ini mungkin akan membantu dengan jaringan lambat</string>
<string name="hostpref_wantsession_title">Jalankan sesi shell</string>
<string name="hostpref_wantsession_summary">Non-aktifkan kesukaan ini hanya untuk port forwards</string>
+ <string name="hostpref_stayconnected_summary">Coba menyambungkan kembali ke host jika terputus</string>
+ <string name="hostpref_delkey_title">Tombol DEL</string>
+ <string name="hostpref_delkey_summary">Kode kunci terkirim ketika tombol DEL ditekan</string>
<string name="hostpref_encoding_title">Pengkodean</string>
<string name="hostpref_encoding_summary">Karakter pengkodean untuk host</string>
<string name="hostpref_connection_category">Konfigurasi koneksi</string>
<string name="hostpref_username_title">Nama pengguna</string>
+ <string name="hostpref_hostname_title">Host</string>
+ <string name="hostpref_port_title">Port</string>
<string name="bind_never">Tidak pernah terhubung</string>
- <string name="bind_minutes">%1$s menit yang lalu</string>
- <string name="bind_hours">%1$s jam yang lalu</string>
- <string name="bind_days">%1$s hari yang lalu</string>
- <string name="console_copy_done">Tersalin %1$d bytes ke papan klip</string>
- <string name="console_copy_start">Sentuh dan tarik\natau gunakan tombol panah\nuntuk memilih daerah untuk disalin</string>
+ <string name="console_copy_done">Tersalin %1$d byte ke papan-klip</string>
+ <string name="console_copy_start">Sentuh dan tarik\natau gunakan tombol panah\nuntuk memilih area yang akan disalin</string>
<string name="console_menu_close">Tutup</string>
<string name="console_menu_copy">Salin</string>
<string name="console_menu_paste">Tempel</string>
- <string name="console_menu_resize">Paksa ukuran</string>
+ <string name="console_menu_portforwards">Penerusan port</string>
+ <string name="console_menu_resize">Paksakan ukuran</string>
+ <string name="console_menu_urlscan">Pindai URL</string>
+ <string name="button_yes">Ya</string>
+ <string name="button_no">Tidak</string>
<string name="portforward_local">Lokal</string>
+ <string name="portforward_remote">Remote</string>
<string name="portforward_dynamic">Dinamis (SOCKS)</string>
- <string name="portforward_pos">Buat port forward</string>
- <string name="portforward_done">Sukses membuat port forward</string>
- <string name="portforward_problem">Masalah membuat port forward, mungkin anda menggunakan port dibawah 1024 atau port telah digunakan?</string>
- <string name="portforward_menu_add">Tambahkan port forward</string>
+ <string name="portforward_pos">Buat penerusan port</string>
+ <string name="portforward_done">Berhasil membuat penerusan port</string>
+ <string name="portforward_problem">Masalah dalam membuat penerusan port, mungkin Anda menggunakan nomor port di bawah 1024 atau nomor port telah digunakan?</string>
+ <string name="portforward_menu_add">Tambah penerusan port</string>
<string name="hint_userhost">pengguna\@namahost</string>
- <string name="list_format_error">Gunakan format %1$s</string>
+ <string name="list_format_error">Gunakan bentuk \"%1$s\"</string>
<string name="format_username">pengguna</string>
<string name="format_hostname">namahost</string>
- <string name="list_menu_pubkeys">Pelihara Pubkeys</string>
+ <string name="format_port">Port</string>
+ <string name="list_menu_pubkeys">Atur Pubkey</string>
<string name="list_menu_sortcolor">Urutkan berdasarkan warna</string>
<string name="list_menu_sortname">Urutkan berdasarkan nama</string>
- <string name="list_menu_settings">Konfigurasi</string>
+ <string name="list_menu_settings">Pengaturan</string>
<string name="list_host_disconnect">Putus koneksi</string>
<string name="list_host_edit">Ubah host</string>
- <string name="list_host_portforwards">Ubah port forwards</string>
+ <string name="list_host_portforwards">Ubah penerusan port</string>
<string name="list_host_delete">Hapus host</string>
- <string name="list_host_empty">Gunakan kotak cepat-hubungi\ndibawah untuk menghubungi sebuah host.</string>
+ <string name="list_host_empty">Gunakan kotak cepat-sambung\ndi bawah untuk tersambung dengan sebuah host</string>
<string name="list_rotation_default">Baku</string>
- <string name="list_rotation_land">Paksa landscape</string>
- <string name="list_rotation_port">Paksa portrait</string>
+ <string name="list_rotation_land">Paksakan bentuk datar</string>
+ <string name="list_rotation_port">Paksakan bentuk tegak</string>
<string name="list_rotation_auto">Otomatis</string>
<string name="list_camera_ctrlaspace">Ctrl+A kemudian spasi</string>
+ <string name="list_camera_ctrla">Ctrl+A</string>
+ <string name="list_camera_esc">Esc</string>
<string name="list_camera_none">Kosong</string>
+ <string name="list_delkey_backspace">Backspace</string>
+ <string name="list_delkey_del">Hapus</string>
<string name="delete_message">Apakah anda yakin ingin menghapus %1$s?</string>
<string name="delete_pos">Ya, hapus</string>
<string name="delete_neg">Batal</string>
@@ -148,23 +172,41 @@
<string name="wizard_next">Selanjutnya</string>
<string name="wizard_back">Sebelumnya</string>
<string name="terminal_no_hosts_connected">Tidak ada host yang saat ini terhubung</string>
+ <string name="terminal_connecting">Menyambungkan ke %1$s:%2$d melalui %3$s</string>
+ <string name="terminal_sucess">Host terverifikasi \'%1$s\' kunci: %2$s</string>
<string name="terminal_failed">Verifikasi kunci host gagal.</string>
+ <string name="terminal_using_s2c_algorithm">Algoritma server-ke-klien: %1$s %2$s</string>
+ <string name="terminal_using_c2s_algorithm">Algoritma klien-ke-server: %1$s %2$s</string>
+ <string name="terminal_using_algorithm">Menggunakan algoritma : %1$s %2$s</string>
<string name="terminal_auth">Mencoba untuk mengotentifikasi</string>
<string name="terminal_auth_pass">Mencoba otentifikasi \'kata sandi\'</string>
<string name="terminal_auth_pass_fail">Metoda otentifikasi \'kata sandi\' gagal</string>
<string name="terminal_auth_pubkey_any">Mencoba otentifikasi \'kunci publik\' dengan kunci publik apapun dalam memori</string>
- <string name="terminal_auth_pubkey_invalid">Kunci publik yang dipilih tidak valid, coba pilih kembali tombol dalam pengubah host</string>
+ <string name="terminal_auth_pubkey_invalid">Kunci publik yang dipilih tidak valid, coba pilih kembali kunci dalam editor host</string>
<string name="terminal_auth_pubkey_specific">Mencoba otentifikasi \'kunci publik\' dengan kunci publik spesifik</string>
+ <string name="terminal_auth_pubkey_fail">Metode otentifikasi \'kunci publik\' dengan kunci \'%1$s\' gagal</string>
<string name="terminal_auth_ki">Mencoba otentifikasi \'papan ketik interaktif\'</string>
<string name="terminal_auth_ki_fail">Metode otentifikasi \'papan ketik interaktif\' gagal</string>
<string name="terminal_auth_fail">[Host anda tidak mendukung otentifikasi \'kata sandi\' atau \'papan ketik interaktif\'.]</string>
+ <string name="terminal_no_session">Sesi tidak akan dimulai karena preferensi host</string>
+ <string name="terminal_enable_portfoward">Aktifkan port forward: %1$s</string>
<string name="local_shell_unavailable">Gagal! Shell lokal tidak tersedia di telepon ini.</string>
- <string name="upgrade">Versi baru</string>
- <string name="upgrade_pos">Ya, perbarui</string>
- <string name="upgrade_neg">Jangan sekarang</string>
- <string name="exceptions_submit_message">It appears ConnectBot had a problem last time it ran. Submit error report to ConnectBot developers?</string>
+ <string name="notification_text">%1$s memerlukan perhatian anda.</string>
+ <string name="no">Tidak</string>
+ <string name="with_confirmation">Dengan konfirmasi</string>
+ <string name="yes">Ya</string>
+ <string name="exceptions_submit_message">Sepertinya ConnectBot mengalami masalah saat berjalan sebelumnya. Kirim laporan galat ke para pengembang ConnectBot?</string>
+ <string name="menu_colors_reset">Atur ulang</string>
+ <string name="app_is_running">ConnectBot sedang berjalan</string>
<string name="color_red">merah</string>
<string name="color_green">hijau</string>
<string name="color_blue">biru</string>
<string name="color_gray">abu-abu</string>
+ <string name="colors_fg_label">WD: %1$d</string>
+ <string name="color_bg_label">WB: %1$d</string>
+ <string name="image_description_connected">Tersambung.</string>
+ <string name="image_description_key_is_locked">Pubkey terkunci.</string>
+ <string name="image_description_toggle_control_character">Ubah-ubah karakter control.</string>
+ <string name="image_description_send_escape_character">Kirim karakter escape.</string>
+ <string name="image_description_show_keyboard">Perlihatkan papan ketik.</string>
</resources>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
new file mode 100644
index 0000000..4558633
--- /dev/null
+++ b/res/values-is/strings.xml
@@ -0,0 +1,173 @@
+<?xml version='1.0' encoding='utf-8'?>
+<resources>
+ <string name="service_desc">Heldur utan um SSH tengingar og geymda \"pubkeys\"</string>
+ <string name="title_hosts_list">Þjónar</string>
+ <string name="title_pubkey_list">Almennir lyklar</string>
+ <string name="title_port_forwards_list">Áframsending ports</string>
+ <string name="title_host_editor">Breyta þjónum</string>
+ <string name="title_help">Hjálp</string>
+ <string name="title_colors">Litir</string>
+ <string name="resolve_connect">Tengjast</string>
+ <string name="resolve_entropy">Safna bitum af handahófi</string>
+ <string name="menu_insert">Bæta við þjóni</string>
+ <string name="menu_delete">Eyða þjóni</string>
+ <string name="menu_preferences">Valmöguleikar</string>
+ <string name="help_intro">Vinsamlegast veldu atriði hér að neðan til þess að fá nánari upplýsingar um það.</string>
+ <string name="help_about">Um ConnectBot</string>
+ <string name="help_keyboard">Lyklaborð</string>
+ <string name="pubkey_generate">Búa til</string>
+ <string name="pubkey_import">Flytja inn</string>
+ <string name="pubkey_delete">Eyða lykli</string>
+ <string name="pubkey_gather_entropy">Safna bitum af handahófi</string>
+ <string name="pubkey_touch_prompt">Snertu kassann til þess að safna bitum af handahófi, %1$d%% búið.</string>
+ <string name="pubkey_touch_hint">Til þess að tryggja handahófs kenndan lykil, færðu fingurinn á þér handahófskennt yfir kassann að neðan.</string>
+ <string name="pubkey_generating">Framkalla lykla par...</string>
+ <string name="pubkey_copy_private">Afrita einka lykil</string>
+ <string name="pubkey_copy_public">Afrita almennan lykil</string>
+ <string name="pubkey_list_empty">Ýttu á \"Menu\" til þess að búa til\neða fluttu inn lykla par.</string>
+ <string name="pubkey_unknown_format">Óþekkt snið</string>
+ <string name="pubkey_change_password">Breyta lykilorði</string>
+ <string name="pubkey_list_pick">Velja af /sdcard</string>
+ <string name="pubkey_import_parse_problem">Ekki tókst að lesa einkalykilinn</string>
+ <string name="pubkey_unlock">Aflæsa lykli</string>
+ <string name="pubkey_failed_add">Rangt lykilorð fyrir lykilinn \'%1$s\'. Staðfesting mistókst</string>
+ <string name="pubkey_memory_load">Hlaða inn í minni</string>
+ <string name="pubkey_memory_unload">Hlaða úr minni</string>
+ <string name="pubkey_load_on_start">Hlaða lyklum við ræsingu</string>
+ <string name="pubkey_confirm_use">Staðfesta fyrir notkun</string>
+ <string name="portforward_list_empty">Ýttu á \"Menu\" til þess að\nbúa til port áframsendingu</string>
+ <string name="portforward_edit">Breyta áframsendingu ports</string>
+ <string name="portforward_delete">Eyða áframsendingu ports</string>
+ <string name="prompt_nickname">Gælunafn:</string>
+ <string name="prompt_nickname_hint_pubkey">Lykillinn minn</string>
+ <string name="prompt_source_port">Frá:</string>
+ <string name="prompt_destination">Til:</string>
+ <string name="prompt_old_password">Gamla lykilorðið</string>
+ <string name="prompt_password">Lykilorð:</string>
+ <string name="prompt_again">(aftur)</string>
+ <string name="prompt_type">Tegund:</string>
+ <string name="prompt_password_can_be_blank">ATH: lykilorðið getur verið tómt</string>
+ <string name="prompt_bits">Bitar:</string>
+ <string name="prompt_pubkey_password">Lykilorð fyrir lykilinn \'%1$s\'</string>
+ <string name="prompt_allow_agent_to_use_key">Leyfa þjóninum að\nnota lykilinn \'%1$s\'?</string>
+ <string name="host_verification_failure_warning_header">VIÐVÖRUN: AUÐKENNING ÞJÓNSINS HEFUR BREYST!</string>
+ <string name="host_verification_failure_warning">ÞAÐ ER MÖGULEIKI Á AÐ EINHVER SÉ AÐ GERA EITTHVAÐ LJÓTT!\nÞað gæti verið að einhver sé að fylgjast með með þér (man-in-the-middle árás)!\nEn það gæti líka verið að lykill þjónsins hafi einfaldlega breyst.</string>
+ <string name="prompt_host_disconnected">Þjónninn hefur verið aftengdur.\nViltu loka?</string>
+ <string name="prompt_continue_connecting">Ertu viss um að þú\nviljir halda áfram að tengjast?</string>
+ <string name="host_authenticity_warning">Upprunaleiki lykilsins fyrir þjóninn \'%1$s\' getur ekki verið staðfestur.</string>
+ <string name="host_fingerprint">Þjónninn er %1$s fingrafar lykilsins er %2$s</string>
+ <string name="alert_passwords_do_not_match_msg">Lykilorðin passa ekki saman!</string>
+ <string name="alert_wrong_password_msg">Rangt lykilorð!</string>
+ <string name="alert_key_corrupted_msg">Einka lykillinn lítur út fyrir að vera skemmdur!</string>
+ <string name="alert_sdcard_absent">SD kort er ekki til staðar!</string>
+ <string name="button_add">Bæta við</string>
+ <string name="button_change">Breyta</string>
+ <string name="button_generate">Framkalla lykil</string>
+ <string name="button_resize">Breyta stærð</string>
+ <string name="alert_disconnect_msg">Tengingu tapað</string>
+ <string name="msg_copyright">Allur réttur áskilinn © 2007-2008 Kenny Root http://the-b.org/, Jeffrey Sharkey http://jsharkey.org/</string>
+ <string name="pref_ui_category">Notenda viðmót</string>
+ <string name="pref_rotation_title">Snúnings hamur</string>
+ <string name="pref_rotation_summary">Hvernig breyta skal snúningnum þegar lyklaborðið er sýnilegt/falið</string>
+ <string name="pref_fullscreen_title">Fylla skjáinn</string>
+ <string name="pref_fullscreen_summary">Fela stöðustiku þegar tenging er virk</string>
+ <string name="pref_memkeys_title">Geyma lykla í minni</string>
+ <string name="pref_memkeys_summary">Geyma ólæsta lykla í minni þangað til forritinu er lokað</string>
+ <string name="pref_conn_persist_title">Halda tengingum</string>
+ <string name="pref_conn_persist_summary">Halda tengingum í gangi þegar þær eru í bakgrunni</string>
+ <string name="pref_keymode_title">Orðabók</string>
+ <string name="pref_keymode_summary">Veldu hvernig nota skal Alt fyrir \'/\' og Shift fyrir Tab</string>
+ <string name="pref_camera_title">Myndavéla takkinn</string>
+ <string name="pref_camera_summary">Veldu hvað gerist þegar þrýst er á myndavéla takkann</string>
+ <string name="pref_keepalive_title">Halda skjánum í gangi</string>
+ <string name="pref_keepalive_summary">Banna skjánum að slokkna þegar tenging er virk</string>
+ <string name="pref_wifilock_title">Halda þráðlausu neti í gangi</string>
+ <string name="pref_wifilock_summary">Halda þráðlausu neti í gangi þegar tenging er virk</string>
+ <string name="pref_bumpyarrows_summary">Titra þegar örvar eru sendar með trackball; sniðugt fyrir óstöðugar tengingar</string>
+ <string name="pref_bell_category">Terminal bjalla</string>
+ <string name="pref_bell_title">Bjalla með hljóði</string>
+ <string name="pref_bell_volume_title">Hljóðstyrkur bjöllu</string>
+ <string name="pref_bell_vibrate_title">Titra þegar bjallan er virk</string>
+ <string name="pref_bell_notification_title">Boð frá bakgrunni</string>
+ <string name="pref_bell_notification_summary">Senda boð þegar þónn í bakgrunni sendir frá sé bjöllu.</string>
+ <string name="list_keymode_right">Nota hægri-hliðar lykla</string>
+ <string name="list_keymode_left">Nota vinstri-hliðar lykla</string>
+ <string name="list_keymode_none">Afvirkja</string>
+ <string name="list_pubkeyids_none">Ekki nota lykla</string>
+ <string name="list_pubkeyids_any">Nota hvaða ólæsta lykil sem er</string>
+ <string name="hostpref_nickname_title">Gælunafn</string>
+ <string name="hostpref_color_title">Lita flokk</string>
+ <string name="hostpref_fontsize_title">Leturstærð (pt)</string>
+ <string name="hostpref_pubkeyid_title">Innskráning með almennum lykli</string>
+ <string name="hostpref_postlogin_summary">Skipanir til þess að keyra á þjóni eftir innskráningu</string>
+ <string name="hostpref_compression_title">Þjöppun</string>
+ <string name="hostpref_compression_summary">Þetta getur hjálpað á hægu neti</string>
+ <string name="hostpref_stayconnected_title">Halda tengingu</string>
+ <string name="hostpref_stayconnected_summary">Reyna að endurtengjast ef aftenging verður</string>
+ <string name="hostpref_encoding_title">Kóðun</string>
+ <string name="hostpref_connection_category">Stillingar fyrir tengingu</string>
+ <string name="hostpref_username_title">Notendanafn</string>
+ <string name="hostpref_hostname_title">Þjónn</string>
+ <string name="hostpref_port_title">Port</string>
+ <string name="bind_never">Aldrei tengt</string>
+ <string name="console_copy_done">Afritaði %1$d bæti</string>
+ <string name="console_menu_close">Loka</string>
+ <string name="console_menu_copy">Afrita</string>
+ <string name="console_menu_paste">Líma</string>
+ <string name="console_menu_resize">Þvinga stærð</string>
+ <string name="console_menu_urlscan">Skanna fyrir slóðum</string>
+ <string name="button_yes">Já</string>
+ <string name="button_no">Nei</string>
+ <string name="portforward_dynamic">Breytilegt (SOCKS)</string>
+ <string name="portforward_pos">Áframsenda port</string>
+ <string name="portforward_done">Áframsendi port farsællega</string>
+ <string name="portforward_problem">Gat ekki áframsent portið, varstu að nota port minna en 1024? Eða er portið nú þegar áframsent?</string>
+ <string name="portforward_menu_add">Áframsenda port</string>
+ <string name="hint_userhost">notandi\@þjónn</string>
+ <string name="format_username">notendanafn</string>
+ <string name="format_hostname">þjónn</string>
+ <string name="format_port">port</string>
+ <string name="list_menu_sortcolor">Raða eftir lit</string>
+ <string name="list_menu_sortname">Raða eftir nafni</string>
+ <string name="list_menu_settings">Stillingar</string>
+ <string name="list_host_disconnect">Aftengjast</string>
+ <string name="list_host_edit">Breyta þjóni</string>
+ <string name="list_host_portforwards">Breyta áframsendingu ports</string>
+ <string name="list_host_delete">Eyða þjóni</string>
+ <string name="list_host_empty">Notaðu boxið hér að neðan\ntil þess að tengjast á þjón.</string>
+ <string name="list_rotation_default">Sjálfgefið</string>
+ <string name="list_rotation_land">þvinga á hlið</string>
+ <string name="list_rotation_port">þvinga beint</string>
+ <string name="list_rotation_auto">Sjálfgefið</string>
+ <string name="list_camera_ctrlaspace">Ctrl+A og Space</string>
+ <string name="list_camera_ctrla">Ctrl+A</string>
+ <string name="list_camera_esc">Esc</string>
+ <string name="list_camera_esc_a">Esc+A</string>
+ <string name="list_camera_none">Ekkert</string>
+ <string name="list_delkey_del">Eyða</string>
+ <string name="delete_message">Ert þú viss um að þú viljir eyða \'%1$s\'?</string>
+ <string name="delete_pos">Já, eyða</string>
+ <string name="delete_neg">Hætta við</string>
+ <string name="wizard_agree">Samþykkja</string>
+ <string name="wizard_next">Næsta</string>
+ <string name="wizard_back">Til baka</string>
+ <string name="terminal_no_hosts_connected">Engir þjónar tengdir í augnablikinu</string>
+ <string name="terminal_connecting">Tengist við %1$s:%2$d með %3$s</string>
+ <string name="terminal_failed">Ekki tókst að staðfesta lykil þjóns.</string>
+ <string name="terminal_using_algorithm">Nota algorithmann: %1$s %2$s</string>
+ <string name="terminal_auth">Reyni að staðfesta</string>
+ <string name="terminal_auth_pass">Reyni að tengjast með lykilorði</string>
+ <string name="terminal_auth_pass_fail">Ekki tókst að tengjast með lykilorði</string>
+ <string name="terminal_enable_portfoward">Áframsenda port: %1$s</string>
+ <string name="notification_text">%1$s krefst athygli þinnar.</string>
+ <string name="no">Nei</string>
+ <string name="with_confirmation">Með staðfestingu</string>
+ <string name="yes">Já</string>
+ <string name="exceptions_submit_message">Það lítur út fyrir að ConnectBot hafi verið í vandræðum síðast þegar það keyrði. Viltu senda villu greiningu á höfunda forritsins?</string>
+ <string name="menu_colors_reset">Endurstilla</string>
+ <string name="app_is_running">ConnectBot er í gangi</string>
+ <string name="color_red">rauður</string>
+ <string name="color_green">grænn</string>
+ <string name="color_blue">blár</string>
+ <string name="color_gray">grár</string>
+</resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 847bf82..22a4715 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -1,14 +1,16 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="app_desc">Un client SSH semplice, potente ed open-source.</string>
- <string name="title_hosts_list">Host</string>
+ <string name="service_desc">Mantieni le connessioni SSH e le chiavi pubbliche caricate</string>
+ <string name="title_hosts_list">Connessione</string>
<string name="title_pubkey_list">Chiavi pubbliche</string>
- <string name="title_port_forwards_list">Inoltro delle porte</string>
+ <string name="title_port_forwards_list">Inoltro porte</string>
<string name="title_host_editor">Modifica connessione</string>
- <string name="title_help">Aiuto</string>
+ <string name="title_help">Guida</string>
<string name="title_colors">Colori</string>
+ <string name="title_color_picker">Scelta colore</string>
<string name="resolve_connect">Connetti</string>
- <string name="resolve_entropy">Raccolta entropia</string>
+ <string name="resolve_entropy">Raccolta disordinata</string>
<string name="menu_insert">Aggiungi connessione</string>
<string name="menu_delete">Elimina connessione</string>
<string name="menu_preferences">Preferenze</string>
@@ -76,10 +78,9 @@
<string name="pref_rotation_summary">Come cambiare rotazione quando la tastiera esce/rientra</string>
<string name="pref_fullscreen_title">Schermo intero</string>
<string name="pref_fullscreen_summary">Nascondi la barra di stato in console</string>
+ <string name="pref_keyboard_category">Tastiera</string>
<string name="pref_memkeys_title">Mantieni chiavi in memoria</string>
<string name="pref_memkeys_summary">Mantiene le chiavi in memoria finché il servizio non viene terminato</string>
- <string name="pref_update_title">Controlla aggiornamenti</string>
- <string name="pref_update_summary">Imposta la frequenza massima per il controllo degli aggiornamenti</string>
<string name="pref_conn_persist_title">Connessioni permanenti</string>
<string name="pref_conn_persist_summary">Forza connessioni a rimanere collegate in sottofondo</string>
<string name="pref_keymode_title">Scorciatoie directory</string>
@@ -103,9 +104,6 @@
<string name="list_keymode_none">Disabilita</string>
<string name="list_pubkeyids_none">Non usare chiavi</string>
<string name="list_pubkeyids_any">Usa qualsiasi chiave sbloccata</string>
- <string name="list_update_daily">Ogni giorno</string>
- <string name="list_update_weekly">Ogni settimana</string>
- <string name="list_update_never">Mai</string>
<string name="hostpref_nickname_title">Soprannome</string>
<string name="hostpref_color_title">Categoria colore</string>
<string name="hostpref_fontsize_title">Dimensione caratteri (pt)</string>
@@ -128,9 +126,6 @@
<string name="hostpref_hostname_title">Host</string>
<string name="hostpref_port_title">Porta</string>
<string name="bind_never">Mai connesso</string>
- <string name="bind_minutes">%1$s minuti fa</string>
- <string name="bind_hours">%1$s ore fa</string>
- <string name="bind_days">%1$s giorni fa</string>
<string name="console_copy_done">%1$d byte copiati</string>
<string name="console_copy_start">Tocca e trascina\noppure usa il pad direzionale\nper selezionare un\'area da copiare</string>
<string name="console_menu_close">Chiudi</string>
@@ -200,9 +195,6 @@
<string name="terminal_enable_portfoward">Abilita il port forward: %1$s</string>
<string name="local_shell_unavailable">Fallimento! La shell locale non è disponibile su questo telefono.</string>
<string name="notification_text">%1$s richiede la tua attenzione.</string>
- <string name="upgrade">Nuova versione</string>
- <string name="upgrade_pos">Sì, aggiorna</string>
- <string name="upgrade_neg">Non adesso</string>
<string name="no">No</string>
<string name="with_confirmation">Richiedi conferma</string>
<string name="yes">Sì</string>
@@ -213,4 +205,10 @@
<string name="color_green">verde</string>
<string name="color_blue">blu</string>
<string name="color_gray">grigio</string>
+ <string name="colors_fg_label">CP: %1$d</string>
+ <string name="color_bg_label">CS: %1$d</string>
+ <string name="image_description_connected">Connesso.</string>
+ <string name="image_description_key_is_locked">La chiave e\' bloccata.</string>
+ <string name="image_description_send_escape_character">Mandare un carattere escape.</string>
+ <string name="image_description_show_keyboard">Mostra la tastiera.</string>
</resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index e6c70c8..f4f06dd 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -1,12 +1,14 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="app_desc">シンプルでパワフルなオープンソースのSSHクライアント</string>
+ <string name="service_desc">SSH接続とロードされた公開鍵の管理</string>
<string name="title_hosts_list">ホスト</string>
<string name="title_pubkey_list">公開鍵</string>
<string name="title_port_forwards_list">ポート転送</string>
<string name="title_host_editor">ホストを編集</string>
<string name="title_help">ヘルプ</string>
<string name="title_colors">配色</string>
+ <string name="title_color_picker">色の取得</string>
<string name="resolve_connect">接続</string>
<string name="resolve_entropy">エントロピーの取得</string>
<string name="menu_insert">ホストを追加</string>
@@ -39,6 +41,7 @@
<string name="portforward_edit">ポート転送を編集</string>
<string name="portforward_delete">ポート転送を削除</string>
<string name="prompt_nickname">鍵の名前</string>
+ <string name="prompt_nickname_hint_pubkey">私の作業用の鍵</string>
<string name="prompt_source_port">ソースポート:</string>
<string name="prompt_destination">転送先:</string>
<string name="prompt_old_password">旧パスワード:</string>
@@ -64,6 +67,7 @@
<string name="button_generate">鍵生成</string>
<string name="button_resize">リサイズ</string>
<string name="alert_disconnect_msg">接続が失われました.</string>
+ <string name="msg_copyright">Copyright © 2007-2008 Kenny Root http://the-b.org/, Jeffrey Sharkey http://jsharkey.org/</string>
<string name="pref_emulation_category">端末エミュレート</string>
<string name="pref_emulation_title">エミュレートモード</string>
<string name="pref_emulation_summary">PTYで使う端末エミュレーションモード</string>
@@ -72,16 +76,26 @@
<string name="pref_ui_category">ユーザーインターフェース</string>
<string name="pref_rotation_title">画面の向き</string>
<string name="pref_rotation_summary">キーボードのポップイン/アウト時の向きの変更</string>
+ <string name="pref_titlebarhide_title">タイトルバーを自動的に隠す</string>
+ <string name="pref_titlebarhide_summary">タイトルバーとアクセスメニューを表示するにはコンソールをタップ</string>
<string name="pref_fullscreen_title">全画面</string>
<string name="pref_fullscreen_summary">ステータスバーを隠蔽しコンソールとして利用</string>
+ <string name="pref_keyboard_category">キーボード</string>
+ <string name="pref_shiftfkeys_title">Shift+数字はFキー</string>
+ <string name="pref_shiftfkeys_summary">ハードウェアキーボードで、shiftと数字キーでF1-F10を送信します</string>
+ <string name="pref_ctrlfkeys_title">Ctrl+数字はFキー</string>
+ <string name="pref_ctrlfkeys_summary">ソフトウェアキーボードで、ctrlと数字キーでF1-F10を送信します</string>
+ <string name="pref_volumefont_title">音量ボタンでフォントサイズを変更する</string>
+ <string name="pref_volumefont_summary">フォントサイズはホストごとの設定でも変更することができます</string>
<string name="pref_memkeys_title">メモリに鍵を保持する</string>
<string name="pref_memkeys_summary">バックエンドのサービスが終了しない限り、アンロックした鍵をメモリに保持します</string>
- <string name="pref_update_title">更新を確認</string>
- <string name="pref_update_summary">ConnectBotの更新をチェックする頻度を設定します</string>
<string name="pref_conn_persist_title">持続的接続</string>
<string name="pref_conn_persist_summary">バックグラウンドでの実行中に接続の持続を強制する</string>
<string name="pref_keymode_title">ディレクトリのショートカット</string>
<string name="pref_keymode_summary">Altを\'/\'、ShiftをTabに割り当てます</string>
+ <string name="pref_stickymodifiers_title">スティッキー修飾キー</string>
+ <string name="pref_stickymodifiers_summary">別のキーが押されるまで、修飾キーは有効のままになります</string>
+ <string name="only_alt">alt のみ</string>
<string name="pref_camera_title">カメラボタンショートカット</string>
<string name="pref_camera_summary">カメラボタンが押されたときのショートカットを選びます</string>
<string name="pref_keepalive_title">画面をスリープしない</string>
@@ -101,9 +115,6 @@
<string name="list_keymode_none">無効</string>
<string name="list_pubkeyids_none">公開鍵を使わない</string>
<string name="list_pubkeyids_any">アンロックされた鍵のいずれかを使う</string>
- <string name="list_update_daily">毎日</string>
- <string name="list_update_weekly">毎週</string>
- <string name="list_update_never">しない</string>
<string name="hostpref_nickname_title">ニックネーム</string>
<string name="hostpref_color_title">色の系統</string>
<string name="hostpref_fontsize_title">フォントのサイズ (pt)</string>
@@ -117,6 +128,8 @@
<string name="hostpref_wantsession_summary">ポート転送のみ行う場合に無効にしてください</string>
<string name="hostpref_stayconnected_title">接続の維持</string>
<string name="hostpref_stayconnected_summary">接続が切れたら再接続を試みます</string>
+ <string name="hostpref_quickdisconnect_title">切断時に閉じる</string>
+ <string name="hostpref_quickdisconnect_summary">リモート切断後、プロンプトを表示せずにすぐに閉じます。</string>
<string name="hostpref_delkey_title">DELキー</string>
<string name="hostpref_delkey_summary">DELキーが押されたときに送るキーコード</string>
<string name="hostpref_encoding_title">エンコーディング</string>
@@ -126,9 +139,6 @@
<string name="hostpref_hostname_title">ホスト</string>
<string name="hostpref_port_title">ポート</string>
<string name="bind_never">未接続</string>
- <string name="bind_minutes">%1$s 分前</string>
- <string name="bind_hours">%1$s 時間前</string>
- <string name="bind_days">%1$s 日前</string>
<string name="console_copy_done">クリップボードに %1$d バイトコピーしました.</string>
<string name="console_copy_start">タッチしてドラッグ\nまたは方向パッドで\nコピー領域を選択</string>
<string name="console_menu_close">切断</string>
@@ -146,6 +156,7 @@
<string name="portforward_done">ポート転送の作成に成功しました.</string>
<string name="portforward_problem">ポート転送の作成ができません.おそらく1024ポート以下か既に利用されているポートを指定されています.</string>
<string name="portforward_menu_add">ポート転送の追加</string>
+ <string name="hint_userhost">ユーザ\@ホスト名</string>
<string name="list_format_error">%1$s 形式を使ってください</string>
<string name="format_username">ユーザ名</string>
<string name="format_hostname">ホスト名</string>
@@ -164,7 +175,12 @@
<string name="list_rotation_port">縦向き固定</string>
<string name="list_rotation_auto">自動判定</string>
<string name="list_camera_ctrlaspace">Ctrl+Aに続いてSpace</string>
+ <string name="list_camera_ctrla">Ctrl+A</string>
+ <string name="list_camera_esc">Escキー</string>
+ <string name="list_camera_esc_a">Esc+A</string>
<string name="list_camera_none">なし</string>
+ <string name="list_delkey_backspace">Backspace</string>
+ <string name="list_delkey_del">Delete</string>
<string name="delete_message">\'%1$s\'を削除してよろしいですか?</string>
<string name="delete_pos">はい、削除します</string>
<string name="delete_neg">キャンセル</string>
@@ -192,9 +208,6 @@
<string name="terminal_enable_portfoward">ポート転送 %1$s を有効化</string>
<string name="local_shell_unavailable">エラー! この電話機にはローカルシェルがありません.</string>
<string name="notification_text">%1$s でベルが鳴りました</string>
- <string name="upgrade">新しいバージョン</string>
- <string name="upgrade_pos">はい、アップグレードします.</string>
- <string name="upgrade_neg">いいえ、今は行いません.</string>
<string name="no">いいえ</string>
<string name="with_confirmation">確認が必要</string>
<string name="yes">はい</string>
@@ -205,4 +218,11 @@
<string name="color_green">緑</string>
<string name="color_blue">青</string>
<string name="color_gray">グレー</string>
+ <string name="colors_fg_label">FG: %1$d</string>
+ <string name="color_bg_label">BG: %1$d</string>
+ <string name="image_description_connected">接続しました。</string>
+ <string name="image_description_key_is_locked">キーがロックされました。</string>
+ <string name="image_description_toggle_control_character">コントロール文字を切り替えます。</string>
+ <string name="image_description_send_escape_character">エスケープ文字を送信します。</string>
+ <string name="image_description_show_keyboard">キーボードを表示します。</string>
</resources>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
new file mode 100644
index 0000000..d1343d7
--- /dev/null
+++ b/res/values-ka/strings.xml
@@ -0,0 +1,15 @@
+<?xml version='1.0' encoding='utf-8'?>
+<resources>
+ <string name="title_host_editor">ჰოსტის შეცვლა</string>
+ <string name="title_help">დახმარება</string>
+ <string name="title_colors">ფერი</string>
+ <string name="resolve_connect">დაკავშირება</string>
+ <string name="menu_insert">ჰოსტის დამატება</string>
+ <string name="menu_delete">ჰოსტის წაშლა</string>
+ <string name="menu_preferences">პარამეტრები</string>
+ <string name="help_about">ConnectBot-ის შესახებ</string>
+ <string name="help_keyboard">კლავიატურა</string>
+ <string name="pubkey_generate">გენერირება</string>
+ <string name="pubkey_import">იმპორტი</string>
+ <string name="pubkey_delete">გასაღების წაშლა</string>
+</resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 830267a..ae84a1a 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -73,8 +73,6 @@
<string name="pref_fullscreen_summary">콘솔일 때 상태 표시줄을 숨김</string>
<string name="pref_memkeys_title">메모리에 키를 기억</string>
<string name="pref_memkeys_summary">백엔드 서비스가 종료되기 전까지 잠금이 풀린 키들을 메모리에 유지합니다</string>
- <string name="pref_update_title">업데이트 검사</string>
- <string name="pref_update_summary">ConnectBot 업데이트 확인 주기 설정</string>
<string name="pref_conn_persist_title">접속 유지</string>
<string name="pref_conn_persist_summary">백그라운드 동안에도 강제로 연결된 상태 유지</string>
<string name="pref_keymode_title">디렉터리 바로 가기</string>
@@ -97,9 +95,6 @@
<string name="list_keymode_none">사용 안 함</string>
<string name="list_pubkeyids_none">키 사용 안함</string>
<string name="list_pubkeyids_any">잠금키 사용</string>
- <string name="list_update_daily">매일</string>
- <string name="list_update_weekly">매주</string>
- <string name="list_update_never">하지 않음</string>
<string name="hostpref_nickname_title">닉네임</string>
<string name="hostpref_color_title">색상 카테고리</string>
<string name="hostpref_fontsize_title">글꼴 크기 (pt)</string>
@@ -122,9 +117,6 @@
<string name="hostpref_hostname_title">호스트</string>
<string name="hostpref_port_title">포트</string>
<string name="bind_never">연결된 적 없음</string>
- <string name="bind_minutes">%1$s 분 전</string>
- <string name="bind_hours">%1$s 시간 전</string>
- <string name="bind_days">%1$s 일 전</string>
<string name="console_copy_done">클립 보드에 %1$d 바이트가 복사되었습니다.</string>
<string name="console_copy_start">터치 후 드래그 또는\n방향 키로\n복사 영역을 선택</string>
<string name="console_menu_close">닫기</string>
@@ -179,9 +171,6 @@
<string name="terminal_enable_portfoward">포트 포워딩 사용: %1$s</string>
<string name="local_shell_unavailable">실패! 이 폰은 로컬 쉘을 사용할 수 없습니다.</string>
<string name="notification_text">%1$s에서 알림이 있습니다.</string>
- <string name="upgrade">새 버전</string>
- <string name="upgrade_pos">네, 업그레이드</string>
- <string name="upgrade_neg">나중에</string>
<string name="no">아니오</string>
<string name="with_confirmation">확인 작업 수행</string>
<string name="yes">예</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
new file mode 100644
index 0000000..9be11ac
--- /dev/null
+++ b/res/values-mk/strings.xml
@@ -0,0 +1,67 @@
+<?xml version='1.0' encoding='utf-8'?>
+<resources>
+ <string name="app_desc">Едноставен но моќен SSH клиент со отворен код.</string>
+ <string name="service_desc">Ги оддрѓува SSH конекциите и вчитаните pub клучеви</string>
+ <string name="title_hosts_list">Хостови</string>
+ <string name="title_pubkey_list">Pub клучеви</string>
+ <string name="title_host_editor">Промени хост</string>
+ <string name="title_help">Помош</string>
+ <string name="title_colors">Бои</string>
+ <string name="resolve_connect">Поврзи се</string>
+ <string name="menu_insert">Додај хост</string>
+ <string name="menu_delete">Одстрани хост</string>
+ <string name="menu_preferences">Параметри</string>
+ <string name="help_intro">Одбеи тема од подолу наведените за повеќе информации за таа тема.</string>
+ <string name="help_about">За ConnectBot</string>
+ <string name="help_keyboard">Тастатура</string>
+ <string name="pubkey_generate">Генерирај</string>
+ <string name="pubkey_import">Увези</string>
+ <string name="pubkey_delete">Одстрани клуч</string>
+ <string name="pubkey_generating">Се генерира пар на клучеви</string>
+ <string name="pubkey_copy_private">Копирај го приватниот клуч</string>
+ <string name="pubkey_copy_public">Копирај го јавниот клуч</string>
+ <string name="pubkey_list_empty">Притисни мени за да направиш\nили увезеш нов пар на клучеви</string>
+ <string name="pubkey_unknown_format">Непознат тип</string>
+ <string name="pubkey_change_password">Промени лозинка</string>
+ <string name="pubkey_list_pick">Превземи од /sdcard</string>
+ <string name="pubkey_import_parse_problem">Проблем со анализирањето на увезениот приватен клуч</string>
+ <string name="pubkey_unlock">Клуч за отклучување</string>
+ <string name="pubkey_failed_add">Погрешна лозинка за клучот \'%1$s\'. Неуспешна авторизација.</string>
+ <string name="pubkey_memory_load">Вчитај во меморија</string>
+ <string name="pubkey_memory_unload">Извади од меморијата</string>
+ <string name="pubkey_load_on_start">Вчитај клуч при вклучување</string>
+ <string name="pubkey_confirm_use">Побарај потврда пред користење</string>
+ <string name="prompt_nickname">Прекар:</string>
+ <string name="prompt_nickname_hint_pubkey">Мојот работен клуч</string>
+ <string name="prompt_source_port">Изворна порта:</string>
+ <string name="prompt_destination">Дестинација:</string>
+ <string name="prompt_old_password">Стара лозинка:</string>
+ <string name="prompt_password">Лозинка:</string>
+ <string name="prompt_again">(повторно)</string>
+ <string name="prompt_type">Тип:</string>
+ <string name="prompt_password_can_be_blank">Белешка: Лозинката не мора да се впише</string>
+ <string name="prompt_bits">Битови:</string>
+ <string name="prompt_pubkey_password">Лозинка за \'%1$s\'</string>
+ <string name="prompt_allow_agent_to_use_key">Дозволи му на одалечениот хост\nда го искористи \'%1$s\'?</string>
+ <string name="host_verification_failure_warning_header">ПРЕДУПРЕДУВАЊЕ: ИДЕНТИФИКАЦИЈАТА НА ОДАЛЕЧЕНИОТ ХОСТ Е ПРОМЕНЕТА!</string>
+ <string name="host_verification_failure_warning">МОЖНО Е НЕКОЈ ДА ПРАВИ НЕШТО ЛОШО!\nНекој можеби ве прислушкува сега (напад човек во средина)!\nИсто така можно е да се променил клучот на одалечениот хост.</string>
+ <string name="prompt_host_disconnected">Хостот се дисконектираше.\nЗатвори ја сесијата?</string>
+ <string name="prompt_continue_connecting">Дали си сигурен дека\nсакаш да продолжиш со поврзување?</string>
+ <string name="host_authenticity_warning">Автентичноста на \'%1$s\' не може да биде потврдена.</string>
+ <string name="alert_passwords_do_not_match_msg">Лозинките не се совпаѓаат!</string>
+ <string name="alert_wrong_password_msg">Погрешна лозинка!</string>
+ <string name="alert_key_corrupted_msg">Приватниот клуч е корумпиран!</string>
+ <string name="alert_sdcard_absent">СД картичката не е ставена!</string>
+ <string name="button_add">Додај</string>
+ <string name="button_change">Промени</string>
+ <string name="button_generate">Направи клуч</string>
+ <string name="button_resize">Промени големина</string>
+ <string name="alert_disconnect_msg">Врската е изгубена</string>
+ <string name="pref_emulation_category">Емулација на терминал</string>
+ <string name="pref_emulation_title">Вид на емулација</string>
+ <string name="pref_emulation_summary">Вид на емулација на терминал да се користи за PTY врски</string>
+ <string name="pref_ui_category">Кориснички интерфејс</string>
+ <string name="pref_fullscreen_title">На цел екран</string>
+ <string name="pref_fullscreen_summary">Сокриј ја статусната трака додека си во конзола</string>
+ <string name="pref_memkeys_title">Запомни ги клучевите во меморија</string>
+</resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 13a017a..f29fca6 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -1,9 +1,10 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
- <string name="app_desc">Enkel, kraftig open-source SSH-klient</string>
+ <string name="app_desc">Enkel og omfattende SSH-klient med åpen kildekode.</string>
+ <string name="service_desc">Opprettholder SSH-tilkoblinger og innlastede offentlige nøkler</string>
<string name="title_hosts_list">Verter</string>
<string name="title_pubkey_list">Offentlige nøkler</string>
- <string name="title_port_forwards_list">Videresendte porter</string>
+ <string name="title_port_forwards_list">Portvideresending</string>
<string name="title_host_editor">Rediger vert</string>
<string name="title_help">Hjelp</string>
<string name="title_colors">Farger</string>
@@ -12,45 +13,50 @@
<string name="menu_insert">Legg til vert</string>
<string name="menu_delete">Slett vert</string>
<string name="menu_preferences">Innstillinger</string>
- <string name="help_intro">Velg et emne nedenfor for mer informasjon om et bestemt emne.</string>
+ <string name="help_intro">Velg en overskrift nedenfor for mer informasjon om et bestemt emne.</string>
<string name="help_about">Om ConnectBot</string>
<string name="help_keyboard">Tastatur</string>
- <string name="pubkey_generate">Lag</string>
+ <string name="pubkey_generate">Generer</string>
<string name="pubkey_import">Importer</string>
<string name="pubkey_delete">Slett nøkkel</string>
<string name="pubkey_gather_entropy">Samler entropi</string>
- <string name="pubkey_touch_prompt">Ta i denne boksen for å samle tilfeldigheter: %1$d%% ferdig</string>
- <string name="pubkey_touch_hint">For å forsikre tilfeldighet under nøkkel generasjon, flytt fingeren tilfeldig over boksen nedenfor.</string>
+ <string name="pubkey_touch_prompt">Berør denne boksen for å samle tilfeldige tall: %1$d%% ferdig</string>
+ <string name="pubkey_touch_hint">Beveg fingeren over boksen nedenfor for å sikre vilkårlighet ved generering av nøkkel.</string>
<string name="pubkey_generating">Genererer nøkkelpar...</string>
<string name="pubkey_copy_private">Kopier privat nøkkel</string>
<string name="pubkey_copy_public">Kopier offentlig nøkkel</string>
- <string name="pubkey_list_empty">Trykk Meny for å lage/eller importere nøkkel par</string>
+ <string name="pubkey_list_empty">Trykk \"Meny\" for å opprette\neller importere nøkkelpar.</string>
<string name="pubkey_unknown_format">Ukjent format</string>
<string name="pubkey_change_password">Endre passord</string>
- <string name="pubkey_list_pick">Velg fra /sdcard</string>
- <string name="pubkey_import_parse_problem">Feil ved innlesing av privat nøkkel</string>
+ <string name="pubkey_list_pick">Hent fra /sdcard</string>
+ <string name="pubkey_import_parse_problem">Feil ved innlasting av privat nøkkel</string>
<string name="pubkey_unlock">Frigjør nøkkel</string>
- <string name="pubkey_failed_add">Feil passord for nøkkel \'%1$s\'. Autentisering feilet.</string>
+ <string name="pubkey_failed_add">Feil passord for nøkkel \'%1$s\'. Autentisering mislyktes.</string>
<string name="pubkey_memory_load">Last inn i minne</string>
<string name="pubkey_memory_unload">Last ut av minne</string>
<string name="pubkey_load_on_start">Last nøkkel ved start</string>
<string name="pubkey_confirm_use">Godkjenn før bruk</string>
- <string name="portforward_list_empty">Trykk Meny for å videresende porter</string>
+ <string name="portforward_list_empty">Trykk \"Meny\" for å opprette\nportvideresending.</string>
<string name="portforward_edit">Rediger videresendte porter</string>
<string name="portforward_delete">Slett videresendte porter</string>
<string name="prompt_nickname">Kallenavn:</string>
- <string name="prompt_nickname_hint_pubkey">Min arbeids nøkkel</string>
+ <string name="prompt_nickname_hint_pubkey">Min arbeidsnøkkel</string>
<string name="prompt_source_port">Kildeport:</string>
- <string name="prompt_destination">Destinasjon</string>
- <string name="prompt_old_password">Gammelt passord:</string>
+ <string name="prompt_destination">Mål:</string>
+ <string name="prompt_old_password">Tidligere passord:</string>
<string name="prompt_password">Passord:</string>
<string name="prompt_again">(en gang til)</string>
- <string name="prompt_password_can_be_blank">NB: Passord kan være tomt</string>
+ <string name="prompt_type">Type:</string>
+ <string name="prompt_password_can_be_blank">NB: passord kan stå tomt</string>
+ <string name="prompt_bits">Bits:</string>
<string name="prompt_pubkey_password">Passord for nøkkel \'%1$s\'</string>
<string name="prompt_allow_agent_to_use_key">Godta ekstern vert til\nå bruke nøkkel \'%1$s\'?</string>
<string name="host_verification_failure_warning_header">ADVARSEL: EKSTERN VERT IDENTIFIKASJON HAR BLITT ENDRET!</string>
+ <string name="host_verification_failure_warning">DET KAN VÆRE UGLER I MOSEN!\nNoen kan lytte på deg akkurat nå (et mellommann-angrep)!\nMen det er også mulig at vertsnøkkelen har blitt endret.</string>
<string name="prompt_host_disconnected">Vert har koblet fra.\nLukk sesjon?</string>
<string name="prompt_continue_connecting">Er du sikker på at\ndu vil koble til?</string>
+ <string name="host_authenticity_warning">Kan ikke bekrefte integriteten til \'%1$s\'.</string>
+ <string name="host_fingerprint">Nøkkelfingeravtrykk til vert %1$s er %2$s</string>
<string name="alert_passwords_do_not_match_msg">Passordene er ikke like!</string>
<string name="alert_wrong_password_msg">Feil passord!</string>
<string name="alert_key_corrupted_msg">Privat nøkkel ser ut til å være korrupt!</string>
@@ -60,61 +66,105 @@
<string name="button_generate">Generer nøkkel</string>
<string name="button_resize">Endre størrelse</string>
<string name="alert_disconnect_msg">Mistet tilkobling</string>
+ <string name="msg_copyright">Copyright © 2007-2008 Kenny Root http://the-b.org/, Jeffrey Sharkey http://jsharkey.org/</string>
<string name="pref_emulation_category">Terminalemulering</string>
+ <string name="pref_emulation_title">Etterligningsmodus</string>
+ <string name="pref_emulation_summary">Etterligningsmodus for terminal for PTY-tilkoblinger</string>
+ <string name="pref_scrollback_title">Husk skjermtekst</string>
+ <string name="pref_scrollback_summary">Mengde skjermtekst som skal beholdes i minnet for hvert konsollvindu</string>
<string name="pref_ui_category">Brukergrensesnitt</string>
+ <string name="pref_rotation_title">Rotasjonsretning</string>
+ <string name="pref_rotation_summary">Hvordan rotasjonsretning endres når tastatur skyves inn/ut</string>
<string name="pref_fullscreen_title">Full skjerm</string>
<string name="pref_fullscreen_summary">Gjem statuslinje mens du er i konsoll</string>
+ <string name="pref_memkeys_title">Lagre nøkler</string>
+ <string name="pref_memkeys_summary">Husk opplåste nøkler helt til programmet avsluttes</string>
+ <string name="pref_conn_persist_title">Oppretthold forbindelser</string>
+ <string name="pref_conn_persist_summary">Oppretthold forbindelser selv når programmet kjører i bakgrunnen</string>
+ <string name="pref_keymode_title">Hurtigtaster for mapper</string>
+ <string name="pref_keymode_summary">Velg hvordan Alt- og Skift-tasten skal brukes for \'/\' og Tab</string>
+ <string name="pref_camera_title">Snarvei til kamera</string>
<string name="pref_camera_summary">Velg hvilken snarvei som skal benyttes når kameraknapp trykkes</string>
<string name="pref_keepalive_title">Hold skjerm våken</string>
<string name="pref_keepalive_summary">Unngå at skjermen slås av når du jobber konsoll</string>
<string name="pref_wifilock_title">Hold Wi-Fi aktiv</string>
+ <string name="pref_wifilock_summary">Hindre Wi-Fi å koble fra når en økt er aktiv</string>
+ <string name="pref_bumpyarrows_title">Vibrerende piler</string>
+ <string name="pref_bumpyarrows_summary">Vibrer når pilkommandoer sendes med styrekule; nyttig for trege forbindelser</string>
<string name="pref_bell_category">Terminalklokke</string>
<string name="pref_bell_title">Varselklokke</string>
<string name="pref_bell_volume_title">Klokkevolum</string>
<string name="pref_bell_vibrate_title">Vibrere med bjelle</string>
+ <string name="pref_bell_notification_title">Bakgrunnsvarsling</string>
+ <string name="pref_bell_notification_summary">Vis varsling når en terminal i bakgrunnen gir et klokkevarsel.</string>
+ <string name="list_keymode_right">Bruk knapper på høyre side</string>
+ <string name="list_keymode_left">Bruk knapper på venstre side</string>
<string name="list_keymode_none">Deaktiver</string>
<string name="list_pubkeyids_none">Ikke bruk nøkler</string>
- <string name="list_update_daily">Daglig</string>
- <string name="list_update_weekly">Ukentlig</string>
- <string name="list_update_never">Aldri</string>
+ <string name="list_pubkeyids_any">Bruk en vilkårlig ulåst tast</string>
<string name="hostpref_nickname_title">Kallenavn</string>
+ <string name="hostpref_color_title">Fargeinndeling</string>
+ <string name="hostpref_fontsize_title">Skriftstørrelse (pt)</string>
+ <string name="hostpref_pubkeyid_title">Bruk offentlig nøkkel som godkjenning</string>
+ <string name="hostpref_authagent_title">Bruk SSH auth-agent</string>
+ <string name="hostpref_postlogin_title">Kommandoer etter innlogging</string>
+ <string name="hostpref_postlogin_summary">Kommandoer som kjøres på tilkoblet tjener etter vellykket autentisering</string>
<string name="hostpref_compression_title">Komprimering</string>
<string name="hostpref_compression_summary">Dette kan hjelpe på trege nettverk</string>
<string name="hostpref_wantsession_title">Start skallsesjon</string>
+ <string name="hostpref_wantsession_summary">Deaktiver denne innstillingen og benytt denne kun for portvideresending</string>
+ <string name="hostpref_stayconnected_title">Oppretthold forbindelse</string>
+ <string name="hostpref_stayconnected_summary">Forsøk å koble til på nytt ved frakobling</string>
+ <string name="hostpref_delkey_title">DEL-knapp</string>
+ <string name="hostpref_delkey_summary">Tastekode som sendes når DEL trykkes</string>
<string name="hostpref_encoding_title">Tegnkoding</string>
<string name="hostpref_encoding_summary">Tegnkoding for verten</string>
<string name="hostpref_connection_category">Tilkoblingsinnstillinger</string>
<string name="hostpref_username_title">Brukernavn</string>
<string name="hostpref_hostname_title">Vert</string>
+ <string name="hostpref_port_title">Port</string>
<string name="bind_never">Aldri tilkoblet</string>
- <string name="bind_minutes">%1$s minutt siden</string>
- <string name="bind_hours">%1$s timer siden</string>
- <string name="bind_days">%1$s dager siden</string>
<string name="console_copy_done">Kopierte %1$d bytes til utklippstavle</string>
+ <string name="console_copy_start">Trykk og dra, eller\nbruk styrekule for å\nvelge område å\nkopiere fra</string>
<string name="console_menu_close">Lukk</string>
<string name="console_menu_copy">Kopier</string>
<string name="console_menu_paste">Lim inn</string>
+ <string name="console_menu_portforwards">Portvideresending</string>
<string name="console_menu_resize">Tving størrelse</string>
+ <string name="console_menu_urlscan">URL-søk</string>
<string name="button_yes">Ja</string>
<string name="button_no">Nei</string>
<string name="portforward_local">Lokal</string>
<string name="portforward_remote">Ekstern</string>
<string name="portforward_dynamic">Dynamisk (SOCKS)</string>
+ <string name="portforward_pos">Opprett portvideresending</string>
+ <string name="portforward_done">Vellykket oppretting av portvideresending</string>
+ <string name="portforward_problem">Mislyktes å opprette portvideresending. Bruker du porter under 1024, eller er denne porten allerede tatt?</string>
+ <string name="portforward_menu_add">Legg til portvideresending</string>
<string name="hint_userhost">bruker\@vert</string>
<string name="list_format_error">Use the format %1$s</string>
<string name="format_username">brukernavn</string>
+ <string name="format_hostname">vertsnavn</string>
+ <string name="format_port">port</string>
+ <string name="list_menu_pubkeys">Behandle offentlige nøkler</string>
<string name="list_menu_sortcolor">Sorter etter farge</string>
<string name="list_menu_sortname">Sorter etter navn</string>
<string name="list_menu_settings">Innstillinger</string>
<string name="list_host_disconnect">Koble fra</string>
<string name="list_host_edit">Rediger vert</string>
+ <string name="list_host_portforwards">Rediger portvidersendinger</string>
<string name="list_host_delete">Slett vert</string>
+ <string name="list_host_empty">Bruk hurtigtilkoblingsboksen\nunder for å koble til en vert.</string>
<string name="list_rotation_default">Forvalg</string>
<string name="list_rotation_land">Tving landskap</string>
<string name="list_rotation_port">Tving portrett</string>
<string name="list_rotation_auto">Automatisk</string>
<string name="list_camera_ctrlaspace">Ctrl+A og mellomrom</string>
+ <string name="list_camera_ctrla">Ctrl+A</string>
+ <string name="list_camera_esc">Esc</string>
+ <string name="list_camera_esc_a">Esc+A</string>
<string name="list_camera_none">Ingen</string>
+ <string name="list_delkey_backspace">Tilbaketast</string>
<string name="list_delkey_del">Slett</string>
<string name="delete_message">Er du sikker på at du vil slette \'%1$s\'?</string>
<string name="delete_pos">Ja, slett</string>
@@ -123,16 +173,26 @@
<string name="wizard_next">Neste</string>
<string name="wizard_back">Tilbake</string>
<string name="terminal_no_hosts_connected">Ingen verter tilkoblet for øyeblikket</string>
+ <string name="terminal_connecting">Kobler til %1$s:%2$d via %3$s</string>
+ <string name="terminal_sucess">Verifiserte vert \'%1$s\' sin nøkkel: %2$s</string>
<string name="terminal_failed">Verifisering av vertsnøkkelen feilet.</string>
+ <string name="terminal_using_s2c_algorithm">Algoritme fra tjener til klient: %1$s %2$s</string>
+ <string name="terminal_using_c2s_algorithm">Algoritme fra klient til tjener: %1$s %2$s</string>
+ <string name="terminal_using_algorithm">Bruker algoritme: %1$s %2$s</string>
<string name="terminal_auth">Forsøker å autentisere</string>
<string name="terminal_auth_pass">Forsøker \'passord\'-autentisering</string>
<string name="terminal_auth_pass_fail">Autentiseringsmetode \'passord\' feilet</string>
+ <string name="terminal_auth_pubkey_any">Forsøker \'publickey\'-autentisering med lagrede offentlige nøkler</string>
+ <string name="terminal_auth_pubkey_invalid">Valgt offentlig nøkkel er ugyldig, forsøk å velge en annen nøkkel i vertsvinduet.</string>
+ <string name="terminal_auth_pubkey_specific">Forsøker \'publickey\'-autentisering med angitt offentlig nøkkel</string>
+ <string name="terminal_auth_pubkey_fail">Autentisering med \'publickey\' og nøkkel \'%1$s\' mislyktes</string>
+ <string name="terminal_auth_ki">Forsøker \'keyboard-interactive\' autentisering</string>
+ <string name="terminal_auth_ki_fail">Autentiseringsmetoden \'keyboard-interactive\' mislyktes</string>
+ <string name="terminal_auth_fail">[Din vert støtter ikke autentiseringsmetodene \'password\' eller \'keyboard-interactive\'.]</string>
<string name="terminal_no_session">Sesjonen vil ikke bli startet grunnet vertsinnstillinger.</string>
+ <string name="terminal_enable_portfoward">Aktiver portvideresending: %1$s</string>
<string name="local_shell_unavailable">Feil! Lokalt skall er utilgjengelig på denne telefonen.</string>
<string name="notification_text">%1$s ønsker din oppmerksomhet.</string>
- <string name="upgrade">Ny versjon</string>
- <string name="upgrade_pos">Ja, oppgrader</string>
- <string name="upgrade_neg">Ikke akkurat nå</string>
<string name="no">Nei</string>
<string name="with_confirmation">Med bekreftelse</string>
<string name="yes">Ja</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 5c8487c..ac9125b 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -79,8 +79,6 @@
<string name="pref_fullscreen_summary">Verberg status balk terwijl in console</string>
<string name="pref_memkeys_title">Onthoud sleutels in geheugen</string>
<string name="pref_memkeys_summary">Houd ontgrendelde sleutels in geheugen tot achtergrond service is gestopt</string>
- <string name="pref_update_title">Update controle</string>
- <string name="pref_update_summary">Stel de maximale frequentie in om te controleren voor ConnectBot updates</string>
<string name="pref_conn_persist_title">Behoud verbindingen</string>
<string name="pref_conn_persist_summary">Forceer dat de verbindingen actief blijven op de achtergrond</string>
<string name="pref_keymode_title">Folder snelkoppelingen</string>
@@ -104,9 +102,6 @@
<string name="list_keymode_none">Schakel uit</string>
<string name="list_pubkeyids_none">Gebruik sleutels niet</string>
<string name="list_pubkeyids_any">Gebruik een willekeurige ontgrendelde sleutel</string>
- <string name="list_update_daily">Dagelijks</string>
- <string name="list_update_weekly">Wekelijks</string>
- <string name="list_update_never">Nooit</string>
<string name="hostpref_nickname_title">Bijnaam</string>
<string name="hostpref_color_title">Kleur catogorie</string>
<string name="hostpref_fontsize_title">Lettergrootte</string>
@@ -129,9 +124,6 @@
<string name="hostpref_hostname_title">Host</string>
<string name="hostpref_port_title">Poort</string>
<string name="bind_never">Nooit verbonden</string>
- <string name="bind_minutes">%1$s minuten geleden</string>
- <string name="bind_hours">%1$s uren geleden</string>
- <string name="bind_days">%1$s dagen geleden</string>
<string name="console_copy_done">%1$d bytes gekopieerd naar het klembord</string>
<string name="console_copy_start">Tik en sleep\nof gebruik directioneel pad\nom velden te selecteren omte kopiëren</string>
<string name="console_menu_close">Sluit</string>
@@ -201,9 +193,6 @@
<string name="terminal_enable_portfoward">Activeer port forward: %1$s</string>
<string name="local_shell_unavailable">Mislukking! Lokale shell is niet beschikbaar op deze telefoon.</string>
<string name="notification_text">%1$s wil je aandacht.</string>
- <string name="upgrade">Nieuwe versie</string>
- <string name="upgrade_pos">Ja, upgrade</string>
- <string name="upgrade_neg">Nog niet</string>
<string name="no">Nee</string>
<string name="with_confirmation">Met toestemming</string>
<string name="yes">Ja</string>
diff --git a/res/values-oc/strings.xml b/res/values-oc/strings.xml
index d845b78..aad61b6 100644
--- a/res/values-oc/strings.xml
+++ b/res/values-oc/strings.xml
@@ -43,13 +43,9 @@
<string name="alert_disconnect_msg">Connexion perduda</string>
<string name="pref_ui_category">Interfàcia d\'utilizaire</string>
<string name="pref_fullscreen_title">Ecran complet</string>
- <string name="pref_update_title">Verificar las mesas a jorn</string>
<string name="pref_conn_persist_title">Connexions persistentas</string>
<string name="pref_keymode_title">Acorchis de navigacion</string>
<string name="list_keymode_none">Desactivar</string>
- <string name="list_update_daily">Cada jorn</string>
- <string name="list_update_weekly">Cada setmana</string>
- <string name="list_update_never">Pas jamai</string>
<string name="hostpref_nickname_title">Escais</string>
<string name="hostpref_encoding_title">Encodatge</string>
<string name="hostpref_connection_category">Paramètre de connexion</string>
@@ -79,8 +75,6 @@
<string name="wizard_next">Seguent</string>
<string name="wizard_back">Precedent</string>
<string name="terminal_connecting">Connexion a %1$s:%2$d via %3$s</string>
- <string name="upgrade">Version novèla</string>
- <string name="upgrade_neg">Pas ara</string>
<string name="no">Non</string>
<string name="with_confirmation">Aprèp confirmacion</string>
<string name="yes">Òc</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 61d736a..f370ad5 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -1,21 +1,23 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="app_desc">Prosty, wszechstronny i otwarty klient SSH</string>
+ <string name="service_desc">Zarządza połączeniami SSH i załadowanymi kluczami publicznymi</string>
<string name="title_hosts_list">Hosty</string>
<string name="title_pubkey_list">Klucze publiczne</string>
<string name="title_port_forwards_list">Przekierowania portów</string>
- <string name="title_host_editor">Edytuj hosta</string>
+ <string name="title_host_editor">Edytuj host</string>
<string name="title_help">Pomoc</string>
<string name="title_colors">Kolory</string>
+ <string name="title_color_picker">Wybór koloru</string>
<string name="resolve_connect">Połącz</string>
<string name="resolve_entropy">Zbierz dane losowe</string>
- <string name="menu_insert">Dodaj hosta</string>
- <string name="menu_delete">Usuń hosta</string>
- <string name="menu_preferences">Preferencje</string>
+ <string name="menu_insert">Dodaj host</string>
+ <string name="menu_delete">Usuń host</string>
+ <string name="menu_preferences">Ustawienia</string>
<string name="help_intro">Proszę wybrać temat poniżej aby uzyskać więcej informacji na konkretny temat.</string>
<string name="help_about">O ConnectBot</string>
<string name="help_keyboard">Klawiatura</string>
- <string name="pubkey_generate">Generuj</string>
+ <string name="pubkey_generate">Wygeneruj</string>
<string name="pubkey_import">Importuj</string>
<string name="pubkey_delete">Usuń klucz</string>
<string name="pubkey_gather_entropy">Zbieranie danych losowych</string>
@@ -24,21 +26,22 @@
<string name="pubkey_generating">Generowanie pary kluczy...</string>
<string name="pubkey_copy_private">Kopiuj klucz prywatny</string>
<string name="pubkey_copy_public">Kopiuj klucz publiczny</string>
- <string name="pubkey_list_empty">Naciśnij Menu, by utworzyć\nlub zaimportować parę kluczy.</string>
+ <string name="pubkey_list_empty">Naciśnij \"Menu\", by utworzyć\nlub zaimportować parę kluczy.</string>
<string name="pubkey_unknown_format">Nieznany format</string>
<string name="pubkey_change_password">Zmień hasło</string>
<string name="pubkey_list_pick">Wybierz z karty SD</string>
<string name="pubkey_import_parse_problem">Niewłaściwy format importowanego klucza prywatnego</string>
<string name="pubkey_unlock">Odblokuj klucz</string>
+ <string name="pubkey_failed_add">Nieprawidłowe hasło dla klucza \'%1$s\'. Uwierzytelnienie nie powiodło się.</string>
<string name="pubkey_memory_load">Załaduj do pamięci</string>
<string name="pubkey_memory_unload">Usuń z pamięci</string>
<string name="pubkey_load_on_start">Załaduj klucz przy uruchamianiu</string>
<string name="pubkey_confirm_use">Potwierdź przed użyciem</string>
- <string name="portforward_list_empty">Naciśnij Menu,\nżeby stworzyć przekierowanie portu.</string>
+ <string name="portforward_list_empty">Naciśnij \"Menu\",\nżeby stworzyć przekierowanie portu.</string>
<string name="portforward_edit">Edytuj przekierowanie portu</string>
<string name="portforward_delete">Usuń przekierowanie portu</string>
<string name="prompt_nickname">Skrócona nazwa:</string>
- <string name="prompt_nickname_hint_pubkey">Klucz roboczy</string>
+ <string name="prompt_nickname_hint_pubkey">Klucz służbowy</string>
<string name="prompt_source_port">Port żródłowy</string>
<string name="prompt_destination">Host docelowy:</string>
<string name="prompt_old_password">Stare hasło:</string>
@@ -49,12 +52,12 @@
<string name="prompt_bits">Bity:</string>
<string name="prompt_pubkey_password">Proszę podać hasło dla klucza \'%1$s\'</string>
<string name="prompt_allow_agent_to_use_key">Zezwolić zdalnemu hostowi na\nużycie klucza \'%1$s\'?</string>
- <string name="host_verification_failure_warning_header">OSTRZEŻENIE: DANE IDENTYFIKACYJNE ZDALNEGO HOSTA ZMIENIŁY SIĘ!</string>
- <string name="host_verification_failure_warning">JEST MOŻLIWE, ŻE PADŁEŚ OFIARĄ ATAKU!\nMożliwe, że jest to atak typu: man-in-the-middle!\nAle też jest możliwe, że zmienił się tylko klucz hosta.</string>
- <string name="prompt_host_disconnected">Host rozłączył się.\n Zamknąć sesję?</string>
- <string name="prompt_continue_connecting">Czy na pewno chcesz\n kontynuować połączenie?</string>
- <string name="host_authenticity_warning">Autentyczność hosta \'%1$s\' nie może zostać ustalona.</string>
- <string name="host_fingerprint">Odcisk hosta %1$s to %2$s</string>
+ <string name="host_verification_failure_warning_header">OSTRZEŻENIE: DANE IDENTYFIKACYJNE ZDALNEGO HOSTU ZMIENIŁY SIĘ!</string>
+ <string name="host_verification_failure_warning">MOŻLIWE, ŻE STAŁEŚ SIĘ OFIARĄ ATAKU!\nPrawdopodobnie jest to atak: man-in-the-middle!\nJednak możliwe jest to, że zmienił się tylko klucz hostu.</string>
+ <string name="prompt_host_disconnected">Host rozłączył się.\nZamknąć sesję?</string>
+ <string name="prompt_continue_connecting">Czy na pewno chcesz\nkontynuować połączenie?</string>
+ <string name="host_authenticity_warning">Autentyczność hostu \'%1$s\' nie może zostać ustalona.</string>
+ <string name="host_fingerprint">Odcisk hostu %1$s to %2$s</string>
<string name="alert_passwords_do_not_match_msg">Hasła nie zgadzają się!</string>
<string name="alert_wrong_password_msg">Nieprawidłowe hasło!</string>
<string name="alert_key_corrupted_msg">Klucz prywatny wydaje się być uszkodzony!</string>
@@ -64,20 +67,20 @@
<string name="button_generate">Generuj klucz</string>
<string name="button_resize">Zmień rozmiar</string>
<string name="alert_disconnect_msg">Połączenie utracone</string>
+ <string name="msg_copyright">Copyright © 2007-2008 Kenny Root http://the-b.org/, Jeffrey Sharkey http://jsharkey.org/</string>
<string name="pref_emulation_category">Emulacja terminalu</string>
<string name="pref_emulation_title">Tryb emulacji</string>
<string name="pref_emulation_summary">Emulacja terminalu dla połączeń PTY</string>
- <string name="pref_scrollback_title">Rozmiar bufora przewijania</string>
- <string name="pref_scrollback_summary">Rozmiar pamięci dla bufora przewijania każdej konsoli</string>
+ <string name="pref_scrollback_title">Rozmiar buforu przewijania</string>
+ <string name="pref_scrollback_summary">Rozmiar pamięci dla buforu przewijania dla każdej konsoli</string>
<string name="pref_ui_category">Interfejs użytkownika</string>
<string name="pref_rotation_title">Sposób orientacji ekranu</string>
<string name="pref_rotation_summary">Sposób orientacji ekranu przy wysuniętej/wsuniętej klawiaturze</string>
<string name="pref_fullscreen_title">Pełny ekran</string>
<string name="pref_fullscreen_summary">Ukryj pasek statusu podczas pracy w konsoli</string>
+ <string name="pref_keyboard_category">Klawiatura</string>
<string name="pref_memkeys_title">Zapamiętaj klucze w pamięci</string>
<string name="pref_memkeys_summary">Trzymaj odblokowane klucze w pamięci dopóki usługa się nie rozłączy</string>
- <string name="pref_update_title">Sprawdź aktualizacje</string>
- <string name="pref_update_summary">Ustawienie częstości sprawdzania aktualizacji</string>
<string name="pref_conn_persist_title">Utrzymuj połącznia</string>
<string name="pref_conn_persist_summary">Wymuś utrzymanie połączeń, gdy aplikacja działa w tle</string>
<string name="pref_keymode_title">Skróty klawiaturowe</string>
@@ -87,7 +90,7 @@
<string name="pref_keepalive_title">Wstrzymaj uśpienie ekranu</string>
<string name="pref_keepalive_summary">Wstrzymaj uśpienie ekranu kiedy konsola jest aktywna</string>
<string name="pref_wifilock_title">Wstrzymaj usypianie Wi-Fi</string>
- <string name="pref_wifilock_summary">Wstrzymaj usypianie Wi-Fi kiedy sesja jest aktywna</string>
+ <string name="pref_wifilock_summary">Wstrzymaj usypianie Wi-Fi gdy sesja jest aktywna</string>
<string name="pref_bumpyarrows_title">Podskakujące strzałki</string>
<string name="pref_bumpyarrows_summary">Wibruj kiedy emulowane są klawisze strzałek; użyteczne dla wolnych połączeń</string>
<string name="pref_bell_category">Brzęczyk terminalu</string>
@@ -101,14 +104,11 @@
<string name="list_keymode_none">Wyłącz</string>
<string name="list_pubkeyids_none">Nie używaj kluczy</string>
<string name="list_pubkeyids_any">Użyj dowolnego odblokowanego klucza</string>
- <string name="list_update_daily">Dziennie</string>
- <string name="list_update_weekly">Tygodniowo</string>
- <string name="list_update_never">Nigdy</string>
<string name="hostpref_nickname_title">Nazwa skrócona</string>
<string name="hostpref_color_title">Kolor kategorii</string>
<string name="hostpref_fontsize_title">Rozmiar czcionki</string>
<string name="hostpref_pubkeyid_title">Używaj kluczy publicznych do uwierzytelnienia</string>
- <string name="hostpref_authagent_title">Używaj agenta autoryzacji SSH</string>
+ <string name="hostpref_authagent_title">Używaj agenta SSH dla uwierzytelniania</string>
<string name="hostpref_postlogin_title">Wykonaj po zalogowaniu</string>
<string name="hostpref_postlogin_summary">Komendy do wykonania po stronie serwera po zalogowaniu</string>
<string name="hostpref_compression_title">Kompresja</string>
@@ -120,13 +120,12 @@
<string name="hostpref_delkey_title">Klawisz DEL</string>
<string name="hostpref_delkey_summary">Kod klawisza wysyłany przy wciśnięciu DEL</string>
<string name="hostpref_encoding_title">Kodowanie</string>
- <string name="hostpref_encoding_summary">Kodowanie znaków dla hosta</string>
+ <string name="hostpref_encoding_summary">Kodowanie znaków dla hostu</string>
<string name="hostpref_connection_category">Ustawienia połączenia</string>
<string name="hostpref_username_title">Nazwa użytkownika</string>
+ <string name="hostpref_hostname_title">Host</string>
+ <string name="hostpref_port_title">Port</string>
<string name="bind_never">Nigdy nie połączono</string>
- <string name="bind_minutes">%1$s minut temu</string>
- <string name="bind_hours">%1$s godzin temu</string>
- <string name="bind_days">%1$s dni temu</string>
<string name="console_copy_done">Skopiowano %1$d bajtów do schowka</string>
<string name="console_copy_start">Dotknij i przeciągnij\nlub użyj trackball\'a\naby zaznaczyć obszar do skopiowania</string>
<string name="console_menu_close">Zamknij</string>
@@ -144,25 +143,30 @@
<string name="portforward_done">Pomyślnie utworzono przekierowanie portu</string>
<string name="portforward_problem">Wystąpił problem podczas tworzenia przekierowania, być może użyłeś portu niższego niż 1024 lub port jest już używany?</string>
<string name="portforward_menu_add">Dodaj przekierowanie portu</string>
- <string name="hint_userhost">użytkownik\@nazwa_hosta</string>
+ <string name="hint_userhost">użytkownik\@host</string>
<string name="list_format_error">Proszę użyć formatu %1$s</string>
<string name="format_username">Nazwa użytkownika</string>
- <string name="format_hostname">nazwa_hosta</string>
+ <string name="format_hostname">nazwa_hostu</string>
+ <string name="format_port">port</string>
<string name="list_menu_pubkeys">Zarządzaj kluczami publicznymi</string>
<string name="list_menu_sortcolor">Sortuj według koloru</string>
<string name="list_menu_sortname">Sortuj według nazwy</string>
<string name="list_menu_settings">Ustawienia</string>
<string name="list_host_disconnect">Rozłącz</string>
- <string name="list_host_edit">Edytuj hosta</string>
+ <string name="list_host_edit">Edytuj host</string>
<string name="list_host_portforwards">Edytuj przekierowania portów</string>
- <string name="list_host_delete">Usuń hosta</string>
+ <string name="list_host_delete">Usuń host</string>
<string name="list_host_empty">Użyj pola szybkiego połączenia\nponiżej, żeby połączyć się z hostem.</string>
<string name="list_rotation_default">Domyślne</string>
<string name="list_rotation_land">Wymuś orientację poziomą</string>
<string name="list_rotation_port">Wymuś orientację pionową</string>
<string name="list_rotation_auto">Automatycznie</string>
<string name="list_camera_ctrlaspace">Ctrl+A następnie Spacja</string>
- <string name="list_camera_none">Żaden</string>
+ <string name="list_camera_ctrla">Ctrl+A</string>
+ <string name="list_camera_esc">Esc</string>
+ <string name="list_camera_esc_a">Esc+A</string>
+ <string name="list_camera_none">Brak akcji</string>
+ <string name="list_delkey_backspace">Backspace</string>
<string name="list_delkey_del">Usuń</string>
<string name="delete_message">Jesteś pewien, że chcesz usunąć \'%1$s\'?</string>
<string name="delete_pos">Tak, usuń</string>
@@ -172,36 +176,40 @@
<string name="wizard_back">Wróć</string>
<string name="terminal_no_hosts_connected">Żaden host nie jest obecnie połączony</string>
<string name="terminal_connecting">Łączenie z hostem %1$s:%2$d przez %3$s</string>
- <string name="terminal_sucess">Zweryfikowany klucz hosta \'%1$s\': %2$s</string>
- <string name="terminal_failed">Weryfikacja klucza hosta nie powiodła się.</string>
+ <string name="terminal_sucess">Host \'%1$s \' zweryfikowany kluczem: %2$s</string>
+ <string name="terminal_failed">Weryfikacja klucza dla hostu nie powiodła się.</string>
<string name="terminal_using_s2c_algorithm">Algorytm serwer-klient: %1$s %2$s</string>
<string name="terminal_using_c2s_algorithm">Algorytm klient-serwer: %1$s %2$s</string>
- <string name="terminal_using_algorithm">Używany algorytm: %1$s %2$s</string>
+ <string name="terminal_using_algorithm">Użyty algorytm: %1$s %2$s</string>
<string name="terminal_auth">Próba uwierzytelnienia</string>
- <string name="terminal_auth_pass">Próba autoryzacji za pomocą hasła</string>
- <string name="terminal_auth_pass_fail">Autoryzacja za pomocą klucza nie powiodła się</string>
- <string name="terminal_auth_pubkey_any">Próba autentykacji za pomocą dowolnego zapamiętanego klucza publicznego</string>
- <string name="terminal_auth_pubkey_invalid">Wybrany klucz publiczny jest nieprawidłowy, spróbuj wybrać inny klucz w edycji hosta</string>
- <string name="terminal_auth_pubkey_specific">Próba autoryzacji za pomocą wybranego klucza publicznego</string>
- <string name="terminal_auth_pubkey_fail">Autoryzacja metodą klucza publicznego z kluczem \'%1$s\' nie powiodła się</string>
- <string name="terminal_auth_ki">Próba autoryzacji za pomocą klawiatury</string>
- <string name="terminal_auth_ki_fail">Autoryzacja za pomocą klawiatury nie powiodła się</string>
- <string name="terminal_auth_fail">[Twój host nie obsługuje autoryzacji za pomocą klawiatury lub hasła]</string>
- <string name="terminal_no_session">Sesja nie zostanie rozpoczęta z powodu ustawień hosta</string>
+ <string name="terminal_auth_pass">Próba uwierzytelnienia za pomocą hasła</string>
+ <string name="terminal_auth_pass_fail">Uwierzytelnienie hasłem nie powiodło się</string>
+ <string name="terminal_auth_pubkey_any">Próba uwierzytelnienia za pomocą dowolnego zapamiętanego klucza publicznego</string>
+ <string name="terminal_auth_pubkey_invalid">Wybrany klucz publiczny jest nieprawidłowy, spróbuj wybrać inny w ustawieniach hostu</string>
+ <string name="terminal_auth_pubkey_specific">"Próba uwierzytelnienia za pomocą wybranego klucza publicznego"</string>
+ <string name="terminal_auth_pubkey_fail">Uwierzytelnienie metodą klucza publicznego z kluczem \'%1$s\' nie powiodło się</string>
+ <string name="terminal_auth_ki">Próba uwierzytelnienia za pomocą klawiatury</string>
+ <string name="terminal_auth_ki_fail">Uwierzytelnienie za pomocą klawiatury nie powiodło się</string>
+ <string name="terminal_auth_fail">[Twój host nie obsługuje uwierzytelnienia za pomocą klawiatury lub hasła]</string>
+ <string name="terminal_no_session">Sesja nie zostanie rozpoczęta z powodu ustawień hostu</string>
<string name="terminal_enable_portfoward">Włącz przekierowanie portu: %1$s</string>
<string name="local_shell_unavailable">Niepowodzenie! Lokalna powłoka nie jest obsługiwana na tym telefonie.</string>
<string name="notification_text">%1$s wymaga reakcji</string>
- <string name="upgrade">Nowa wersja</string>
- <string name="upgrade_pos">Tak, zaktualizuj</string>
- <string name="upgrade_neg">Nie teraz</string>
<string name="no">Nie</string>
<string name="with_confirmation">Z potwierdzeniem</string>
<string name="yes">Tak</string>
<string name="exceptions_submit_message">ConnectBot miał prawdopodobnie problem podczas ostatniego uruchomienia. Wysłać raport o błędzie?</string>
<string name="menu_colors_reset">Przywróć</string>
- <string name="app_is_running">Uruchomiono ConnectBot</string>
+ <string name="app_is_running">ConnectBot jest uruchomiony</string>
<string name="color_red">czerwony</string>
<string name="color_green">zielony</string>
<string name="color_blue">niebieski</string>
<string name="color_gray">szary</string>
+ <string name="colors_fg_label">FG: %1$d</string>
+ <string name="color_bg_label">BG: %1$d</string>
+ <string name="image_description_connected">Połączono</string>
+ <string name="image_description_key_is_locked">Klucz zablokowany</string>
+ <string name="image_description_toggle_control_character">Przełącz znak kontrolny</string>
+ <string name="image_description_send_escape_character">Wyślij znak ESC</string>
+ <string name="image_description_show_keyboard">Pokarz klawiaturę</string>
</resources>
diff --git a/res/values-pt-rBR/strings.xml b/res/values-pt-rBR/strings.xml
index 069e56c..de8ed75 100644
--- a/res/values-pt-rBR/strings.xml
+++ b/res/values-pt-rBR/strings.xml
@@ -1,17 +1,20 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="app_desc">Cliente SSH simples e poderoso de código aberto</string>
- <string name="title_pubkey_list">Chaves públicas</string>
- <string name="title_port_forwards_list">Encaminhamento de portas</string>
- <string name="title_host_editor">Editar hosts</string>
+ <string name="service_desc">Mantém conexões SSH e de Chaves Públicas</string>
+ <string name="title_hosts_list">Hosts</string>
+ <string name="title_pubkey_list">Chaves Públicas</string>
+ <string name="title_port_forwards_list">Redirecionamento de Portas</string>
+ <string name="title_host_editor">Edição de Host</string>
<string name="title_help">Ajuda</string>
<string name="title_colors">Cores</string>
+ <string name="title_color_picker">Escolha a cor</string>
<string name="resolve_connect">Conectar</string>
<string name="resolve_entropy">Sensibilidade</string>
<string name="menu_insert">Adicionar Host</string>
<string name="menu_delete">Apagar host</string>
- <string name="menu_preferences">Perferências</string>
- <string name="help_intro">Escolha um tópico para obter maiores informações.</string>
+ <string name="menu_preferences">Preferências</string>
+ <string name="help_intro">Escolha um dos tópicos abaixo para maiores informações</string>
<string name="help_about">Sobre o ConnectBot</string>
<string name="help_keyboard">Teclado</string>
<string name="pubkey_generate">Gerar</string>
@@ -29,6 +32,7 @@
<string name="pubkey_list_pick">Escolher do /sdcard</string>
<string name="pubkey_import_parse_problem">Erro na leitura da chave privada importada</string>
<string name="pubkey_unlock">Abrir chave</string>
+ <string name="pubkey_failed_add">Senha inválida para Chave \'%1$s\'.Falha na autenticação.</string>
<string name="pubkey_memory_load">Carregar na memória</string>
<string name="pubkey_memory_unload">Descarregar da memória</string>
<string name="pubkey_load_on_start">Carregar chave ao iniciar</string>
@@ -45,12 +49,14 @@
<string name="prompt_again">(novamente)</string>
<string name="prompt_type">Tipo:</string>
<string name="prompt_password_can_be_blank">Nota: a senha pode ser em branco</string>
+ <string name="prompt_bits">Bits:</string>
<string name="prompt_pubkey_password">Senha para chave %1$s</string>
<string name="prompt_allow_agent_to_use_key">Permitir que a máquina remota use a chave %1$s?</string>
<string name="host_verification_failure_warning_header">ATENÇÃO: A IDENTIFICAÇÃO DO HOST REMOTO FOI ALTERADA!</string>
<string name="host_verification_failure_warning">É POSSÍVEL QUE ALGUÉM ESTEJA QUERENDO TE ENGANAR!\nAlguém pode estar tentando um ataque do tipo man-in-the-middle!\nTambém é possível que a chave do host remoto tenha mesmo sido modificada.</string>
<string name="prompt_host_disconnected">Host desconectado.\nFechar sessão?</string>
<string name="prompt_continue_connecting">Você tem certeza que\ndeseja se conectar?</string>
+ <string name="host_authenticity_warning">A autenticidade do Host \'%1$s\' não pode ser estabelecida</string>
<string name="host_fingerprint">O host %1$s tem a impressão digital %2$s</string>
<string name="alert_passwords_do_not_match_msg">As senhas são diferentes!</string>
<string name="alert_wrong_password_msg">Senha incorreta!</string>
@@ -61,6 +67,7 @@
<string name="button_generate">Gerar chave</string>
<string name="button_resize">Redimensionar</string>
<string name="alert_disconnect_msg">Conexão perdida</string>
+ <string name="msg_copyright">Copyright © 2007-2008 Kenny Root http://the-b.org/, Jeffrey Sharkey http://jsharkey.org/</string>
<string name="pref_emulation_category">Emulação de Terminal</string>
<string name="pref_emulation_title">Modo de emulação</string>
<string name="pref_emulation_summary">Modo de emulação do terminal com conexões PTY</string>
@@ -71,13 +78,13 @@
<string name="pref_rotation_summary">Como alterar a rotação da tela quando se conecta/desconecta um teclado</string>
<string name="pref_fullscreen_title">Tela cheia</string>
<string name="pref_fullscreen_summary">Ocultar a barra de estatus no modo terminal</string>
+ <string name="pref_keyboard_category">Teclado</string>
<string name="pref_memkeys_title">Guardar as chaves em memória</string>
<string name="pref_memkeys_summary">Manter chaves desbloqueadas na memória até que o serviço seja terminado</string>
- <string name="pref_update_title">Procurar por atualizações</string>
- <string name="pref_update_summary">Configurar frequencia para checar atualizações do ConnectBot</string>
<string name="pref_conn_persist_title">Conexões persistentes</string>
<string name="pref_conn_persist_summary">Forçar as conexões para que continuem conectadas mesmo em segundo plano</string>
<string name="pref_keymode_title">Teclas de atalho</string>
+ <string name="pref_keymode_summary">Selecione para usar Alt para \'/\' e Shift para Tab</string>
<string name="pref_camera_title">Atalho para a câmera</string>
<string name="pref_camera_summary">Selecione o que fazer quando o botão da câmera for pressionado</string>
<string name="pref_keepalive_title">Manter tela ligada</string>
@@ -97,9 +104,6 @@
<string name="list_keymode_none">Desabilitar</string>
<string name="list_pubkeyids_none">Não usar chaves</string>
<string name="list_pubkeyids_any">Usar qualquer chave pública</string>
- <string name="list_update_daily">Diariamente</string>
- <string name="list_update_weekly">Semanalmente</string>
- <string name="list_update_never">Nunca</string>
<string name="hostpref_nickname_title">Apelido</string>
<string name="hostpref_color_title">Categoria de cor</string>
<string name="hostpref_fontsize_title">Tamanho da fonte (pontos)</string>
@@ -119,11 +123,9 @@
<string name="hostpref_encoding_summary">Codificação de caracteres para o host</string>
<string name="hostpref_connection_category">Configurações de conexão</string>
<string name="hostpref_username_title">Usuário</string>
+ <string name="hostpref_hostname_title">Anfitrião (host)</string>
<string name="hostpref_port_title">Porta</string>
<string name="bind_never">Nunca usado</string>
- <string name="bind_minutes">%1$s minutos atrás</string>
- <string name="bind_hours">%1$s horas atrás</string>
- <string name="bind_days">%1$s dias atrás</string>
<string name="console_copy_done">%1$d bytes copiados para a área de tranferência</string>
<string name="console_copy_start">Toque e arraste\nou use o trackball\npara selecionar uma área para copiar</string>
<string name="console_menu_close">Fechar</string>
@@ -139,6 +141,7 @@
<string name="portforward_dynamic">Dinâmico (SOCKS)</string>
<string name="portforward_pos">Criar encaminhamento de porta</string>
<string name="portforward_done">Encaminhamento de porta criado com sucesso</string>
+ <string name="portforward_problem">Problema na criação do redirecionamento de portas, talvez você esteja usando portas menores que 1024 ou a porta já esteja sendo usada.</string>
<string name="portforward_menu_add">Adicionar encaminhamento de porta</string>
<string name="hint_userhost">usuario\@nome_do_host</string>
<string name="list_format_error">Use o formato %1$s</string>
@@ -159,9 +162,13 @@
<string name="list_rotation_port">Forçar modo retrato</string>
<string name="list_rotation_auto">Automático</string>
<string name="list_camera_ctrlaspace">Ctrl + A + Espaço</string>
+ <string name="list_camera_ctrla">Ctrl+A</string>
+ <string name="list_camera_esc">Esc</string>
+ <string name="list_camera_esc_a">Esc+A</string>
<string name="list_camera_none">Nada</string>
<string name="list_delkey_backspace">Tecla Backspace</string>
<string name="list_delkey_del">Apagar</string>
+ <string name="delete_message">Certeza que quer deletar \'%1$s\' ?</string>
<string name="delete_pos">Sim, apagar</string>
<string name="delete_neg">Cancelar</string>
<string name="wizard_agree">Aceito</string>
@@ -169,19 +176,25 @@
<string name="wizard_back">Voltar</string>
<string name="terminal_no_hosts_connected">Sem host conectado atualmente</string>
<string name="terminal_connecting">Conectando no %1$s:%2$d via %3$s</string>
+ <string name="terminal_sucess">"Anfitrião comprovado \'%1$s\' chave: %2$s"</string>
<string name="terminal_failed">Falha na verificação da chave do host</string>
<string name="terminal_using_s2c_algorithm">Algoritmo do servidor para o cliente: %1$s %2$s</string>
<string name="terminal_using_c2s_algorithm">Algoritmo do cliente para o servidor: %1$s %2$s</string>
<string name="terminal_using_algorithm">Usando o algoritmo: %1$s %2$s</string>
<string name="terminal_auth">Tentando autenticação</string>
+ <string name="terminal_auth_pass">Tentativa de autenticação de \'senha\' em andamento</string>
+ <string name="terminal_auth_pass_fail">Método de autenticação por \'senha\' falhou</string>
+ <string name="terminal_auth_pubkey_any">Tentando autenticação \'chave pública\' com qualquer chave pública na memória</string>
<string name="terminal_auth_pubkey_invalid">A chave pública selecionada é inválida, tente selecionar outra no editor de hosts</string>
+ <string name="terminal_auth_pubkey_specific">Tentando autenticação \'chave pública\' com uma chave pública específica</string>
+ <string name="terminal_auth_pubkey_fail">Metodo de autenticação \'publickey\' com a chave \'%1$s\' falhou</string>
+ <string name="terminal_auth_ki">Tentando autenticação \'teclado-interativo\'</string>
+ <string name="terminal_auth_ki_fail">Metodo de autenticação \'interativa por teclado\' falhou</string>
+ <string name="terminal_auth_fail">[Seu host não suporta autenticação por \'senha\' ou \'interativa por teclado\'.</string>
<string name="terminal_no_session">A sessão não será iniciada devido à configurações do host.</string>
<string name="terminal_enable_portfoward">Habilitar encaminhamento de porta: %1$s</string>
<string name="local_shell_unavailable">Erro! O terminal local não está disponível neste telefone.</string>
<string name="notification_text">%1$s deseja sua atenção.</string>
- <string name="upgrade">Nova versão</string>
- <string name="upgrade_pos">Sim, atualizar</string>
- <string name="upgrade_neg">Agora não</string>
<string name="no">Não</string>
<string name="with_confirmation">Com confirmação</string>
<string name="yes">Sim</string>
@@ -192,4 +205,10 @@
<string name="color_green">verde</string>
<string name="color_blue">azul</string>
<string name="color_gray">cinza</string>
+ <string name="colors_fg_label">PP: %1$d</string>
+ <string name="color_bg_label">SP: %1$d</string>
+ <string name="image_description_connected">Conectado.</string>
+ <string name="image_description_key_is_locked">A chave está bloqueada.</string>
+ <string name="image_description_send_escape_character">Enviar caractere de escape.</string>
+ <string name="image_description_show_keyboard">Exibir teclado</string>
</resources>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 06f4267..0f372c9 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -1,6 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="app_desc">Simples e poderoso cliente SSH open-source</string>
+ <string name="service_desc">Manter ligações SSH e chaves públicas carregadas</string>
<string name="title_hosts_list">Máquinas</string>
<string name="title_pubkey_list">Chaves Públicas</string>
<string name="title_port_forwards_list">Redireciomento de portas</string>
@@ -30,6 +31,7 @@
<string name="pubkey_list_pick">Escolher de /sdcard</string>
<string name="pubkey_import_parse_problem">Problema de análise com a chave privada importada</string>
<string name="pubkey_unlock">Desbloquear chave</string>
+ <string name="pubkey_failed_add">Password inválida para a chave \'%1$s\'. Erro na autenticação.</string>
<string name="pubkey_memory_load">Carregar para a memória</string>
<string name="pubkey_memory_unload">retirar da memória</string>
<string name="pubkey_load_on_start">carregar chave no Inicio</string>
@@ -47,10 +49,13 @@
<string name="prompt_type">Tipo:</string>
<string name="prompt_password_can_be_blank">Nota: password pode ser em branco</string>
<string name="prompt_bits">bits:</string>
+ <string name="prompt_pubkey_password">Password para a chave \'%1$s\'</string>
+ <string name="prompt_allow_agent_to_use_key">Permitir ao anfitrião remoto\nusar a chave \'%1$s\'?</string>
<string name="host_verification_failure_warning_header">AVISO: a identificação do anfitrião remoto mudou!</string>
<string name="host_verification_failure_warning">É POSSÍVEL QUE ALGUÉM ESTEJA A FAZER ALGO ERRADO!\n Alguém pode estar espiando ( ataque man-in-the-middle)!\nTambém e possível que a chave do hospede tenha sido alterada</string>
<string name="prompt_host_disconnected">Hospede desconectou-se.\nFechar sessão?</string>
<string name="prompt_continue_connecting">Tem a certeza que quer\ncontinuar a connectar?</string>
+ <string name="host_authenticity_warning">A autenticidade do anfitrião \'%1$s\' não pode ser verificada.</string>
<string name="host_fingerprint">Hospede %1$s id da chave é %2$s</string>
<string name="alert_passwords_do_not_match_msg">As palavras passe não coincidem!</string>
<string name="alert_wrong_password_msg">Palavra passe errada!</string>
@@ -66,19 +71,26 @@
<string name="pref_emulation_title">Modo de emulação</string>
<string name="pref_emulation_summary">Terminal de emulação para usar conecções PTY</string>
<string name="pref_scrollback_title">Tamanho de Scrollback</string>
+ <string name="pref_scrollback_summary">Tamanho do buffer de scrollback em memória para cada consola</string>
<string name="pref_ui_category">Interface de utilizador</string>
<string name="pref_rotation_title">Modo de rotação</string>
+ <string name="pref_rotation_summary">Como alterar a rotação quando o teclado é aberto/fechado</string>
<string name="pref_fullscreen_title">Ecrã completo</string>
<string name="pref_fullscreen_summary">Esconder a barra de notificação quando estiver na consola</string>
<string name="pref_memkeys_title">Lembrar chaves em memoria</string>
- <string name="pref_update_title">Verificar actualizações</string>
+ <string name="pref_memkeys_summary">Manter chaves desbloqueadas em memória até ao serviço de fundo terminar</string>
<string name="pref_conn_persist_title">Persistir ligações</string>
+ <string name="pref_conn_persist_summary">Forçar ligações a ficarem ligadas quando em segundo plano</string>
+ <string name="pref_keymode_title">Atalhos de directoria</string>
+ <string name="pref_keymode_summary">Escolher como usar Alt para \'/\' e Shift para Tab</string>
<string name="pref_camera_title">Atalho para a camera</string>
<string name="pref_camera_summary">Escolha o atalho a activar quando o botão da camara é pressionado</string>
<string name="pref_keepalive_title">Mantar ecrã activo</string>
<string name="pref_keepalive_summary">Previne que o ecrã seja desligado quando se esta a utilizar a consola</string>
<string name="pref_wifilock_title">Manter Wi-Fi activo</string>
<string name="pref_wifilock_summary">Evitar que o Wi-Fi desligue quando uma sessão está activa</string>
+ <string name="pref_bumpyarrows_title">Cursores com vibração</string>
+ <string name="pref_bumpyarrows_summary">Vibrar quando enviar teclas de cursor com a trackball; útil para ligações com atraso</string>
<string name="pref_bell_category">Aviso sonoro do terminal</string>
<string name="pref_bell_title">Aviso sonoro audível</string>
<string name="pref_bell_volume_title">Volume do aviso sonoro</string>
@@ -90,37 +102,45 @@
<string name="list_keymode_none">Desactivar</string>
<string name="list_pubkeyids_none">Não usar teclas</string>
<string name="list_pubkeyids_any">Usar qualquer tecla não bloqueada</string>
- <string name="list_update_daily">Diariamente</string>
- <string name="list_update_weekly">Semanalmente</string>
- <string name="list_update_never">Nunca</string>
<string name="hostpref_nickname_title">Alcunha</string>
<string name="hostpref_color_title">Categoria de cor</string>
<string name="hostpref_fontsize_title">Tamanho da fonte (pt)</string>
+ <string name="hostpref_pubkeyid_title">Utilizar autenticação de chave publica (pubkey)</string>
+ <string name="hostpref_authagent_title">Usar agente de autenticação SSH</string>
<string name="hostpref_postlogin_title">Automação pós-login</string>
<string name="hostpref_postlogin_summary">Comandos a correr no servidor remoto após autenticar</string>
<string name="hostpref_compression_title">Compressão</string>
<string name="hostpref_compression_summary">Isto pode ajudar em redes mais lentas</string>
+ <string name="hostpref_wantsession_title">Iniciar sessão de shell</string>
+ <string name="hostpref_wantsession_summary">Desactivar esta preferência para usar apenas redirecionamento de portas</string>
<string name="hostpref_stayconnected_title">Permanecer ligado</string>
+ <string name="hostpref_stayconnected_summary">Tentar ligar de novo ao anfitrião se desligar</string>
<string name="hostpref_delkey_title">Tecla DEL</string>
<string name="hostpref_delkey_summary">O código de tecla a enviar quando a tecla DEL é pressionada</string>
<string name="hostpref_encoding_title">Codificação</string>
+ <string name="hostpref_encoding_summary">Codificação de caractéres para o anfitrião</string>
<string name="hostpref_connection_category">Configuração da Ligação</string>
<string name="hostpref_username_title">Nome do Utilizador</string>
<string name="hostpref_hostname_title">Anfitrião</string>
<string name="hostpref_port_title">Porto</string>
- <string name="bind_minutes">%1$s minutos atrás</string>
- <string name="bind_hours">%1$s horas atrás</string>
- <string name="bind_days">%1$s dias atrás</string>
+ <string name="bind_never">Nunca ligado</string>
<string name="console_copy_done">Copiados %1$d bytes para a área de transferência</string>
<string name="console_copy_start">Toque e arraste\nou use as teclas direcionais\npara selecionar a área a copiar</string>
<string name="console_menu_close">Fechar</string>
<string name="console_menu_copy">Copiar</string>
<string name="console_menu_paste">Colar</string>
+ <string name="console_menu_portforwards">Redirecionamento de portas</string>
<string name="console_menu_resize">Forçar Tamanho</string>
+ <string name="console_menu_urlscan">Detecção de URL</string>
<string name="button_yes">Sim</string>
<string name="button_no">Não</string>
+ <string name="portforward_local">Local</string>
<string name="portforward_remote">Remoto</string>
<string name="portforward_dynamic">Dinamico (SOCKS)</string>
+ <string name="portforward_pos">Criar redirecionamento de portas</string>
+ <string name="portforward_done">Redirecionamento de porta criado com sucesso</string>
+ <string name="portforward_problem">Problema ao criar redirecionamento de porta, talvez esteja a usar portas abaixo de 1024 ou a porta já esteja em uso?</string>
+ <string name="portforward_menu_add">Adicionar redirecionamento de porta</string>
<string name="hint_userhost">utilizador\@servidor</string>
<string name="list_format_error">Use o formato %1$s</string>
<string name="format_username">nome do utilizador</string>
@@ -131,24 +151,48 @@
<string name="list_menu_sortname">Ordenar por nome</string>
<string name="list_menu_settings">Definições</string>
<string name="list_host_disconnect">Desconectar</string>
+ <string name="list_host_edit">Editar anfitrião</string>
+ <string name="list_host_portforwards">Editar redirecionamento de portas</string>
+ <string name="list_host_delete">Eliminar anfitrião</string>
+ <string name="list_host_empty">Usar a caixa de ligação rápida\nabaixo para ligar a um anfitrião.</string>
<string name="list_rotation_default">Padrão</string>
+ <string name="list_rotation_land">Forçar panoramico</string>
+ <string name="list_rotation_port">Forçar retrato</string>
<string name="list_rotation_auto">Automática</string>
<string name="list_camera_ctrlaspace">Ctrl+A seguido de Espaço</string>
+ <string name="list_camera_ctrla">Ctrl+A</string>
+ <string name="list_camera_esc">Esc</string>
+ <string name="list_camera_esc_a">Esc+A</string>
<string name="list_camera_none">Nenhum</string>
+ <string name="list_delkey_backspace">Retrocesso</string>
<string name="list_delkey_del">Apagar</string>
+ <string name="delete_message">Tem a certeza que deseja eliminar \'%1$s\'?</string>
<string name="delete_pos">Sim, apagar</string>
<string name="delete_neg">Cancelar</string>
<string name="wizard_agree">Aceitar</string>
<string name="wizard_next">Seguinte</string>
<string name="wizard_back">Retroceder</string>
+ <string name="terminal_no_hosts_connected">Nenhum anfitrião ligado actualmente</string>
<string name="terminal_connecting">A ligar a %1$s:%2$d via %3$s</string>
+ <string name="terminal_sucess">Verificado anfitrião \'%1$s\' chave: %2$s</string>
+ <string name="terminal_failed">Verificação da chave do anfitrião falhou.</string>
<string name="terminal_using_s2c_algorithm">Algoritmo servidor-cliente: %1$s %2$s</string>
<string name="terminal_using_c2s_algorithm">Algoritmo cliente-servidor: %1$s %2$s</string>
<string name="terminal_using_algorithm">A usar algoritmo %1$s %2$s</string>
<string name="terminal_auth">A tentar autenticação</string>
- <string name="upgrade">Nova versão</string>
- <string name="upgrade_pos">Sim, actualizar</string>
- <string name="upgrade_neg">Não, por agora</string>
+ <string name="terminal_auth_pass">A tentar autenticação por \'password\'</string>
+ <string name="terminal_auth_pass_fail">Autenticação pelo método \'password\' falhou</string>
+ <string name="terminal_auth_pubkey_any">A tentar autenticação de chave pública (pubkey) com todas as chaves públicas em memória</string>
+ <string name="terminal_auth_pubkey_invalid">A chave pública escolhida é inválida, tente escolher outra chave no editor de anfitriões</string>
+ <string name="terminal_auth_pubkey_specific">A tentar autenticação de chave pública (publickey) com uma chave pública específica</string>
+ <string name="terminal_auth_pubkey_fail">Autenticação pelo método de chave pública (publickey) com a chave \'%1$s\' falhou</string>
+ <string name="terminal_auth_ki">A tentar autenticação pelo método \'teclado-interactivo\'</string>
+ <string name="terminal_auth_ki_fail">Autenticação pelo método \'teclado-interactivo\' falhou</string>
+ <string name="terminal_auth_fail">[O seu anfitrião não suporta autenticação por \'password\' ou \'teclado-interactivo\'.]</string>
+ <string name="terminal_no_session">A sessão não será iniciada devido a preferência do anfitrião.</string>
+ <string name="terminal_enable_portfoward">Permitir redirecionamento de porta: %1$s</string>
+ <string name="local_shell_unavailable">Falha! Shell local não está disponível neste telemóvel.</string>
+ <string name="notification_text">%1$s requer a sua atenção.</string>
<string name="no">Não</string>
<string name="with_confirmation">com confirmação</string>
<string name="yes">Sim</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
new file mode 100644
index 0000000..2581c39
--- /dev/null
+++ b/res/values-ro/strings.xml
@@ -0,0 +1,55 @@
+<?xml version='1.0' encoding='utf-8'?>
+<resources>
+ <string name="app_desc">Un client SSH simplu, flexibil, open-source</string>
+ <string name="service_desc">Managementul conexiunilor si al cheilor publice</string>
+ <string name="title_hosts_list">Gazde</string>
+ <string name="title_pubkey_list">Chei publice</string>
+ <string name="title_port_forwards_list">Redirectări trafic</string>
+ <string name="title_host_editor">Editare gazdă</string>
+ <string name="title_help">Ajutor</string>
+ <string name="title_colors">Culori</string>
+ <string name="resolve_connect">Conectează-te</string>
+ <string name="resolve_entropy">Genereaza sursă entropie</string>
+ <string name="menu_insert">Adaugă gazdă</string>
+ <string name="menu_delete">Sterge gazdă</string>
+ <string name="menu_preferences">Setări</string>
+ <string name="help_intro">Selecteaza un subiect pentru a accesa mai multe informatii</string>
+ <string name="help_about">Despre ConnectBot</string>
+ <string name="help_keyboard">Tastatură</string>
+ <string name="pubkey_generate">Generează</string>
+ <string name="pubkey_import">Importă</string>
+ <string name="pubkey_delete">Șterge cheia</string>
+ <string name="pubkey_gather_entropy">Genereaza sursa entropie</string>
+ <string name="pubkey_touch_prompt">Apasa aceasta zona pentru a genera entropie: %1$d%% finalizat</string>
+ <string name="pubkey_touch_hint">Pentru a va asigura un grad inalt de stocasticitate in timpul generarii cheii, miscati cat mai aleator degetul in zona de mai jos</string>
+ <string name="pubkey_generating">Generare cheie ...</string>
+ <string name="pubkey_copy_private">Copiaza cheia privata</string>
+ <string name="pubkey_copy_public">Copiaza cheia publica</string>
+ <string name="pubkey_list_empty">Apasa \"Menu\" pentru a crea sau pentru a importa chei.</string>
+ <string name="pubkey_unknown_format">Format necunoscut</string>
+ <string name="pubkey_change_password">Schimbă parola</string>
+ <string name="pubkey_list_pick">Selecteaza de pe /card SD</string>
+ <string name="pubkey_import_parse_problem">A aparut o eroare in timpul citirii cheii private</string>
+ <string name="pubkey_unlock">Decripteaza cheia</string>
+ <string name="pubkey_failed_add">Parola gresita pentru cheia \'%1$s\'. Autenticitatea cererii nu poate fi verificata.</string>
+ <string name="pubkey_memory_load">Incarca in memorie</string>
+ <string name="pubkey_memory_unload">Sterge din memorie</string>
+ <string name="pubkey_load_on_start">Incarca cheia la start</string>
+ <string name="pubkey_confirm_use">Confirma alegerea inainte de folosirea cheii</string>
+ <string name="portforward_list_empty">Apasa \"Menu\" pentru a crea redirectari de trafic</string>
+ <string name="portforward_edit">Editeaza redirectari trafic</string>
+ <string name="portforward_delete">Sterge redirectari trafic</string>
+ <string name="prompt_nickname">Poreclă:</string>
+ <string name="prompt_nickname_hint_pubkey">Cheia mea</string>
+ <string name="prompt_source_port">Port sursă</string>
+ <string name="prompt_destination">Destinație</string>
+ <string name="prompt_old_password">Parola veche:</string>
+ <string name="prompt_password">Parolă:</string>
+ <string name="prompt_again">(din nou)</string>
+ <string name="prompt_type">Mod redirectare:</string>
+ <string name="prompt_password_can_be_blank">Nota: parola</string>
+ <string name="prompt_bits">Biți:</string>
+ <string name="prompt_pubkey_password">Parola pentru cheia \'%1$s\'</string>
+ <string name="prompt_allow_agent_to_use_key">Acceptați utilizarea cheii \'%1$s\' \nde către agentul de autentificare?</string>
+ <string name="host_verification_failure_warning_header">ATENTIE: IDENTITATEA SISTEMULUI CONECTAT S-A SCHIMBAT!</string>
+</resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 5e3db2f..e759607 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -1,25 +1,26 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
- <string name="app_desc">Простой и мощный SSH клиент с открытым исходным кодом</string>
- <string name="title_hosts_list">Хосты</string>
+ <string name="app_desc">Простой и мощный SSH-клиент с открытым исходным кодом.</string>
+ <string name="service_desc">Управляет SSH-соединениями и загруженными публичными ключами</string>
+ <string name="title_hosts_list">Серверы</string>
<string name="title_pubkey_list">Публичные ключи</string>
- <string name="title_port_forwards_list">Переадресация портов</string>
- <string name="title_host_editor">Изменить хост</string>
+ <string name="title_port_forwards_list">Проброс портов</string>
+ <string name="title_host_editor">Изменить сервер</string>
<string name="title_help">Помощь</string>
- <string name="title_colors">Цвета</string>
- <string name="resolve_connect">Соединение</string>
- <string name="resolve_entropy">Получить произвольный шум</string>
- <string name="menu_insert">Добавить хост</string>
- <string name="menu_delete">Удалить хост</string>
+ <string name="title_colors">Настройка цветов</string>
+ <string name="resolve_connect">Подключиться</string>
+ <string name="resolve_entropy">Сбор случайных данных</string>
+ <string name="menu_insert">Добавить сервер</string>
+ <string name="menu_delete">Удалить сервер</string>
<string name="menu_preferences">Настройки</string>
- <string name="help_intro">Для дополнительной информации выберите топик</string>
+ <string name="help_intro">Выберите то, о чём Вы хотите узнать</string>
<string name="help_about">О ConnectBot</string>
<string name="help_keyboard">Клавиатура</string>
<string name="pubkey_generate">Генерировать</string>
- <string name="pubkey_import">Импорт</string>
+ <string name="pubkey_import">Импортировать</string>
<string name="pubkey_delete">Удалить ключ</string>
- <string name="pubkey_gather_entropy">Получение шума</string>
- <string name="pubkey_touch_prompt">Прикоснитесь сюда для получения шума: %1$d%% готово</string>
+ <string name="pubkey_gather_entropy">Сбор случайной информации</string>
+ <string name="pubkey_touch_prompt">Нажмите сюда для генерирования случайной информации: %1$d%% готово</string>
<string name="pubkey_touch_hint">Для использования случайной выборки во время генерации ключа - перемещайте палец в области экрана ниже.</string>
<string name="pubkey_generating">Генерирование пары ключей</string>
<string name="pubkey_copy_private">Копировать приватный ключ</string>
@@ -74,12 +75,13 @@
<string name="pref_ui_category">Пользовательский интерфейс</string>
<string name="pref_rotation_title">Настройки ориентации экрана</string>
<string name="pref_rotation_summary">Настройки смены ориентации дисплея при пользовании клавиатурой</string>
+ <string name="pref_titlebarhide_title">Автоскрытие заголовка</string>
<string name="pref_fullscreen_title">Полный экран</string>
<string name="pref_fullscreen_summary">Убирать область статуса во время работы с консолью</string>
+ <string name="pref_ctrlfkeys_title">Ctrl+num аналогично F-keys</string>
+ <string name="pref_volumefont_title">Клавиша громкости регулирует размер шрифта</string>
<string name="pref_memkeys_title">Запомнить ключи в памяти</string>
<string name="pref_memkeys_summary">Держать ключи в памяти пока процессы работают</string>
- <string name="pref_update_title">Проверка обновления</string>
- <string name="pref_update_summary">Как часто проверять обновления для ConnectBot</string>
<string name="pref_conn_persist_title">Постоянные соединения</string>
<string name="pref_conn_persist_summary">Принудительно сохранять соединение в фоновом режиме.</string>
<string name="pref_keymode_title">Ярлыки папок</string>
@@ -102,10 +104,7 @@
<string name="list_keymode_left">Использовать клавиши с левой стороны</string>
<string name="list_keymode_none">Отключить</string>
<string name="list_pubkeyids_none">Не использовать ключи</string>
- <string name="list_pubkeyids_any">Использовать любую разблокированную клавишу</string>
- <string name="list_update_daily">Ежедневно</string>
- <string name="list_update_weekly">Еженедельно</string>
- <string name="list_update_never">Никогда</string>
+ <string name="list_pubkeyids_any">Использовать любую свободную клавишу</string>
<string name="hostpref_nickname_title">Псевдоним</string>
<string name="hostpref_color_title">Цвет для категории</string>
<string name="hostpref_fontsize_title">Размер шрифта</string>
@@ -128,9 +127,6 @@
<string name="hostpref_hostname_title">Узел</string>
<string name="hostpref_port_title">Порт</string>
<string name="bind_never">Не подключен</string>
- <string name="bind_minutes">%1$s минут назад</string>
- <string name="bind_hours">%1$s час(ов) назад</string>
- <string name="bind_days">%1$s дней назад</string>
<string name="console_copy_done">Скопировано %1$d байт в буфер обмена</string>
<string name="console_copy_start">Для выделения области на экране используйте Touch and Drag или Directional Pad</string>
<string name="console_menu_close">Закрыть</string>
@@ -200,9 +196,6 @@
<string name="terminal_enable_portfoward">Включить переадресацию порта: %1$s</string>
<string name="local_shell_unavailable">Ошибка! На этом телефоне отсутствует локальная оболочка.</string>
<string name="notification_text">%1$s требует внимания</string>
- <string name="upgrade">Новая версия</string>
- <string name="upgrade_pos">Да, обновить</string>
- <string name="upgrade_neg">Не сейчас</string>
<string name="no">Нет</string>
<string name="with_confirmation">С подтверждением</string>
<string name="yes">Да</string>
@@ -213,4 +206,7 @@
<string name="color_green">зелёный</string>
<string name="color_blue">синий</string>
<string name="color_gray">серый</string>
+ <string name="image_description_connected">Подключено</string>
+ <string name="image_description_key_is_locked">Ключ заблокирован</string>
+ <string name="image_description_show_keyboard">Показать клавиатуру</string>
</resources>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 462e89a..7b91870 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -1,107 +1,206 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
- <string name="title_hosts_list">Servre</string>
+ <string name="app_desc">Jednoduchý, výkonný, open-source SSH klient.</string>
+ <string name="service_desc">Spravuje SSH spojenia a načítané verejné kľúče</string>
+ <string name="title_hosts_list">Hostitelia</string>
<string name="title_pubkey_list">Verejné kľúče</string>
<string name="title_port_forwards_list">Presmerovanie portov</string>
<string name="title_host_editor">Upraviť Hostiteľa</string>
- <string name="title_help">Pomoc</string>
+ <string name="title_help">Nápoveda</string>
<string name="title_colors">Farby</string>
<string name="resolve_connect">Pripojiť</string>
- <string name="menu_insert">Pridať Hostiteľa</string>
- <string name="menu_delete">Zmazať Hostiteľa</string>
+ <string name="resolve_entropy">Získať entropiu</string>
+ <string name="menu_insert">Pridať hostiteľa</string>
+ <string name="menu_delete">Zmazať hostiteľa</string>
<string name="menu_preferences">Nastavenia</string>
- <string name="help_intro">Prosím vyberte si sekciu pre viac informácií o danej téme.</string>
+ <string name="help_intro">Prosím, vyberte si tému pre viac informácií o danom subjekte.</string>
<string name="help_about">O programe ConnectBot</string>
<string name="help_keyboard">Klávesnica</string>
<string name="pubkey_generate">Vygenerovať</string>
- <string name="pubkey_touch_hint">Pre zabezpečenie nepravideľnosti počas generovania kľúčov, pohybujte sa prstom náhodne cez štvorec.</string>
+ <string name="pubkey_import">Importovať</string>
+ <string name="pubkey_delete">Zmazať kľúč</string>
+ <string name="pubkey_gather_entropy">Získavanie entropie</string>
+ <string name="pubkey_touch_prompt">Dotknite sa plochy pre získanie náhodnosti: %1$d%% hotovo</string>
+ <string name="pubkey_touch_hint">Pre zabezpečenie náhodnosti počas generovania kľúča pohybujte prstom náhodne po ploche nižšie.</string>
<string name="pubkey_generating">Generujem kľúčový pár...</string>
- <string name="pubkey_copy_private">Skopírovať privátny kľúč</string>
+ <string name="pubkey_copy_private">Kopírovať súkromný kľúč</string>
<string name="pubkey_copy_public">Kopírovať verejný kľúč</string>
- <string name="pubkey_list_empty">Ťukni na Menu pre vytvorenie\nalebo importovanie kľúčov.</string>
- <string name="pubkey_unknown_format">Neznámy formát.</string>
+ <string name="pubkey_list_empty">Ťukni na Menu pre vytvorenie\nalebo importovanie páru kľúčov.</string>
+ <string name="pubkey_unknown_format">Neznámy formát</string>
<string name="pubkey_change_password">Zmeniť heslo</string>
- <string name="pubkey_import_parse_problem">Problém analýzy importovaného privátneho kľúča</string>
+ <string name="pubkey_list_pick">Vyberte z /sdcard</string>
+ <string name="pubkey_import_parse_problem">Problém s importom súkromného kľúča</string>
<string name="pubkey_unlock">Odomknúť kľúč</string>
+ <string name="pubkey_failed_add">Nesprávne heslo pre kľúč \'%1$s\'. Overenie zlyhalo.</string>
<string name="pubkey_memory_load">Nahrať do pamäte</string>
<string name="pubkey_memory_unload">Odstrániť z pamäte</string>
- <string name="pubkey_confirm_use">Odsúhlasovať pred použitím</string>
- <string name="portforward_list_empty">Tap Menu to create\nport forwards.</string>
+ <string name="pubkey_load_on_start">Načítať kľúč pri štarte</string>
+ <string name="pubkey_confirm_use">Potvrdiť pred použitím</string>
+ <string name="portforward_list_empty">Ťukni na Menu pre vytvorenie\npresmerovania portov.</string>
+ <string name="portforward_edit">Upraviť presmerovanie portov</string>
+ <string name="portforward_delete">Zmazať smerovanie portu</string>
<string name="prompt_nickname">Prezývka:</string>
<string name="prompt_nickname_hint_pubkey">Môj pracovný kľúč</string>
+ <string name="prompt_source_port">Zdrojový port:</string>
<string name="prompt_destination">Cieľ:</string>
<string name="prompt_old_password">Staré heslo:</string>
<string name="prompt_password">Heslo:</string>
<string name="prompt_again">(znovu)</string>
<string name="prompt_type">Typ:</string>
- <string name="prompt_password_can_be_blank">Poznámka: heslo nemôže byt prázdne</string>
+ <string name="prompt_password_can_be_blank">Poznámka: heslo nemôže byť prázdne</string>
<string name="prompt_bits">Veľkost v bitoch:</string>
- <string name="alert_key_corrupted_msg">Privátny kľúč vyzerá poškodene!</string>
- <string name="alert_sdcard_absent">SD karta nie je vložená!</string>
+ <string name="prompt_pubkey_password">Heslo pre kľúč \'%1$s\'</string>
+ <string name="prompt_allow_agent_to_use_key">Povoliť vzdialenému hostitelovi\npoužíť kľúč \'%1$s\'?</string>
+ <string name="host_verification_failure_warning_header">VAROVANIE! IDENTIFIKÁCIA VZDIALENÉHO HOSTITEĽA SA ZMENILA!</string>
+ <string name="host_verification_failure_warning">JE MOŽNÉ, ŽE IDE O NEKALÚ AKTIVITU!\nNiekto vás môže sledovať (man-in-the-middle útok)!\nTiež je možné, že kľúč hostiteľa bol zmenený.</string>
+ <string name="prompt_host_disconnected">Hostiteľ sa odpojil.\nUkončiť sedenie?</string>
+ <string name="prompt_continue_connecting">Naozaj chcete\npokračovať v pripájaní?</string>
+ <string name="host_authenticity_warning">Autenticita hostiteľa \'%1$s\' nemohla byť overená.</string>
+ <string name="host_fingerprint">Odtlačok kľúča hostiteľa %1$s je %2$s</string>
+ <string name="alert_passwords_do_not_match_msg">Heslo sa nezhoduje!</string>
+ <string name="alert_wrong_password_msg">Nesprávne heslo!</string>
+ <string name="alert_key_corrupted_msg">Súkromný kľúč vyzerá poškodene!</string>
+ <string name="alert_sdcard_absent">Nie je vložená SD karta!</string>
<string name="button_add">Pridať</string>
<string name="button_change">Zmeniť</string>
- <string name="button_generate">Vygenerovať Kľúč</string>
+ <string name="button_generate">Vygenerovať kľúč</string>
<string name="button_resize">Zmeniť veľkosť</string>
<string name="alert_disconnect_msg">Spojenie stratené</string>
+ <string name="msg_copyright">Copyright © 2007-2008 Kenny Root http://the-b.org/, Jeffrey Sharkey http://jsharkey.org/</string>
<string name="pref_emulation_category">Emulácia terminálu</string>
<string name="pref_emulation_title">Mód emulácie</string>
+ <string name="pref_emulation_summary">Použiť mód emulácie terminálu pri PTY spojeniach</string>
+ <string name="pref_scrollback_title">Veľkosť histórie</string>
+ <string name="pref_scrollback_summary">Veľkosť histórie uložená v pamäti pre každú konzolu</string>
<string name="pref_ui_category">Užívateľské rozhranie</string>
<string name="pref_rotation_title">Mód otáčania</string>
- <string name="pref_fullscreen_title">Na celú obrazovku</string>
- <string name="pref_keymode_title">Adresárové skratky</string>
- <string name="pref_wifilock_title">Ponechať WiFi aktívne</string>
- <string name="pref_wifilock_summary">Ponechať WiFi aktívne pokial je otvorené spojenie</string>
+ <string name="pref_rotation_summary">Ako zmeniť rotáciu, keď sa zobrazí / skryje klávesnica</string>
+ <string name="pref_fullscreen_title">Celá obrazovka</string>
+ <string name="pref_fullscreen_summary">Skryť stavový riadok, keď je zobrazená konzola</string>
+ <string name="pref_memkeys_title">Zapamätať kľúče v pamäti</string>
+ <string name="pref_memkeys_summary">Ponechať odomknuté kľúče v pamäti pokiaľ nie je ukončená služba na pozadí.</string>
+ <string name="pref_conn_persist_title">Perzistentné pripojenia</string>
+ <string name="pref_conn_persist_summary">Vynútiť spojenie aj v pozadí</string>
+ <string name="pref_keymode_title">Skratky adresárov</string>
+ <string name="pref_keymode_summary">Vyberte ako použiť Alt pre \'/\' a Shift pre Tab</string>
+ <string name="pref_camera_title">Odkaz pre fotoaparát</string>
+ <string name="pref_camera_summary">Vyberte odkaz, ktorý spustiť po stlačení tlačidla kamera</string>
+ <string name="pref_keepalive_title">Ponechať obrazovku zapnutú</string>
+ <string name="pref_keepalive_summary">Zabrániť vypnutiu obrazovky pri práci s konzolou</string>
+ <string name="pref_wifilock_title">Ponechať Wi-Fi aktívne</string>
+ <string name="pref_wifilock_summary">Ponechať Wi-Fi aktívne pokiaľ je otvorené spojenie</string>
+ <string name="pref_bumpyarrows_title">Hrboľaté šípky</string>
+ <string name="pref_bumpyarrows_summary">Vibrovať pri posielaní kurzorových tlačidiel z trackballu; užitočné pre chybové spojenia</string>
<string name="pref_bell_category">Zvonček terminálu</string>
+ <string name="pref_bell_title">Akustický zvonček</string>
<string name="pref_bell_volume_title">Hlasitosť zvončeka</string>
- <string name="pref_bell_vibrate_title">Zavybrovať pri zvončeku</string>
+ <string name="pref_bell_vibrate_title">Vibrovať pri zvončeku</string>
+ <string name="pref_bell_notification_title">Upozornenia na pozadí</string>
+ <string name="pref_bell_notification_summary">Zobraziť upozorneni ak zazvoní zvonček terminálu bežiaceho v pozadí.</string>
+ <string name="list_keymode_right">Použite klávesy vpravo</string>
+ <string name="list_keymode_left">Použite klávesy vľavo</string>
<string name="list_keymode_none">Vypnúť</string>
<string name="list_pubkeyids_none">Nepoužívať kľúče</string>
- <string name="list_pubkeyids_any">Použiť ktorýkoľvek kľúč</string>
- <string name="list_update_daily">Denne</string>
- <string name="list_update_weekly">Týždenne</string>
- <string name="list_update_never">Nikdy</string>
+ <string name="list_pubkeyids_any">Použiť ktorýkoľvek odomknutý kľúč</string>
<string name="hostpref_nickname_title">Prezývka</string>
- <string name="hostpref_fontsize_title">Veľkosť písma</string>
+ <string name="hostpref_color_title">Farebná kategória</string>
+ <string name="hostpref_fontsize_title">Veľkosť písma (bodov)</string>
<string name="hostpref_pubkeyid_title">Použiť verejný kľúč pre autentifikáciu</string>
+ <string name="hostpref_authagent_title">Použiť SSH autorizačného agenta</string>
+ <string name="hostpref_postlogin_title">Automatizácia po prihlásení</string>
+ <string name="hostpref_postlogin_summary">Príkazy, ktoré sa spustia na vzdialenom serveri po prihlásení</string>
<string name="hostpref_compression_title">Kompresia</string>
- <string name="hostpref_compression_summary">Toto môže pomôcť pri pomalých sieťach</string>
+ <string name="hostpref_compression_summary">Môže pomôcť pri pomalých sieťach</string>
+ <string name="hostpref_wantsession_title">Spustiť sedenie shellu</string>
+ <string name="hostpref_wantsession_summary">Vypnite túto možnosť ak chcete len presmerovať porty</string>
<string name="hostpref_stayconnected_title">Zostať pripojený</string>
- <string name="hostpref_stayconnected_summary">Pokúsiť sa po odpojení opať pripojiť</string>
+ <string name="hostpref_stayconnected_summary">Znovu sa pripojiť pri odpojení</string>
+ <string name="hostpref_delkey_title">Klávesa DEL</string>
+ <string name="hostpref_delkey_summary">Kód stlačenej DEL klávesy</string>
<string name="hostpref_encoding_title">Kódovanie</string>
+ <string name="hostpref_encoding_summary">Kódovanie znakov hostiteľa</string>
<string name="hostpref_connection_category">Nastavenia pripojenia</string>
<string name="hostpref_username_title">Užívateľské meno</string>
<string name="hostpref_hostname_title">Hostiteľ</string>
- <string name="bind_never">Nikdy nebol pripojený</string>
- <string name="bind_days">Pred %1$s dňami</string>
+ <string name="hostpref_port_title">Port</string>
+ <string name="bind_never">Nikdy nepripojený</string>
+ <string name="console_copy_done">%1$d bajtov skopírovaných do schránky</string>
+ <string name="console_copy_start">Pre výber kopírovanej oblasti \nsa dotknite a ťahajte\nalebo použite smerové tlačidlo</string>
<string name="console_menu_close">Zatvoriť</string>
<string name="console_menu_copy">Kopírovať</string>
<string name="console_menu_paste">Vložiť</string>
+ <string name="console_menu_portforwards">Presmerovanie portov</string>
+ <string name="console_menu_resize">Vynútiť veľkosť</string>
+ <string name="console_menu_urlscan">Nájsť URL</string>
<string name="button_yes">Áno</string>
<string name="button_no">Nie</string>
<string name="portforward_local">Lokálny</string>
<string name="portforward_remote">Vzdialený</string>
- <string name="list_format_error">Use the format %1$s</string>
+ <string name="portforward_dynamic">Dynamický (SOCKS)</string>
+ <string name="portforward_pos">Vytvoriť presmerovanie portu</string>
+ <string name="portforward_done">Premerovanie portu úspešne vytvorené</string>
+ <string name="portforward_problem">Problém s vytvorením presmerovania portu, možno používate port menší ako 1024 alebo je už obsadený?</string>
+ <string name="portforward_menu_add">Pridať presmerovanie portu</string>
+ <string name="hint_userhost">používateľské_meno\@hostiteľ</string>
+ <string name="list_format_error">Použite formát \"%1$s\"</string>
<string name="format_username">užívateľské meno</string>
+ <string name="format_hostname">meno hostiteľa</string>
+ <string name="format_port">port</string>
+ <string name="list_menu_pubkeys">Spravovať verejné kľúče</string>
+ <string name="list_menu_sortcolor">Usporiadať podľa farby</string>
+ <string name="list_menu_sortname">Usporiadať podľa názvu</string>
<string name="list_menu_settings">Nastavenia</string>
<string name="list_host_disconnect">Odpojiť</string>
<string name="list_host_edit">Zmeniť hostiteľa</string>
+ <string name="list_host_portforwards">Upraviť presmerovanie portov</string>
<string name="list_host_delete">Zmazať hostiteľa</string>
+ <string name="list_host_empty">Použite pole rýchleho pripojenia\nnižšie pre pripojenie k hostiteľovi.</string>
<string name="list_rotation_default">Predvolené</string>
+ <string name="list_rotation_land">Vynútiť na šírku</string>
+ <string name="list_rotation_port">Vynútiť na výšku</string>
<string name="list_rotation_auto">Automaticky</string>
+ <string name="list_camera_ctrlaspace">Ctrl+A , potom medzerník</string>
+ <string name="list_camera_ctrla">Ctrl+A</string>
+ <string name="list_camera_esc">Klávesa Esc</string>
+ <string name="list_camera_esc_a">Esc+A</string>
<string name="list_camera_none">Žiadny</string>
- <string name="delete_pos">Ano, vymazať</string>
+ <string name="list_delkey_backspace">Backspace</string>
+ <string name="list_delkey_del">Delete</string>
+ <string name="delete_message">Naozaj chcete zmazať \'%1$s\'?</string>
+ <string name="delete_pos">Áno, vymazať</string>
<string name="delete_neg">Zrušiť</string>
<string name="wizard_agree">Súhlasím</string>
<string name="wizard_next">Ďalej</string>
<string name="wizard_back">Späť</string>
- <string name="upgrade_pos">Ano, nahrať novú verziu</string>
- <string name="upgrade_neg">Nie teraz</string>
+ <string name="terminal_no_hosts_connected">Žiadny hostiteľ nie je pripijený</string>
+ <string name="terminal_connecting">Pripájanie k %1$s:%2$d cez %3$s</string>
+ <string name="terminal_sucess">Hostiteľ overený \'%1$s\' kľúč: %2$s</string>
+ <string name="terminal_failed">Overenie kľúča hostiteľa neúspešné.</string>
+ <string name="terminal_using_s2c_algorithm">Server-klient algoritmus: %1$s %2$s</string>
+ <string name="terminal_using_c2s_algorithm">Klient-server algoritmus: %1$s %2$s</string>
+ <string name="terminal_using_algorithm">Použitie algoritmu: %1$s %2$s</string>
+ <string name="terminal_auth">Pokus o overenie</string>
+ <string name="terminal_auth_pass">Pokus o overenie heslom</string>
+ <string name="terminal_auth_pass_fail">Overenie heslom neúspešné</string>
+ <string name="terminal_auth_pubkey_any">Pokus o overenie verejným kľúčom z pamäti</string>
+ <string name="terminal_auth_pubkey_invalid">Vybraný verejný kľúč je neplatný, skúste zvoliť kľúč v editore hostiteľov</string>
+ <string name="terminal_auth_pubkey_specific">Pokus o overenie konkrétnym verejným kľúčom</string>
+ <string name="terminal_auth_pubkey_fail">Overenie verejným kľúčom \'%1$s\' zlyhalo</string>
+ <string name="terminal_auth_ki">Pokus o overenie cez interaktívnu klávesnicu</string>
+ <string name="terminal_auth_ki_fail">Overenie cez interaktívnu klávesnicu zlyhalo</string>
+ <string name="terminal_auth_fail">[Váš hostiteľ nepodporuje overenie heslom alebo overenie cez interaktívnu klávesnicu.]</string>
+ <string name="terminal_no_session">Sedenie nebude spustené z dôvodu nastavenia hostiteľa.</string>
+ <string name="terminal_enable_portfoward">Zapnúť presmerovanie portu: %1$s</string>
+ <string name="local_shell_unavailable">Chyba! Lokálny shell je nedostupný na tomto telefóne.</string>
+ <string name="notification_text">%1$s vyžaduje vašu pozornosť.</string>
<string name="no">Nie</string>
+ <string name="with_confirmation">S potvrdením</string>
<string name="yes">Áno</string>
- <string name="exceptions_submit_message">It appears ConnectBot had a problem last time it ran. Submit error report to ConnectBot developers?</string>
+ <string name="exceptions_submit_message">Ostatné spustenie ConnectBot asi skončilo chybou. Zaslať chýbovú správu vývojárom ConnectBot?</string>
<string name="menu_colors_reset">Pôvodné</string>
+ <string name="app_is_running">ConnectBot je spustený</string>
<string name="color_red">červená</string>
<string name="color_green">zelená</string>
<string name="color_blue">modrá</string>
- <string name="color_gray">šedá</string>
+ <string name="color_gray">sivá</string>
</resources>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 2998ed2..107cfcd 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -1,21 +1,185 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
- <string name="app_desc">Enostaven, močan, odprtokodni SSH odjemalec</string>
+ <string name="app_desc">Enostaven, močan, odprtokodni SSH odjemalec.</string>
+ <string name="service_desc">Vzdržuje SSH povezave in naložene javne ključe</string>
<string name="title_hosts_list">Gostitelji</string>
- <string name="title_host_editor">Uredi gostitelja</string>
+ <string name="title_pubkey_list">Javni ključi</string>
+ <string name="title_port_forwards_list">Posredovana vrata</string>
+ <string name="title_host_editor">Urejanje gostitelja</string>
<string name="title_help">Pomoč</string>
<string name="title_colors">Barve</string>
<string name="resolve_connect">Poveži</string>
+ <string name="resolve_entropy">Zberi entropijo</string>
<string name="menu_insert">Dodaj gostitelja</string>
<string name="menu_delete">Odstrani gostitelja</string>
<string name="menu_preferences">Možnosti</string>
- <string name="help_about">Vizitka</string>
+ <string name="help_intro">Izberite temo spodaj za več informacij o določeni zadevi.</string>
+ <string name="help_about">O ConnectBotu</string>
<string name="help_keyboard">Tipkovnica</string>
+ <string name="pubkey_generate">Ustvari</string>
<string name="pubkey_import">Uvozi javni ključ</string>
- <string name="pubkey_delete">Zbriši ključ</string>
- <string name="pubkey_gather_entropy">Pridobivanje entropije</string>
- <string name="pubkey_list_empty">Tap Menu to create\nor import key pairs.</string>
- <string name="portforward_list_empty">Tap Menu to create\nport forwards.</string>
- <string name="list_format_error">Use the format %1$s</string>
+ <string name="pubkey_delete">Izbriši ključ</string>
+ <string name="pubkey_gather_entropy">Zbiranje entropije</string>
+ <string name="pubkey_touch_prompt">Dotaknite to polje za zbiranje naključnih vrednosti: %1$d%% končano</string>
+ <string name="pubkey_touch_hint">Da bi zagotovili naključnost med ustvarjanjem ključa, naključno premikajte prst po polju spodaj.</string>
+ <string name="pubkey_generating">Ustvarjanje para ključev</string>
+ <string name="pubkey_copy_private">Kopirajte zasebni ključ</string>
+ <string name="pubkey_copy_public">Kopirajte javni ključ</string>
+ <string name="pubkey_list_empty">Dotaknite \"Meni\" za ustvarjanje\nali uvažanje para ključev</string>
+ <string name="pubkey_unknown_format">Neznana vrsta</string>
+ <string name="pubkey_change_password">Spremenite geslo</string>
+ <string name="pubkey_list_pick">Izberite iz /sdcard</string>
+ <string name="pubkey_import_parse_problem">Problem pri razčlenjevanju uvoženega zasebnega ključa</string>
+ <string name="pubkey_unlock">Odkleni ključ</string>
+ <string name="pubkey_failed_add">Napačno geslo za ključ \'%1$s. Overitev spodletela.</string>
+ <string name="pubkey_memory_load">Naloži v spomin</string>
+ <string name="pubkey_memory_unload">Razloži s spomina</string>
+ <string name="pubkey_load_on_start">Naloži ključ ob začetku</string>
+ <string name="pubkey_confirm_use">Potrdi pred uporabo</string>
+ <string name="portforward_list_empty">Dotakni \"Meni\" za ustvarjanje\nposredovanih vrat.</string>
+ <string name="portforward_edit">Urejaj posredovana vrata</string>
+ <string name="portforward_delete">Izbriši posredovana vrata</string>
+ <string name="prompt_nickname">Vzdevek:</string>
+ <string name="prompt_nickname_hint_pubkey">Moj delovni ključ</string>
+ <string name="prompt_source_port">Izvorna vrata:</string>
+ <string name="prompt_destination">Cilj:</string>
+ <string name="prompt_old_password">Staro geslo:</string>
+ <string name="prompt_password">Geslo:</string>
+ <string name="prompt_again">(ponovno)</string>
+ <string name="prompt_type">Vrsta:</string>
+ <string name="prompt_password_can_be_blank">Opomba: geslo je lahko prazno</string>
+ <string name="prompt_bits">Biti:</string>
+ <string name="prompt_pubkey_password">Geslo za ključ \'%1$s\'</string>
+ <string name="prompt_allow_agent_to_use_key">Dovoli oddaljenemu gostitelju\nuporabo ključa \'%1$s\'?</string>
+ <string name="host_verification_failure_warning_header">OPOZORILO: ISTOVETENJE ODDALJENEGA GOSTITELJA SE JE SPREMENILO!</string>
+ <string name="host_verification_failure_warning">MOŽNO JE, DA NEKDO POČNE NEKAJ ZLOBNEGA!\nNekdo vam lahko prisluškuje (napad s posrednikom)!\nMožno pa je tudi, da se je spremenil ključ gostitelja.</string>
+ <string name="prompt_host_disconnected">Gostitelj je prekinil povezavo.\nZaprem sejo?</string>
+ <string name="prompt_continue_connecting">Ali ste prepričani, da želite\nnadaljevati s povezovanjem?</string>
+ <string name="host_authenticity_warning">Istovetnost gostitelja \'%1$s\' ni mogoče ugotoviti.</string>
+ <string name="host_fingerprint">Ključ gostitelja %1$s ima zgoščeno vrednost %2$s</string>
+ <string name="alert_passwords_do_not_match_msg">Gesla se ne ujemajo!</string>
+ <string name="alert_wrong_password_msg">Napačno geslo!</string>
+ <string name="alert_key_corrupted_msg">Zasebni ključ izgleda pokvarjen!</string>
+ <string name="alert_sdcard_absent">Kartica SD ni vstavljena!</string>
+ <string name="button_add">Dodaj</string>
+ <string name="button_change">Spremeni</string>
+ <string name="button_generate">Ustvari ključ</string>
+ <string name="button_resize">Spremeni velikost</string>
+ <string name="alert_disconnect_msg">Povezava prekinjena</string>
+ <string name="msg_copyright">Avtorske pravice © 2007-2008 Kenny Root http://the-b.org/, Jeffrey Sharkey http://jsharkey.org/</string>
+ <string name="pref_emulation_category">Posnemanje terminala</string>
+ <string name="pref_emulation_title">Način posnemanja</string>
+ <string name="pref_emulation_summary">Način posnemanja terminala za uporabo za PTY povezave</string>
+ <string name="pref_scrollback_title">Velikost pomnjenja vrstic</string>
+ <string name="pref_scrollback_summary">Velikost medpomnilnika pomnjenih vrstic ohraniti v spomnu za vsako konzolo</string>
+ <string name="pref_ui_category">Uporabniški vmesnik</string>
+ <string name="pref_rotation_title">Način za sukanje</string>
+ <string name="pref_rotation_summary">Kako spremeniti sukanje, ko tipkovnica skoči ven/noter</string>
+ <string name="pref_fullscreen_title">Celozaslonski način</string>
+ <string name="pref_fullscreen_summary">Skrij vrstico stanja, ko v konzoli</string>
+ <string name="pref_memkeys_title">Zapomni ključe v spominu</string>
+ <string name="pref_memkeys_summary">Hrani odklenjene ključe v spomnu dokler storitev v zaledju ni zaključena</string>
+ <string name="pref_conn_persist_title">Obstojne povezave</string>
+ <string name="pref_conn_persist_summary">Prisili povezave, da ostanejo povezane v ozadju</string>
+ <string name="pref_keymode_title">Bližnjice mape</string>
+ <string name="pref_keymode_summary">Izberite kako uporabiti Alt za \'/\' ter Shift za Tab</string>
+ <string name="pref_camera_title">Bližnjica kamere</string>
+ <string name="pref_camera_summary">Izberite, katera bližnjica se sproži, ko se pritisne gumb kamere</string>
+ <string name="pref_keepalive_title">Ohrani zaslon dejaven</string>
+ <string name="pref_keepalive_summary">Prepreči ugašanje zaslona med delom v konzoli</string>
+ <string name="pref_wifilock_title">Ohrani Wi-Fi dejaven</string>
+ <string name="pref_wifilock_summary">Prepreči ugašanje Wi-Fija med dejavno sejo</string>
+ <string name="pref_bumpyarrows_title">Izbočene puščice</string>
+ <string name="pref_bell_category">Zvonec terminala</string>
+ <string name="pref_bell_title">Slišen zvonec</string>
+ <string name="pref_bell_volume_title">Glasnost zvonca</string>
+ <string name="pref_bell_vibrate_title">Vibriraj ob zvoncu</string>
+ <string name="pref_bell_notification_title">Obvestila ozadja</string>
+ <string name="list_keymode_left">Uporabi ključe z leve strani</string>
+ <string name="list_keymode_none">Onemogoči</string>
+ <string name="list_pubkeyids_none">Ne uporabljaj ključev</string>
+ <string name="list_pubkeyids_any">Uporabi kateregakoli odklenjenega ključa</string>
+ <string name="hostpref_nickname_title">Vzdevek</string>
+ <string name="hostpref_color_title">Barvna kategorija</string>
+ <string name="hostpref_fontsize_title">Velikost pisave (pt)</string>
+ <string name="hostpref_pubkeyid_title">Uporabi overitev z javnim ključem</string>
+ <string name="hostpref_authagent_title">Uporabi SSH overitvenega agenta</string>
+ <string name="hostpref_compression_title">Stiskanje</string>
+ <string name="hostpref_compression_summary">To vam lahko pomaga pri počasnejših omrežjih</string>
+ <string name="hostpref_wantsession_title">Začni sejo lupine</string>
+ <string name="hostpref_stayconnected_title">Ostanite povezani</string>
+ <string name="hostpref_stayconnected_summary">Poskusi se ponovno povezati, če se prekine povezava</string>
+ <string name="hostpref_delkey_title">Tipka DEL</string>
+ <string name="hostpref_delkey_summary">Poslana koda tipke, ko je tipka DEL pritisnjena</string>
+ <string name="hostpref_encoding_title">Kodiranje</string>
+ <string name="hostpref_encoding_summary">Kodiranje znakov za gostitelja</string>
+ <string name="hostpref_connection_category">Nastavitve povezave</string>
+ <string name="hostpref_username_title">Uporabniško ime</string>
+ <string name="hostpref_hostname_title">Gostitelj</string>
+ <string name="hostpref_port_title">Vrata</string>
+ <string name="bind_never">Nikoli povezani</string>
+ <string name="console_menu_close">Zapri</string>
+ <string name="console_menu_copy">Kopiraj</string>
+ <string name="console_menu_paste">Prilepi</string>
+ <string name="console_menu_portforwards">Posredovanje vrat</string>
+ <string name="button_yes">Da</string>
+ <string name="button_no">Ne</string>
+ <string name="portforward_local">Krajevno</string>
+ <string name="portforward_remote">Oddaljeno</string>
+ <string name="portforward_pos">Ustvari posredovanje vrat</string>
+ <string name="portforward_done">Uspešno ustvarjeno posredovanje vrat</string>
+ <string name="portforward_menu_add">Dodaj posredovanje vrat</string>
+ <string name="hint_userhost">uporabnik\@gostitelj</string>
+ <string name="list_format_error">Uporabi vrsto %1$s</string>
+ <string name="format_username">Uporabniško ime</string>
+ <string name="format_hostname">ime gostitelja</string>
+ <string name="format_port">vrata</string>
+ <string name="list_menu_pubkeys">Upravljanje z javnimi ključi</string>
+ <string name="list_menu_sortcolor">Razvrsti po barvi</string>
+ <string name="list_menu_sortname">Razvrsti po imenu</string>
+ <string name="list_menu_settings">Nastavitve</string>
+ <string name="list_host_disconnect">Prekini povezavo</string>
+ <string name="list_host_edit">Urejaj gostitelja</string>
+ <string name="list_host_portforwards">Urejaj posredovanja vrat</string>
+ <string name="list_host_delete">Izbriši gostitelja</string>
+ <string name="list_rotation_default">Privzeto</string>
+ <string name="list_rotation_land">Prisili ležeče</string>
+ <string name="list_rotation_port">Prisili pokončno</string>
+ <string name="list_rotation_auto">Samodejno</string>
+ <string name="list_camera_ctrlaspace">Ctrl+A nato Space</string>
+ <string name="list_camera_ctrla">Ctrl+A</string>
+ <string name="list_camera_esc">Esc</string>
+ <string name="list_camera_esc_a">Esc+A</string>
+ <string name="list_camera_none">Nič</string>
+ <string name="list_delkey_backspace">Vračalka</string>
+ <string name="list_delkey_del">Delete</string>
+ <string name="delete_message">Ali ste prepričani, da želite izbrisati \'%1$s\'?</string>
+ <string name="delete_pos">Da, izbriši.</string>
+ <string name="delete_neg">Prekliči</string>
+ <string name="wizard_agree">Se strinjam</string>
+ <string name="wizard_next">Naprej</string>
+ <string name="wizard_back">Nazaj</string>
+ <string name="terminal_no_hosts_connected">Trenutno ni noben gostitelj povezan</string>
+ <string name="terminal_connecting">Povezujem se na %1$s:%2$d preko %3$s</string>
+ <string name="terminal_sucess">Preverjen gostitelj \'%1$s\' ključ: %2$s</string>
+ <string name="terminal_failed">Preverjanje ključa gostitelja spodletelo.</string>
+ <string name="terminal_using_s2c_algorithm">Strežnik - odjemalec algoritem: %1$s %2$s</string>
+ <string name="terminal_using_c2s_algorithm">Odjemalec - strežnik algoritem: %1$s %2$s</string>
+ <string name="terminal_using_algorithm">Uporabljam algoritem: %1$s %2$s</string>
+ <string name="terminal_auth">Poskušam se overit</string>
+ <string name="terminal_auth_pass">Poskušam overitev z geslom</string>
+ <string name="terminal_auth_pass_fail">Overitev z geslom spodletela</string>
+ <string name="terminal_auth_pubkey_fail">Spodletela avtentikacijska metoda \'publickey\' z ključem \'%1$s\'</string>
+ <string name="terminal_auth_fail">[Vaš gostitelj ne podpira \'password\' ali \'keyboard-interactive\' avtentikacije.]</string>
+ <string name="notification_text">%1$s želi vašo pozornost.</string>
+ <string name="no">Ne</string>
+ <string name="with_confirmation">S potrditvijo</string>
+ <string name="yes">Da</string>
<string name="exceptions_submit_message">It appears ConnectBot had a problem last time it ran. Submit error report to ConnectBot developers?</string>
+ <string name="menu_colors_reset">Ponastavi</string>
+ <string name="app_is_running">ConnectBot je dejaven</string>
+ <string name="color_red">rdeča</string>
+ <string name="color_green">zelena</string>
+ <string name="color_blue">modra</string>
+ <string name="color_gray">siva</string>
</resources>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
new file mode 100644
index 0000000..d499a5d
--- /dev/null
+++ b/res/values-sr/strings.xml
@@ -0,0 +1,228 @@
+<?xml version='1.0' encoding='utf-8'?>
+<resources>
+ <string name="app_desc">Једноставан, моћан, ССХ клијент отвореног кôда.</string>
+ <string name="service_desc">Одржава ССХ везе и учитане јавне кључеве</string>
+ <string name="title_hosts_list">Домаћини</string>
+ <string name="title_pubkey_list">Јавни кључеви</string>
+ <string name="title_port_forwards_list">Прослеђени портови</string>
+ <string name="title_host_editor">Уреди домаћина</string>
+ <string name="title_help">Помоћ</string>
+ <string name="title_colors">Боје</string>
+ <string name="title_color_picker">Изаберите боју</string>
+ <string name="resolve_connect">Повежи</string>
+ <string name="resolve_entropy">Сакупи ентропију</string>
+ <string name="menu_insert">Додај домаћина</string>
+ <string name="menu_delete">Обриши домаћина</string>
+ <string name="menu_preferences">Поставке</string>
+ <string name="help_intro">Изаберите тему испод за више информација о поједином предмету.</string>
+ <string name="help_about">О Конектботу</string>
+ <string name="help_keyboard">Тастатура</string>
+ <string name="pubkey_generate">Генериши</string>
+ <string name="pubkey_import">Увези</string>
+ <string name="pubkey_delete">Обриши кључ</string>
+ <string name="pubkey_gather_entropy">Сакупљам ентропију</string>
+ <string name="pubkey_touch_prompt">Додирните овде да бисте сакупили ентропију: %1$d%% завршено</string>
+ <string name="pubkey_touch_hint">Да бисте осигурали насумичност током генерисања кључа, померајте прст насумично преко поља испод.</string>
+ <string name="pubkey_generating">Генеришем пар кључева...</string>
+ <string name="pubkey_copy_private">Копирај приватни кључ</string>
+ <string name="pubkey_copy_public">Копирај јавни кључ</string>
+ <string name="pubkey_list_empty">Тапните „Мени“ да бисте направили \nили увезли пар кључева.</string>
+ <string name="pubkey_unknown_format">Непознат формат</string>
+ <string name="pubkey_change_password">Измени лозинку</string>
+ <string name="pubkey_list_pick">Изабери са /sdcard</string>
+ <string name="pubkey_import_parse_problem">Проблем рашчлањивања увезеног приватног кључа</string>
+ <string name="pubkey_unlock">Откључај кључ</string>
+ <string name="pubkey_failed_add">Лоша лозинка за кључ „%1$s“. Аутентификација није успела.</string>
+ <string name="pubkey_memory_load">Учитај у меморију</string>
+ <string name="pubkey_memory_unload">Уклони из меморије</string>
+ <string name="pubkey_load_on_start">Учитај кључеве по покретању</string>
+ <string name="pubkey_confirm_use">Потврди пре употребе</string>
+ <string name="portforward_list_empty">Тапните „Мени“ да бисте направили\nпрослеђење порта.</string>
+ <string name="portforward_edit">Уреди прослеђење порта</string>
+ <string name="portforward_delete">Обриши прослеђење порта</string>
+ <string name="prompt_nickname">Надимак:</string>
+ <string name="prompt_nickname_hint_pubkey">Мој радни кључ</string>
+ <string name="prompt_source_port">Изворни порт:</string>
+ <string name="prompt_destination">Одредиште:</string>
+ <string name="prompt_old_password">Стара лозинка:</string>
+ <string name="prompt_password">Лозинка:</string>
+ <string name="prompt_again">(поново)</string>
+ <string name="prompt_type">Тип:</string>
+ <string name="prompt_password_can_be_blank">Напомена: лозинка може да буде празна</string>
+ <string name="prompt_bits">Бита:</string>
+ <string name="prompt_pubkey_password">Лозинка за кључ „%1$s“</string>
+ <string name="prompt_allow_agent_to_use_key">Дозволити удаљеном домаћину да\nкористи тастер „%1$s“?</string>
+ <string name="host_verification_failure_warning_header">УПОЗОРЕЊЕ: ИДЕНТИФИКАЦИЈА УДАЉЕНОГ ДОМАЋИНА ЈЕ ИЗМЕЊЕНА!</string>
+ <string name="host_verification_failure_warning">МОГУЋЕ ЈЕ ДА НЕКО РАДИ НЕШТО ЛОШЕ!\nНеко вас можда управо прислушкује (човек-између напад)!\nТакође је могуће да је кључ домаћина управо измењен.</string>
+ <string name="prompt_host_disconnected">Домаћин је прекинуо везу.\nДа затворим сесију?</string>
+ <string name="prompt_continue_connecting">Желите ли заиста да\nнаставите повезивање?</string>
+ <string name="host_authenticity_warning">Не могу да утврдим аутентичност домаћина „%1$s“.</string>
+ <string name="host_fingerprint">Отисак домаћина %1$s је %2$s</string>
+ <string name="alert_passwords_do_not_match_msg">Лозинке се поклапају!</string>
+ <string name="alert_wrong_password_msg">Погрешна лозинка!</string>
+ <string name="alert_key_corrupted_msg">Изгледа да је приватни кључ покварен!</string>
+ <string name="alert_sdcard_absent">СД картица није уметнута!</string>
+ <string name="button_add">Додај</string>
+ <string name="button_change">Измени</string>
+ <string name="button_generate">Генериши кључ</string>
+ <string name="button_resize">Промени величину</string>
+ <string name="alert_disconnect_msg">Веза изгубљена</string>
+ <string name="msg_copyright">Ауторско право © 2007-2008 Kenny Root http://the-b.org/, Jeffrey Sharkey http://jsharkey.org/</string>
+ <string name="pref_emulation_category">Емулација терминала</string>
+ <string name="pref_emulation_title">Режим емулације</string>
+ <string name="pref_emulation_summary">Режим емулације терминала за PTY везе</string>
+ <string name="pref_scrollback_title">Величина преписа</string>
+ <string name="pref_scrollback_summary">Величина бафера преписа који се држи у меморији за сваку конзолу</string>
+ <string name="pref_ui_category">Корисничко сучеље</string>
+ <string name="pref_rotation_title">Режим ротације</string>
+ <string name="pref_rotation_summary">Како променити ротацију при извлачењу/увлачењу тастатуре</string>
+ <string name="pref_titlebarhide_title">Аутоматски сакриј траку наслова</string>
+ <string name="pref_titlebarhide_summary">Тапните конзолу да бисте приказали траку наслова и приступили менију</string>
+ <string name="pref_fullscreen_title">Цео екран</string>
+ <string name="pref_fullscreen_summary">Сакриј траку стања при раду у конзоли</string>
+ <string name="pref_keyboard_category">Тастатура</string>
+ <string name="pref_shiftfkeys_title">Shift+num су F-тастери</string>
+ <string name="pref_shiftfkeys_summary">На хардверским тастатурама, тастери бројева шаљу F1-F10 са Shift</string>
+ <string name="pref_ctrlfkeys_title">Ctrl+num су F-тастери</string>
+ <string name="pref_ctrlfkeys_summary">На софтверским тастатурама, тастери бројева шаљу F1-F10 са Ctrl</string>
+ <string name="pref_volumefont_title">Тастери за звук мењају вел. фонта</string>
+ <string name="pref_volumefont_summary">Величину фонта такође можете да мењате у поставкама по домаћину</string>
+ <string name="pref_memkeys_title">Држи кључеве у меморији</string>
+ <string name="pref_memkeys_summary">Држи откључане кључеве у меморији док се позадински сервис не оконча</string>
+ <string name="pref_conn_persist_title">Трајне везе</string>
+ <string name="pref_conn_persist_summary">Присили одржање веза при раду у позадини</string>
+ <string name="pref_keymode_title">Пречице директоријума</string>
+ <string name="pref_keymode_summary">Одредите како користити Alt за „/“ и Shift за табулатор</string>
+ <string name="pref_stickymodifiers_title">Лепљиви модификатори</string>
+ <string name="pref_stickymodifiers_summary">Модификаторски тастери остају укључени до притиска на други тастер</string>
+ <string name="only_alt">Само Alt</string>
+ <string name="pref_camera_title">Пречица камере</string>
+ <string name="pref_camera_summary">Одредите пречицу за окидање по притиску на тастер камере</string>
+ <string name="pref_keepalive_title">Држи екран укљученим</string>
+ <string name="pref_keepalive_summary">Спречи искључивање екрана при раду у конзоли</string>
+ <string name="pref_wifilock_title">Држи бежични активним</string>
+ <string name="pref_wifilock_summary">Спречи искључивање бежичног кад је сесија активна</string>
+ <string name="pref_bumpyarrows_title">Џомбасте стрелице</string>
+ <string name="pref_bumpyarrows_summary">Вибрирај по слању тастера стрелице са трекбола; корисно при спорим везама</string>
+ <string name="pref_bell_category">Звоно терминала</string>
+ <string name="pref_bell_title">Звучно звоно</string>
+ <string name="pref_bell_volume_title">Јачина звука звона</string>
+ <string name="pref_bell_vibrate_title">Вибрирај на звоно</string>
+ <string name="pref_bell_notification_title">Позадинска обавештења</string>
+ <string name="pref_bell_notification_summary">Прикажи обавештење кад терминал у позадини огласи звоно.</string>
+ <string name="list_keymode_right">Користи тастере десне стране</string>
+ <string name="list_keymode_left">Користи тастере леве стране</string>
+ <string name="list_keymode_none">Онемогући</string>
+ <string name="list_pubkeyids_none">Не користи кључеве</string>
+ <string name="list_pubkeyids_any">Користи било који откључани кључ</string>
+ <string name="hostpref_nickname_title">Надимак</string>
+ <string name="hostpref_color_title">Категорија боје</string>
+ <string name="hostpref_fontsize_title">Величина фонта (pt)</string>
+ <string name="hostpref_pubkeyid_title">Користи аутентификацију кључем</string>
+ <string name="hostpref_authagent_title">Користи ССХ агента за аутентификацију</string>
+ <string name="hostpref_postlogin_title">Аутоматизација по пријави</string>
+ <string name="hostpref_postlogin_summary">Наредбе за покретање на удаљеном серверу након аутентификације</string>
+ <string name="hostpref_compression_title">Компресија</string>
+ <string name="hostpref_compression_summary">Ово може да помогне при спорим мрежама</string>
+ <string name="hostpref_wantsession_title">Покрени сесију шкољке</string>
+ <string name="hostpref_wantsession_summary">Онемогућите да бисте користили само прослеђења портова</string>
+ <string name="hostpref_stayconnected_title">Остани повезан</string>
+ <string name="hostpref_stayconnected_summary">Покушај се поново повезати по прекиду везе</string>
+ <string name="hostpref_quickdisconnect_title">Затвори по прекиду</string>
+ <string name="hostpref_quickdisconnect_summary">Затвори без питања одмах по прекиду везе.</string>
+ <string name="hostpref_delkey_title">DEL тастер</string>
+ <string name="hostpref_delkey_summary">Кôд тастера који се шаље притиском на DEL тастер</string>
+ <string name="hostpref_encoding_title">Кодирање</string>
+ <string name="hostpref_encoding_summary">Кодирање за домаћина</string>
+ <string name="hostpref_connection_category">Поставке везе</string>
+ <string name="hostpref_username_title">Корисничко име</string>
+ <string name="hostpref_hostname_title">Домаћин</string>
+ <string name="hostpref_port_title">Порт</string>
+ <string name="bind_never">Никад повезиван</string>
+ <string name="console_copy_done">Копирано %1$d бајта на клипборд</string>
+ <string name="console_copy_start">Додирните и превуците\nили користите дирекциони контролер\nда изаберете површину за копирање</string>
+ <string name="console_menu_close">Затвори</string>
+ <string name="console_menu_copy">Копирај</string>
+ <string name="console_menu_paste">Налепи</string>
+ <string name="console_menu_portforwards">Прослеђења портова</string>
+ <string name="console_menu_resize">Наметни величину</string>
+ <string name="console_menu_urlscan">Тражење УРЛова</string>
+ <string name="button_yes">Да</string>
+ <string name="button_no">Не</string>
+ <string name="portforward_local">локални</string>
+ <string name="portforward_remote">удаљени</string>
+ <string name="portforward_dynamic">динамички (СОЦКС)</string>
+ <string name="portforward_pos">Направи прослеђење порта</string>
+ <string name="portforward_done">Успешно направљено прослеђење порта</string>
+ <string name="portforward_problem">Појавио се проблем при прављењу прослеђења порта. Можда користите портове испод 1024 или је порт већ у употреби?</string>
+ <string name="portforward_menu_add">Додај прослеђење порта</string>
+ <string name="hint_userhost">korisnik\@domacin</string>
+ <string name="list_format_error">Користи формат „%1$s“</string>
+ <string name="format_username">korisnik</string>
+ <string name="format_hostname">domacin</string>
+ <string name="format_port">port</string>
+ <string name="list_menu_pubkeys">Управљај кључевима</string>
+ <string name="list_menu_sortcolor">Поређај по боји</string>
+ <string name="list_menu_sortname">Поређај по имену</string>
+ <string name="list_menu_settings">Поставке</string>
+ <string name="list_host_disconnect">Прекини везу</string>
+ <string name="list_host_edit">Уреди домаћина</string>
+ <string name="list_host_portforwards">Уреди прослеђења портова</string>
+ <string name="list_host_delete">Обриши домаћина</string>
+ <string name="list_host_empty">Користите поље за брзо повезивање \nиспод да бисте се повезали са домаћином.</string>
+ <string name="list_rotation_default">Подразумевана</string>
+ <string name="list_rotation_land">Наметни пејзаж</string>
+ <string name="list_rotation_port">Наметни портрет</string>
+ <string name="list_rotation_auto">Аутоматска</string>
+ <string name="list_camera_ctrlaspace">Ctrl+A онда размакница</string>
+ <string name="list_camera_ctrla">Ctrl+A</string>
+ <string name="list_camera_esc">Esc</string>
+ <string name="list_camera_esc_a">Esc+A</string>
+ <string name="list_camera_none">Ништа</string>
+ <string name="list_delkey_backspace">Backspace</string>
+ <string name="list_delkey_del">Обриши</string>
+ <string name="delete_message">Желите ли заиста да обришете „%1$s“?</string>
+ <string name="delete_pos">Да, обриши</string>
+ <string name="delete_neg">Одустани</string>
+ <string name="wizard_agree">Прихваћам</string>
+ <string name="wizard_next">Следеће</string>
+ <string name="wizard_back">Назад</string>
+ <string name="terminal_no_hosts_connected">Нема повезаних домаћина тренутно</string>
+ <string name="terminal_connecting">Повезујем се са %1$s:%2$d преко %3$s</string>
+ <string name="terminal_sucess">Verified host \'%1$s\' key: %2$s</string>
+ <string name="terminal_failed">Host key verification failed.</string>
+ <string name="terminal_using_s2c_algorithm">Сервер-клијент алгоритам: %1$s %2$s</string>
+ <string name="terminal_using_c2s_algorithm">Клијент-сервер алгоритам: %1$s %2$s</string>
+ <string name="terminal_using_algorithm">Користим алгоритам: %1$s %2$s</string>
+ <string name="terminal_auth">Покушавам аутентификацију</string>
+ <string name="terminal_auth_pass">Покушавам аутентификацију лозинком</string>
+ <string name="terminal_auth_pass_fail">Метода аутентификације лозинком није успела</string>
+ <string name="terminal_auth_pubkey_any">Покушавам аутентификацију кључем помоћу било којег јавног кључа у меморији</string>
+ <string name="terminal_auth_pubkey_invalid">Изабрани јавни кључ није исправан, покушајте поново изабрати кључ у уређивачу домаћина</string>
+ <string name="terminal_auth_pubkey_specific">Покушавам аутентификацију кључем помоћу наведеног јавног кључа</string>
+ <string name="terminal_auth_pubkey_fail">Метода аутентификације кључем помоћу кључа „%1$s“ није успела</string>
+ <string name="terminal_auth_ki">Покушавам аутентификацију интерактивном тастатуром</string>
+ <string name="terminal_auth_ki_fail">Метода аутентификације интерактивном тастатуром није успела</string>
+ <string name="terminal_auth_fail">[Ваш домаћин не подржава аутентификацију лозинком или интерактивном тастатуром.]</string>
+ <string name="terminal_no_session">Сесија неће бити покренута због поставке домаћина.</string>
+ <string name="terminal_enable_portfoward">Омогући прослеђење порта: %1$s</string>
+ <string name="local_shell_unavailable">Неуспех! Нема локалне шкољке на овом телефону.</string>
+ <string name="notification_text">%1$s жели вашу пажњу.</string>
+ <string name="no">Не</string>
+ <string name="with_confirmation">Тражи потврду</string>
+ <string name="yes">Да</string>
+ <string name="exceptions_submit_message">"Чини се да је Конектбот имао проблем последњи пут кад је покренут. Да пошаљем извештај о грешци програмерима?"</string>
+ <string name="menu_colors_reset">Ресетуј</string>
+ <string name="app_is_running">Конектбот је покренут</string>
+ <string name="color_red">црвена</string>
+ <string name="color_green">зелена</string>
+ <string name="color_blue">плава</string>
+ <string name="color_gray">сива</string>
+ <string name="colors_fg_label">FG: %1$d</string>
+ <string name="color_bg_label">BG: %1$d</string>
+ <string name="image_description_connected">Повезан.</string>
+ <string name="image_description_key_is_locked">Кључ је закључан.</string>
+ <string name="image_description_toggle_control_character">Мењај контролни знак.</string>
+ <string name="image_description_send_escape_character">Пошаљи Esc знак.</string>
+ <string name="image_description_show_keyboard">Прикажи тастатуру.</string>
+</resources>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index ebd2b85..063d586 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -1,17 +1,18 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
- <string name="app_desc">Enkel, kraftfull, open-source SSH-klient.</string>
- <string name="title_hosts_list">Värdar</string>
+ <string name="app_desc">Enkel och kraftfull SSH-klient baserad på öppen källkod.</string>
+ <string name="service_desc">Upprätthåller SSH-anslutningar och laddade publika nycklar</string>
+ <string name="title_hosts_list">Värddatorer</string>
<string name="title_pubkey_list">Publika nycklar</string>
<string name="title_port_forwards_list">Vidarebefordring av portar</string>
- <string name="title_host_editor">Redigera värd</string>
+ <string name="title_host_editor">Redigera värddator</string>
<string name="title_help">Hjälp</string>
<string name="title_colors">Färger</string>
<string name="resolve_connect">Anslut</string>
- <string name="resolve_entropy">Samla in Entropi</string>
+ <string name="resolve_entropy">Samla in entropi</string>
<string name="menu_insert">Lägg till värd</string>
<string name="menu_delete">Ta bort värd</string>
- <string name="menu_preferences">Egenskaper</string>
+ <string name="menu_preferences">Inställningar</string>
<string name="help_intro">Var god välj ett ämne nedan som du skulle vilja ha mer information om.</string>
<string name="help_about">Om ConnectBot</string>
<string name="help_keyboard">Tangentbord</string>
@@ -22,8 +23,8 @@
<string name="pubkey_touch_prompt">Rör vid rutan för att samla in slumpdata: %1$d%% klart</string>
<string name="pubkey_touch_hint">För att försäkra dig om att nyckeln som skapas är slumpmässig, rör fingret slumpmässigt över skärmen.</string>
<string name="pubkey_generating">Genererar nyckelpar...</string>
- <string name="pubkey_copy_private">Kopiera privata nyckeln</string>
- <string name="pubkey_copy_public">Kopiera publika nyckeln</string>
+ <string name="pubkey_copy_private">Kopiera privat nyckel</string>
+ <string name="pubkey_copy_public">Kopiera publik nyckel</string>
<string name="pubkey_list_empty">Tryck på Meny för att\nskapa eller importera nyckelpar</string>
<string name="pubkey_unknown_format">Okänt format</string>
<string name="pubkey_change_password">Ändra lösenord</string>
@@ -39,8 +40,9 @@
<string name="portforward_edit">Ändra vidarebefordring av port</string>
<string name="portforward_delete">Ta bort vidarebefodring av port</string>
<string name="prompt_nickname">Namn:</string>
- <string name="prompt_nickname_hint_pubkey">Min jobbenyckel</string>
+ <string name="prompt_nickname_hint_pubkey">Min jobbnyckel</string>
<string name="prompt_source_port">Inkommande portnummer:</string>
+ <string name="prompt_destination">Destination:</string>
<string name="prompt_old_password">Gammalt lösenord:</string>
<string name="prompt_password">Lösenord:</string>
<string name="prompt_again">(igen)</string>
@@ -54,6 +56,7 @@
<string name="prompt_host_disconnected">Värden har kopplat ner anslutningen.\nStäng?</string>
<string name="prompt_continue_connecting">Är du säker att du vill\nfortsätta att ansluta?</string>
<string name="host_authenticity_warning">Identiteten av värden \'%1$s\' går ej att fastställa.</string>
+ <string name="host_fingerprint">Värd %1$s nyckel-fingeravtryck är %2$s</string>
<string name="alert_passwords_do_not_match_msg">Lösenorden överenstämmer inte med varandra!</string>
<string name="alert_wrong_password_msg">Felaktigt lösenord!</string>
<string name="alert_key_corrupted_msg">Privata nyckeln ser ut att vara korrupt!</string>
@@ -63,20 +66,21 @@
<string name="button_generate">Generera nyckel</string>
<string name="button_resize">Ändra storlek</string>
<string name="alert_disconnect_msg">Anslutningen förlorad</string>
+ <string name="msg_copyright">Copyright © 2007-2008 Kenny Root http://the-b.org/, Jeffrey Sharkey http://jsharkey.org/</string>
<string name="pref_emulation_category">Terminalemulering</string>
<string name="pref_emulation_title">Emuleringsläge</string>
<string name="pref_emulation_summary">Emuleringsläge för PTY-anslutningar</string>
- <string name="pref_scrollback_title">Antal rader att rulla tillbaka</string>
- <string name="pref_scrollback_summary">Storlek på tillbakarullningsbuffert för varje konsol</string>
+ <string name="pref_scrollback_title">Textbufferstorlek</string>
+ <string name="pref_scrollback_summary">Storlek på textbuffer för varje konsol</string>
<string name="pref_ui_category">Användargränssnitt</string>
<string name="pref_rotation_title">Rotationsläge</string>
<string name="pref_rotation_summary">Hur rotation skall ändras när tangentbord aktiveras/avaktiveras</string>
- <string name="pref_fullscreen_title">Fullskärm</string>
+ <string name="pref_fullscreen_title">Helskärm</string>
<string name="pref_fullscreen_summary">Göm status medan konsol visas</string>
<string name="pref_memkeys_title">Kom ihåg nycklar</string>
<string name="pref_memkeys_summary">Behåll olåsta nycklar i minnet tills bakände-tjänst avslutas</string>
- <string name="pref_update_title">Uppdateringskontroll</string>
- <string name="pref_update_summary">Intervall för att söka efter ConnectBot-uppdateringar</string>
+ <string name="pref_conn_persist_title">Kvarstående anslutningar</string>
+ <string name="pref_conn_persist_summary">Tvinga anslutningar att fortsätta i bakgrunden</string>
<string name="pref_keymode_title">Genvägar för katalog</string>
<string name="pref_keymode_summary">Välj hur Alt används för \'/\' och Shift för Tab</string>
<string name="pref_camera_title">Genväg för kamera</string>
@@ -85,32 +89,51 @@
<string name="pref_keepalive_summary">Håll skärmen aktiv medan konsolen används</string>
<string name="pref_wifilock_title">Håll trådlöst nätverk aktivt</string>
<string name="pref_wifilock_summary">Hindra att trådlöst nätverk slås av under en aktiv session</string>
+ <string name="pref_bumpyarrows_title">Aktiva piltangenter</string>
<string name="pref_bumpyarrows_summary">Vibrera när piltangenter sänds från pekdon; bra för långsamma anslutningar</string>
<string name="pref_bell_category">Terminal-ljud</string>
<string name="pref_bell_title">Aktivt</string>
<string name="pref_bell_volume_title">Ljudvolym</string>
<string name="pref_bell_vibrate_title">Vibrera</string>
+ <string name="pref_bell_notification_title">Bakgrundsmeddelanden</string>
+ <string name="pref_bell_notification_summary">Skicka meddelande när en terminal som körs i bakgrunden skickar ljudsignal.</string>
<string name="list_keymode_right">Använd högerknapparna</string>
<string name="list_keymode_left">Använd vänsterknapparna</string>
<string name="list_keymode_none">Avaktivera</string>
<string name="list_pubkeyids_none">Använd inte knapparna</string>
<string name="list_pubkeyids_any">Använd valfri olåst nyckel</string>
- <string name="list_update_daily">Varje dag</string>
- <string name="list_update_weekly">Varje vecka</string>
- <string name="list_update_never">Aldrig</string>
+ <string name="hostpref_nickname_title">Smeknamn</string>
+ <string name="hostpref_color_title">Färgkategori</string>
+ <string name="hostpref_fontsize_title">Teckenstorlek</string>
+ <string name="hostpref_pubkeyid_title">Använd publik nyckelverifiering</string>
+ <string name="hostpref_authagent_title">Använd SSH-verifieringsagent</string>
+ <string name="hostpref_postlogin_title">Automatisering efter inloggning</string>
+ <string name="hostpref_postlogin_summary">Kommandon att köras på fjärrservern när verifierad</string>
<string name="hostpref_compression_title">Kompression</string>
- <string name="hostpref_encoding_title">Kodar</string>
+ <string name="hostpref_compression_summary">Detta kan hjälpa på långsammare nätverk</string>
+ <string name="hostpref_wantsession_title">Starta shell-session</string>
+ <string name="hostpref_wantsession_summary">Inaktivera denna inställning för att bara använda vidarebefodring av portar</string>
+ <string name="hostpref_stayconnected_title">Fortsätt vara ansluten</string>
+ <string name="hostpref_stayconnected_summary">Försök återansluta till värden om frånkopplad</string>
+ <string name="hostpref_delkey_title">DEL-tangent</string>
+ <string name="hostpref_delkey_summary">Tangentkoden som sänds när DEL-tangenten trycks ner</string>
+ <string name="hostpref_encoding_title">Teckenkodning</string>
+ <string name="hostpref_encoding_summary">Teckenkodning för värddatorn</string>
+ <string name="hostpref_connection_category">Anslutningsinställningar</string>
<string name="hostpref_username_title">Användarnamn</string>
- <string name="bind_minutes">%1$s minuter sedan</string>
- <string name="bind_hours">%1$s timmar sendan</string>
- <string name="bind_days">%1$s dagar sedan</string>
+ <string name="hostpref_hostname_title">Värd</string>
+ <string name="hostpref_port_title">Port</string>
+ <string name="bind_never">Aldrig ansluten</string>
<string name="console_copy_done">Kopierade %1$d bytes till urklippshanteraren.</string>
<string name="console_copy_start">Tryck och drag\neller använd bollen\nför att markera en yta för kopiering</string>
<string name="console_menu_close">Stäng</string>
<string name="console_menu_copy">Kopiera</string>
<string name="console_menu_paste">Klistra in</string>
- <string name="console_menu_portforwards">Port vidarebefodringar</string>
+ <string name="console_menu_portforwards">Vidarebefodring av port</string>
<string name="console_menu_resize">Fast storlek</string>
+ <string name="console_menu_urlscan">URL-sökning</string>
+ <string name="button_yes">Acceptera</string>
+ <string name="button_no">Neka</string>
<string name="portforward_local">Lokal</string>
<string name="portforward_remote">Fjärr</string>
<string name="portforward_dynamic">Dynamisk (SOCKS)</string>
@@ -122,6 +145,7 @@
<string name="list_format_error">Använd formatet %1$s</string>
<string name="format_username">användarnamn</string>
<string name="format_hostname">värdnamn</string>
+ <string name="format_port">port</string>
<string name="list_menu_pubkeys">Hantera publika nycklar</string>
<string name="list_menu_sortcolor">Sortera efter färg</string>
<string name="list_menu_sortname">Sortera efter namn</string>
@@ -131,27 +155,52 @@
<string name="list_host_portforwards">Ändra vidarebefodringar av portar</string>
<string name="list_host_delete">Ta bort värd</string>
<string name="list_host_empty">Använd snabbanslutningen\nnedan för att ansluta till en värd.</string>
+ <string name="list_rotation_default">Förvalt värde</string>
<string name="list_rotation_land">Tvinga horisontellt läge</string>
<string name="list_rotation_port">Tvinga vertikalt läge</string>
<string name="list_rotation_auto">Automatiskt</string>
<string name="list_camera_ctrlaspace">Ctrl+A sen Space</string>
+ <string name="list_camera_ctrla">Ctrl+A</string>
+ <string name="list_camera_esc">Esc</string>
+ <string name="list_camera_esc_a">Esc+A</string>
<string name="list_camera_none">Ingen</string>
+ <string name="list_delkey_backspace">Baksteg</string>
+ <string name="list_delkey_del">Ta bort</string>
<string name="delete_message">Är du säker att du vill ta bort \'%1$s\'?</string>
<string name="delete_pos">Ja, ta bort</string>
<string name="delete_neg">Avbryt</string>
<string name="wizard_agree">Godkänn</string>
<string name="wizard_next">Nästa</string>
<string name="wizard_back">Tillbaka</string>
+ <string name="terminal_no_hosts_connected">Inga värdar anslutna</string>
+ <string name="terminal_connecting">Ansluter till %1$s:%2$d via %3$s</string>
+ <string name="terminal_sucess">Verifierad värd \'%1$s\' nyckel: %2$s</string>
<string name="terminal_failed">Verifikation av värd-nyckeln misslyckades.</string>
+ <string name="terminal_using_s2c_algorithm">Server-till-klient algoritm: %1$s %2$s</string>
+ <string name="terminal_using_c2s_algorithm">Klient-till-server algoritm: %1$s %2$s</string>
+ <string name="terminal_using_algorithm">Använder algoritm: %1$s %2$s</string>
<string name="terminal_auth">Försöker att autentisera</string>
<string name="terminal_auth_pass">Försöker med \'password\' autentisering</string>
<string name="terminal_auth_pass_fail">Autentiseringsmetoden \'password\' misslyckades</string>
+ <string name="terminal_auth_pubkey_any">Försöker \'publik nyckel\' verifiering med en nyckel från det lokala minnet</string>
+ <string name="terminal_auth_pubkey_invalid">Vald publik nyckel är ogiltig, försök välj ny nyckel i värd redigerare</string>
+ <string name="terminal_auth_pubkey_specific">Försöker \'publik nyckel\' verifiera med en specifik publik nyckel</string>
+ <string name="terminal_auth_pubkey_fail">Verifierings metod \'publik nyckel\' med nyckel \'%1$s\' misslyckades</string>
<string name="terminal_auth_ki">Försöker med \'keyboard-interactive\' autentisering</string>
<string name="terminal_auth_ki_fail">Autentiseringsmetoden \'keyboard-interactive\' misslyckades</string>
<string name="terminal_auth_fail">[Din värd stödjer inte \'password\' eller \'keyboard-interactive\' autentiseringsmetoderna]</string>
- <string name="upgrade">Ny version</string>
- <string name="upgrade_pos">Ja, uppgradera</string>
- <string name="upgrade_neg">Inte just nu</string>
+ <string name="terminal_no_session">Session kommer inte att startas på grund av värd önskemål.</string>
+ <string name="terminal_enable_portfoward">Aktivera vidarebefodring av port: %1$s</string>
+ <string name="local_shell_unavailable">Misslyckande! Lokal shell är otillgänglig på denna telefon</string>
+ <string name="notification_text">%1$s vill ha din uppmärksamhet.</string>
+ <string name="no">Nej</string>
+ <string name="with_confirmation">Med bekräftelse</string>
+ <string name="yes">Ja</string>
<string name="exceptions_submit_message">It appears ConnectBot had a problem last time it ran. Submit error report to ConnectBot developers?</string>
+ <string name="menu_colors_reset">Återställ</string>
+ <string name="app_is_running">ConnectBot är igång</string>
<string name="color_red">röd</string>
+ <string name="color_green">grön</string>
+ <string name="color_blue">blå</string>
+ <string name="color_gray">grå</string>
</resources>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 35625bb..22e2f48 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -1,49 +1,51 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
- <string name="app_desc">Basit, güçlü, açık kaynaklı SSH istemci.</string>
- <string name="title_hosts_list">Anasistemler</string>
- <string name="title_pubkey_list">Açık Anahtarlar</string>
- <string name="title_port_forwards_list">Erişim noktası yönlendirmeleri</string>
- <string name="title_host_editor">Anasistemi Düzenle</string>
+ <string name="app_desc">Basit, güçlü, açık kaynak kodlu SSH client</string>
+ <string name="service_desc">SSH bağlantılarını ve yüklenmiş açık-anahtarları muhafaza eder</string>
+ <string name="title_hosts_list">Hostlar</string>
+ <string name="title_pubkey_list">Açık-anahtarlar</string>
+ <string name="title_port_forwards_list">Port yönlendirme</string>
+ <string name="title_host_editor">Host\'u Düzenle</string>
<string name="title_help">Yardım</string>
<string name="title_colors">Renkler</string>
+ <string name="title_color_picker">Bir renk seç</string>
<string name="resolve_connect">Bağlan</string>
- <string name="resolve_entropy">Entropi Topla</string>
- <string name="menu_insert">Anasistem Ekle</string>
- <string name="menu_delete">Anasistemi Sil</string>
- <string name="menu_preferences">Tercihler</string>
- <string name="help_intro">Daha fazla bilgi almak için aşağıdaki konulardan birisini seçiniz.</string>
+ <string name="resolve_entropy">Rastgele girdi topla</string>
+ <string name="menu_insert">Host ekle</string>
+ <string name="menu_delete">Host\'u sil</string>
+ <string name="menu_preferences">Seçenekler</string>
+ <string name="help_intro">Özel bir konu hakkinda daha fazla bilgi için lütfen aşağıdaki ilgili konuyu seçiniz.</string>
<string name="help_about">ConnectBot Hakkında</string>
<string name="help_keyboard">Klavye</string>
<string name="pubkey_generate">Oluştur</string>
- <string name="pubkey_import">İçe aktar</string>
+ <string name="pubkey_import">Harici dosyadan aktar</string>
<string name="pubkey_delete">Anahtarı Sil</string>
<string name="pubkey_gather_entropy">Entropi Toplanıyor</string>
- <string name="pubkey_touch_prompt">Raslantısallık toplamak için bu kutuya dokun: %1$d%% gerçekleşti</string>
- <string name="pubkey_touch_hint">Anahtar oluşturma süresince raslantısallığı sağlamak için parmağınızı aşağıdaki kutu içinde rastgele haraket ettiriniz.</string>
+ <string name="pubkey_touch_prompt">Rastgele girdi toplamak için bu kutuya dokun: %1$d%% oluşturuldu</string>
+ <string name="pubkey_touch_hint">Anahtar oluşturmada gerekli rastgele girdi kaydedebilmemiz için parmağınızı aşağıdaki kutu üzerinde rastgele haraket ettiriniz.</string>
<string name="pubkey_generating">Anahtar çifti oluşturuluyor...</string>
- <string name="pubkey_copy_private">Kişisel anahtarı kopyala</string>
- <string name="pubkey_copy_public">Açık anahtarı kopyala</string>
- <string name="pubkey_list_empty">Anahtar çiftini yaratmak veya\niçeri aktarmak için Menü ye dokun</string>
- <string name="pubkey_unknown_format">Bilinmeyen biçim</string>
- <string name="pubkey_change_password">Parolayı değiştir</string>
- <string name="pubkey_list_pick">/sdcard \'dan seç</string>
- <string name="pubkey_import_parse_problem">İçeri aktarılan kişisel anahtarı ayrıştırma sorunu</string>
- <string name="pubkey_unlock">Anahtarın kilidini aç</string>
- <string name="pubkey_failed_add">Anahtar \'%1$s\' için hatalı parola. Kimlik doğrulama başarısız oldu.</string>
+ <string name="pubkey_copy_private">Özel-anahtarı kopyala</string>
+ <string name="pubkey_copy_public">Umumi-anahtarı kopyala</string>
+ <string name="pubkey_list_empty">Anahtar-çiftini oluşturmak için\nveya dişardan eklemek için \"Menü\"ye dokunun.</string>
+ <string name="pubkey_unknown_format">Tanınmayan dosya formatı</string>
+ <string name="pubkey_change_password">Şifreyi değiştir</string>
+ <string name="pubkey_list_pick">/sdcard dan aktar</string>
+ <string name="pubkey_import_parse_problem">Dışardan eklenen özel-anahtarı okurken sorun oluştu</string>
+ <string name="pubkey_unlock">Anahtarın şifresini giriniz</string>
+ <string name="pubkey_failed_add">Anahtar \'%1$s\' için hatalı şifre verdiniz. Kimlik doğrulama başarısız oldu.</string>
<string name="pubkey_memory_load">Belleğe yükle</string>
<string name="pubkey_memory_unload">Bellekten kaldır</string>
<string name="pubkey_load_on_start">Başlangıçta anahtarı yükle</string>
- <string name="pubkey_confirm_use">Kullanmadan önce doğrulat</string>
- <string name="portforward_list_empty">Kapı yönlendirme yaratmak\niçin Menü ye dokun</string>
- <string name="portforward_edit">Kapı yönlendirmeyi düzenle</string>
- <string name="portforward_delete">Kapı yönlendirmeyi sil</string>
- <string name="prompt_nickname">Takma Ad:</string>
- <string name="prompt_nickname_hint_pubkey">Açık anahtar için takma ad</string>
+ <string name="pubkey_confirm_use">Kullanım öncesi doğrulama</string>
+ <string name="portforward_list_empty">Port yönlendirmesi oluşturmak için\n\"Menü\"ye dokunun</string>
+ <string name="portforward_edit">Port yönlendirmeyi düzenle</string>
+ <string name="portforward_delete">Port yönlendirmeyi sil</string>
+ <string name="prompt_nickname">Rumuz:</string>
+ <string name="prompt_nickname_hint_pubkey">Umumi-anahtar rumuzu</string>
<string name="prompt_source_port">Kaynak kapı:</string>
<string name="prompt_destination">Hedef (Anasistem:Kapı):</string>
- <string name="prompt_old_password">Eski Parola:</string>
- <string name="prompt_password">Parola:</string>
+ <string name="prompt_old_password">Eski şifre:</string>
+ <string name="prompt_password">Şifre:</string>
<string name="prompt_again">(tekrar)</string>
<string name="prompt_type">Tip:</string>
<string name="prompt_password_can_be_blank">Not: parola boş bırakılabilir</string>
@@ -76,10 +78,11 @@
<string name="pref_rotation_summary">Klavye saklandığında/açıldığında dönüş nasıl değişir</string>
<string name="pref_fullscreen_title">Tüm ekran</string>
<string name="pref_fullscreen_summary">Konsolda iken durum çubuğunu gizle</string>
+ <string name="pref_keyboard_category">Klavye</string>
<string name="pref_memkeys_title">Anahtarları bellekte tut</string>
<string name="pref_memkeys_summary">Kilidi açılmış anahtarları arkadaki hizmetler sonlanana kadar bellekte tut</string>
- <string name="pref_update_title">Güncellemelere bak</string>
- <string name="pref_update_summary">ConnectBot güncellemelerine bakmak için en fazla sıklığı ayarla</string>
+ <string name="pref_conn_persist_title">Bağlantıları sürekli tut</string>
+ <string name="pref_conn_persist_summary">Arkaplanda iken bağlantıları bağlı tutmaya zorla</string>
<string name="pref_keymode_title">Dizin kısayolları</string>
<string name="pref_keymode_summary">\'/\' için Alt ve Tab için Shift kullanımının nasıl olacağını seç</string>
<string name="pref_camera_title">Kamera kısayolu</string>
@@ -88,43 +91,48 @@
<string name="pref_keepalive_summary">Bir konsolda çalışırken ekranın kapanmasını engelle</string>
<string name="pref_wifilock_title">Wi-Fi etkin tut</string>
<string name="pref_wifilock_summary">Bir oturum etkinken Wi-Fi kapanmasını engelle</string>
+ <string name="pref_bumpyarrows_title">dalgalı oklar</string>
<string name="pref_bumpyarrows_summary">Okları izotop ile kullanırken titret; düşük bağlantılar için kullanışlıdır</string>
<string name="pref_bell_category">Terminal zili</string>
<string name="pref_bell_title">Sesli zil</string>
<string name="pref_bell_volume_title">Zil ses düzeyi</string>
<string name="pref_bell_vibrate_title">Titreşim</string>
<string name="pref_bell_notification_title">Arkaplan uyarıları</string>
+ <string name="pref_bell_notification_summary">Arka planda çalışan terminal zil sesinde uyarı gönder</string>
<string name="list_keymode_right">Sağ taraftaki tuşları kullan</string>
<string name="list_keymode_left">Sol taraftaki tuşları kullan</string>
<string name="list_keymode_none">Devre Dışı Bırak</string>
<string name="list_pubkeyids_none">Tuşları kullanma</string>
<string name="list_pubkeyids_any">Herhangi bir tuşu kullan</string>
- <string name="list_update_daily">Günlük</string>
- <string name="list_update_weekly">Haftalık</string>
- <string name="list_update_never">Asla</string>
<string name="hostpref_nickname_title">Rumuz</string>
<string name="hostpref_color_title">Renk kategorisi</string>
<string name="hostpref_fontsize_title">Yazıtipi boyutu (pt)</string>
+ <string name="hostpref_pubkeyid_title">Umumi-anahtar doğrulaması yap</string>
+ <string name="hostpref_authagent_title">SSH auth agent teyidi kullan</string>
+ <string name="hostpref_postlogin_title">post-login otomasyonu</string>
<string name="hostpref_postlogin_summary">Uzak sunucuya bağlanıldığında çalıştırılacak komutlar</string>
<string name="hostpref_compression_title">Sıkıştırma</string>
<string name="hostpref_compression_summary">Yavaş ağlarda yardımcı olabilir</string>
+ <string name="hostpref_wantsession_title">Shell oturumu başlat</string>
+ <string name="hostpref_wantsession_summary">Sadece port yönlendirmeyi kullanmak için bu seçeneği boş bırakın</string>
<string name="hostpref_stayconnected_title">Bağlı kal</string>
<string name="hostpref_stayconnected_summary">Bağlantı koparsa tekrar bağlanmayı dene</string>
+ <string name="hostpref_delkey_title">DEL tuşu</string>
+ <string name="hostpref_delkey_summary">DEL yani sil tuşuna basıldığında gönderilecek key kodu</string>
<string name="hostpref_encoding_title">Dil Kodlaması</string>
<string name="hostpref_encoding_summary">Sunucu karakter kodlaması</string>
<string name="hostpref_connection_category">Bağlantı ayarları</string>
<string name="hostpref_username_title">Kullanıcı Adı</string>
<string name="hostpref_hostname_title">Sunucu</string>
+ <string name="hostpref_port_title">Port</string>
<string name="bind_never">Daha önce bağlanılmadı</string>
- <string name="bind_minutes">%1$s dakika önce</string>
- <string name="bind_hours">%1$ saat önce</string>
- <string name="bind_days">%1$s gün önce</string>
<string name="console_copy_done">%1$d byte panoya kopyalandı</string>
<string name="console_copy_start">Kopyalanacak alanı seçmek için dokun ve sürükle ya da pad kullan</string>
<string name="console_menu_close">Kapat</string>
<string name="console_menu_copy">Kopyala</string>
<string name="console_menu_paste">Yapıştır</string>
<string name="console_menu_portforwards">Port Yönlendirmeleri</string>
+ <string name="console_menu_resize">Ebat zorunlu</string>
<string name="console_menu_urlscan">URL Tara</string>
<string name="button_yes">Evet</string>
<string name="button_no">Hayır</string>
@@ -133,11 +141,14 @@
<string name="portforward_dynamic">Dinamik (SOCKS)</string>
<string name="portforward_pos">Port yönlendirmesi oluştur</string>
<string name="portforward_done">Port yönlendirmesi başarıyla oluşturuldu</string>
+ <string name="portforward_problem">Port yönlendirmesi oluşturmada hata oldu, port 1024 altı yada meşgul bir port kullanmakdan kaynaklanabilir</string>
<string name="portforward_menu_add">Port yönlendirmesi ekle</string>
<string name="hint_userhost">kullanici\@sunucuadi</string>
<string name="list_format_error">%1$s formatında kullanın</string>
<string name="format_username">kullanıcı adı</string>
<string name="format_hostname">sunucu adı</string>
+ <string name="format_port">port</string>
+ <string name="list_menu_pubkeys">Umumi-anahtarlar Yönetimi</string>
<string name="list_menu_sortcolor">Rengine göre sırala</string>
<string name="list_menu_sortname">İsime göre sırala</string>
<string name="list_menu_settings">Ayarlar</string>
@@ -147,11 +158,17 @@
<string name="list_host_delete">Sunucuyu sil</string>
<string name="list_host_empty">Sunucuya bağlanmak için\naşağıdaki hızlı bağlan kutucuğunu kullan</string>
<string name="list_rotation_default">Varsayılan</string>
+ <string name="list_rotation_land">Yatay zorunlu</string>
+ <string name="list_rotation_port">Dikey zorunlu</string>
<string name="list_rotation_auto">Otomatik</string>
<string name="list_camera_ctrlaspace">Ctrl+A ardından Boşluk</string>
+ <string name="list_camera_ctrla">Ctrl+A</string>
+ <string name="list_camera_esc">Esc</string>
+ <string name="list_camera_esc_a">Esc+A</string>
<string name="list_camera_none">Hiçbiri</string>
<string name="list_delkey_backspace">Sil</string>
<string name="list_delkey_del">Sil</string>
+ <string name="delete_message">\'%1$s\' silinecek onaylıyor musunuz?</string>
<string name="delete_pos">Evet, sil</string>
<string name="delete_neg">İptal Et</string>
<string name="wizard_agree">Kabul et</string>
@@ -159,14 +176,27 @@
<string name="wizard_back">Geri dön</string>
<string name="terminal_no_hosts_connected">Hiçbir sunucuya bağlanılmadı</string>
<string name="terminal_connecting">%3$s ile %1$s:%2$d bağlanıyor</string>
+ <string name="terminal_sucess">Host doğrulaması \'%1$s\' anahtar: %2$s</string>
<string name="terminal_failed">Sunucu anahtarı doğrulaması başarısız oldu</string>
+ <string name="terminal_using_s2c_algorithm">Server-to-client s2c algoritması: %1$s %2$s</string>
+ <string name="terminal_using_c2s_algorithm">Client-to-server c2s algoritması: %1$s %2$s</string>
+ <string name="terminal_using_algorithm">Kullanılan algoritma: %1$s %2$s</string>
+ <string name="terminal_auth">Kimlik doğrulaması yapılıyor</string>
+ <string name="terminal_auth_pass">\'Şifre\' doğrulaması yapılıyor</string>
+ <string name="terminal_auth_pass_fail">Kimlik doğrulama \'şifre\' başarısız</string>
+ <string name="terminal_auth_pubkey_any">Bellekteki herhangi bir umumi-anahtar ile \'umumi-anahtar\' doğrulaması yapılıyor</string>
+ <string name="terminal_auth_pubkey_invalid">Seçilen umumi-anahtar geçersiz, host düzenleyicideki anahtarı tekrar seçerek deneyiniz</string>
+ <string name="terminal_auth_pubkey_specific">yerel umumi-anahtar specific public key ile \'publickey\' doğrulaması yapılıyor</string>
+ <string name="terminal_auth_pubkey_fail">Anahtar \'%1$s\' ile \'publickey\' doğrulama işlemi başarısız</string>
+ <string name="terminal_auth_ki">\'Klavyeden karşılıklı\' kimlik doğrulaması deneniyor</string>
+ <string name="terminal_auth_ki_fail">\'Klavyeden karşılıklı\' kimlik doğrulaması işlemi başarısız</string>
+ <string name="terminal_auth_fail">[Hostunuz \'şifre\' veya \'klavyeden karşılıklı\' kimlik doğrulamasını desteklemiyor.]</string>
<string name="terminal_no_session">Sunucu tercihleri sebebiyle oturum başlatılamıyor</string>
<string name="terminal_enable_portfoward">Port yönlendirmesini etkinleştir: %1$s</string>
<string name="local_shell_unavailable">Başarısız! Yerel shell bu telefonda kullanılamıyor.</string>
- <string name="upgrade">Yeni sürüm</string>
- <string name="upgrade_pos">Evet, yükselt</string>
- <string name="upgrade_neg">Şimdi değil</string>
+ <string name="notification_text">%1$s sizi uyarıyor</string>
<string name="no">Hayır</string>
+ <string name="with_confirmation">teyitli</string>
<string name="yes">Evet</string>
<string name="exceptions_submit_message">ConnectBot son çalıştırıldığında sorunla karşılaştı. Hata raporunu ConnectBot geliştiricilerine iletmek ister misiniz?</string>
<string name="menu_colors_reset">Sıfırla</string>
@@ -175,4 +205,8 @@
<string name="color_green">yeşil</string>
<string name="color_blue">mavi</string>
<string name="color_gray">gri</string>
+ <string name="colors_fg_label">ÖR: %1$d</string>
+ <string name="color_bg_label">AR: %1$d</string>
+ <string name="image_description_connected">Bağlandı.</string>
+ <string name="image_description_show_keyboard">Klavye göster.</string>
</resources>
diff --git a/res/values-v11/styles.xml b/res/values-v11/styles.xml
new file mode 100644
index 0000000..9a0c22b
--- /dev/null
+++ b/res/values-v11/styles.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * ConnectBot: simple, powerful, open-source SSH client for Android
+ * Copyright 2007 Kenny Root, Jeffrey Sharkey
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources>
+ <style name="NoTitle" parent="android:Theme.Holo">
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:actionBarStyle">@style/SolidActionBar</item>
+ </style>
+
+ <style name="SolidActionBar" parent="android:Widget.Holo.ActionBar">
+ <item name="android:background">#222222</item>
+ </style>
+</resources>
diff --git a/res/values-v14/styles.xml b/res/values-v14/styles.xml
new file mode 100644
index 0000000..3969010
--- /dev/null
+++ b/res/values-v14/styles.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * ConnectBot: simple, powerful, open-source SSH client for Android
+ * Copyright 2007 Kenny Root, Jeffrey Sharkey
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources>
+ <style name="NoTitle" parent="android:Theme.DeviceDefault">
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:actionBarStyle">@style/SolidActionBar</item>
+ </style>
+
+ <style name="SolidActionBar" parent="android:Widget.Holo.ActionBar">
+ <item name="android:background">#222222</item>
+ </style>
+</resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 46d1754..4420982 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -1,14 +1,16 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
- <string name="app_desc">简单,强大,开源的ssh客户端</string>
+ <string name="app_desc">简洁、强大、开源的SSH客户端</string>
+ <string name="service_desc">维护SSH连接及已装载的公钥</string>
<string name="title_hosts_list">主机</string>
<string name="title_pubkey_list">公钥</string>
- <string name="title_port_forwards_list">端口重定向</string>
+ <string name="title_port_forwards_list">端口转发</string>
<string name="title_host_editor">编辑主机</string>
<string name="title_help">帮助</string>
<string name="title_colors">颜色</string>
+ <string name="title_color_picker">选择一种颜色</string>
<string name="resolve_connect">连接</string>
- <string name="resolve_entropy">收集用户熵</string>
+ <string name="resolve_entropy">收集杂乱数</string>
<string name="menu_insert">添加主机</string>
<string name="menu_delete">删除主机</string>
<string name="menu_preferences">设置</string>
@@ -20,52 +22,52 @@
<string name="pubkey_delete">删除密钥</string>
<string name="pubkey_gather_entropy">正在收集用户熵</string>
<string name="pubkey_touch_prompt">触摸方框来收集随机数:%1$d%完成</string>
- <string name="pubkey_touch_hint">由于密钥产生的时候需要随机数据,请在下面的方框中随便移动你的手指</string>
+ <string name="pubkey_touch_hint">请在方框内随机移动手指来生成随机密钥</string>
<string name="pubkey_generating">正在产生密钥配对</string>
<string name="pubkey_copy_private">复制私钥</string>
<string name="pubkey_copy_public">复制公钥</string>
- <string name="pubkey_list_empty">点击菜单来创建\\不是导入密钥对</string>
+ <string name="pubkey_list_empty">点击\"菜单\"来创建或者导入密钥对</string>
<string name="pubkey_unknown_format">未知格式</string>
- <string name="pubkey_change_password">改变密码</string>
- <string name="pubkey_list_pick">从/sdcard获取</string>
+ <string name="pubkey_change_password">更改密码</string>
+ <string name="pubkey_list_pick">从/sdcard导入</string>
<string name="pubkey_import_parse_problem">导入私钥的时候出现问题</string>
- <string name="pubkey_unlock">未锁定key</string>
- <string name="pubkey_failed_add">验证失败,错误的的密钥密码「%1$s」</string>
- <string name="pubkey_memory_load">载入内存中</string>
+ <string name="pubkey_unlock">解锁密钥</string>
+ <string name="pubkey_failed_add">验证失败,\'%1$s\'密钥密码错误</string>
+ <string name="pubkey_memory_load">载入到内存</string>
<string name="pubkey_memory_unload">从内存中移除</string>
- <string name="pubkey_load_on_start">在启动的时候载入密钥</string>
+ <string name="pubkey_load_on_start">启动时自动载入公钥</string>
<string name="pubkey_confirm_use">使用前确认</string>
<string name="portforward_list_empty">点击「菜单」来创建端口转发</string>
- <string name="portforward_edit">编辑端口转发</string>
+ <string name="portforward_edit">编辑转发端口</string>
<string name="portforward_delete">删除端口转发</string>
<string name="prompt_nickname">昵称:</string>
<string name="prompt_nickname_hint_pubkey">我的工作密钥</string>
<string name="prompt_source_port">源端口:</string>
<string name="prompt_destination">目标端口:</string>
<string name="prompt_old_password">旧密码:</string>
- <string name="prompt_password">密码:</string>
- <string name="prompt_again">再输入一遍密码</string>
+ <string name="prompt_password">新密码:</string>
+ <string name="prompt_again">确认新密码</string>
<string name="prompt_type">转发类型</string>
- <string name="prompt_password_can_be_blank">密码不能为空</string>
+ <string name="prompt_password_can_be_blank">注意:密码可以为空</string>
<string name="prompt_bits">位数:</string>
<string name="prompt_pubkey_password">密钥「%1$s」的密码</string>
- <string name="prompt_allow_agent_to_use_key">允许远程主机到\\ 不使用按键 %1$s?</string>
- <string name="host_verification_failure_warning_header">警告:远程主机的标志已经改变</string>
+ <string name="prompt_allow_agent_to_use_key">允许远程主机使用\'%1$s\'密钥?</string>
+ <string name="host_verification_failure_warning_header">警告:远程主机身份标识已经改变</string>
<string name="host_verification_failure_warning">可能某人正在做非善意的事情\n可能有人正在偷听你的会话(中间人攻击)\n但也许仅仅是主密匙(host key)已被更改,你确信如此?</string>
<string name="prompt_host_disconnected">主机已经断开。\n是否关闭本次对话?</string>
<string name="prompt_continue_connecting">是否要继续连接?</string>
<string name="host_authenticity_warning">主机「%1$s」的验证无法建立</string>
<string name="host_fingerprint">主机 %1$s 密钥指纹是 %2$s</string>
<string name="alert_passwords_do_not_match_msg">密码不匹配!</string>
- <string name="alert_wrong_password_msg">错误的密码!</string>
- <string name="alert_key_corrupted_msg">私钥好像已经损坏!</string>
+ <string name="alert_wrong_password_msg">密码错误</string>
+ <string name="alert_key_corrupted_msg">私钥似乎已破坏</string>
<string name="alert_sdcard_absent">没有插入SD卡!</string>
<string name="button_add">添加</string>
<string name="button_change">修改</string>
<string name="button_generate">生成密钥</string>
- <string name="button_resize">缩放</string>
+ <string name="button_resize">改变窗口大小</string>
<string name="alert_disconnect_msg">连接中断</string>
- <string name="msg_copyright">版权所有2007-2008 Kenny Root http://the-b.org/, Jeffrey Sharkey http://jsharkey.org/</string>
+ <string name="msg_copyright">版权所有 ©2007-2008 Kenny Root http://the-b.org/, Jeffrey Sharkey http://jsharkey.org/</string>
<string name="pref_emulation_category">模拟终端</string>
<string name="pref_emulation_title">仿真模式</string>
<string name="pref_emulation_summary">设置使用PTY连接时的终端模式</string>
@@ -73,79 +75,79 @@
<string name="pref_scrollback_summary">在内存中为每个控制台保留的回滚缓存长度</string>
<string name="pref_ui_category">用户界面</string>
<string name="pref_rotation_title">循环模式</string>
- <string name="pref_rotation_summary">当滑开或关闭键盘时如何调整屏幕朝向(水平或竖直)</string>
+ <string name="pref_rotation_summary">当键盘打开或关闭时屏幕如何旋转(水平或竖直)</string>
<string name="pref_fullscreen_title">全屏</string>
<string name="pref_fullscreen_summary">处于控制台时隐藏状态栏</string>
+ <string name="pref_keyboard_category">键盘</string>
+ <string name="pref_shiftfkeys_title">Shift+num(数字键) 作为 功能键</string>
+ <string name="pref_shiftfkeys_summary">在硬件键盘上,数字键和 shift 键组合发送 F1-F10</string>
+ <string name="pref_ctrlfkeys_title">Ctrl+num(数字键) 作为 功能键</string>
+ <string name="pref_ctrlfkeys_summary">在软件键盘上,数字键和 ctrl 键组合发送 F1-F10</string>
+ <string name="pref_volumefont_title">音量键用来改变字体大小</string>
+ <string name="pref_volumefont_summary">字体大小也可以在每个主机的设置中改变</string>
<string name="pref_memkeys_title">在内存中记录密钥</string>
- <string name="pref_memkeys_summary">保存解锁密匙在内存中,直到后端服务停止为止</string>
- <string name="pref_update_title">升级检查</string>
- <string name="pref_update_summary">设置检查ConnectBot最新版本的最大频率</string>
+ <string name="pref_memkeys_summary">保留解锁的密匙于内存中直到后台服务停止</string>
<string name="pref_conn_persist_title">保持联机</string>
- <string name="pref_conn_persist_summary">强制并保持连接在后台</string>
- <string name="pref_keymode_title">目录快捷键</string>
+ <string name="pref_conn_persist_summary">切换到后台时强制保持连接</string>
+ <string name="pref_keymode_title">目录快捷方式</string>
<string name="pref_keymode_summary">选择如何使用 Alt 代表 /,Shift 代表 Tab</string>
<string name="pref_camera_title">摄像快捷键</string>
- <string name="pref_camera_summary">选择当照相键按下时将要触发的快捷键</string>
+ <string name="pref_camera_summary">选择照相键按下时触发的快捷功能</string>
<string name="pref_keepalive_title">保持屏幕唤醒</string>
- <string name="pref_keepalive_summary">禁止屏幕睡眠当终端正在工作时</string>
+ <string name="pref_keepalive_summary">终端运行时禁止屏幕休眠</string>
<string name="pref_wifilock_title">保持WI-FI激活</string>
- <string name="pref_wifilock_summary">禁止WiFi自动关闭当会话激活时</string>
- <string name="pref_bumpyarrows_summary">振动当用轨迹球发出模拟方向键时; (对滞后连接特别有用)</string>
- <string name="pref_bell_category">终端铃声</string>
+ <string name="pref_wifilock_summary">会话运行时禁止Wi-Fi关闭</string>
+ <string name="pref_bumpyarrows_title">触摸反馈</string>
+ <string name="pref_bumpyarrows_summary">轨迹球发出模拟方向键时振动; (对滞后连接特别有用)</string>
+ <string name="pref_bell_category">终端响铃</string>
<string name="pref_bell_title">响铃</string>
<string name="pref_bell_volume_title">铃声音量</string>
- <string name="pref_bell_vibrate_title">振动同时响铃</string>
+ <string name="pref_bell_vibrate_title">响铃时振动</string>
<string name="pref_bell_notification_title">背景通知</string>
- <string name="pref_bell_notification_summary">当后台终端响铃时发出通知</string>
+ <string name="pref_bell_notification_summary">当终端在后台响铃时发出通知</string>
<string name="list_keymode_right">使用右边键盘</string>
<string name="list_keymode_left">使用左边键盘</string>
<string name="list_keymode_none">禁用</string>
<string name="list_pubkeyids_none">不使用密匙</string>
<string name="list_pubkeyids_any">使用解锁密匙</string>
- <string name="list_update_daily">每日</string>
- <string name="list_update_weekly">每周</string>
- <string name="list_update_never">从不</string>
<string name="hostpref_nickname_title">昵称</string>
<string name="hostpref_color_title">颜色类型</string>
<string name="hostpref_fontsize_title">字型大小 (pt)</string>
<string name="hostpref_pubkeyid_title">使用公钥验证</string>
<string name="hostpref_authagent_title">使用 SSH 认证</string>
- <string name="hostpref_postlogin_title">登入后动作</string>
- <string name="hostpref_postlogin_summary">每次验证后在远端服务器上运行的命令</string>
+ <string name="hostpref_postlogin_title">登录后自动运行</string>
+ <string name="hostpref_postlogin_summary">登录后在远程服务器上自动运行命令</string>
<string name="hostpref_compression_title">压缩</string>
<string name="hostpref_compression_summary">此选项对低速网络会有帮助</string>
<string name="hostpref_wantsession_title">开始Shell会话</string>
<string name="hostpref_wantsession_summary">仅仅对端口转发禁用偏好设置</string>
<string name="hostpref_stayconnected_title">保持连接</string>
- <string name="hostpref_stayconnected_summary">断线后自动重新连接到主机</string>
+ <string name="hostpref_stayconnected_summary">断线重连</string>
<string name="hostpref_delkey_title">DEL键</string>
- <string name="hostpref_delkey_summary">当DEL按下时触发的按键</string>
+ <string name="hostpref_delkey_summary">按下DEL键时发送给服务器的键值</string>
<string name="hostpref_encoding_title">编码</string>
<string name="hostpref_encoding_summary">主机的字符编码</string>
<string name="hostpref_connection_category">连接设置</string>
<string name="hostpref_username_title">用户名</string>
<string name="hostpref_hostname_title">主机</string>
<string name="hostpref_port_title">端口</string>
- <string name="bind_never">从未连接</string>
- <string name="bind_minutes">已连接 %1$s 分钟</string>
- <string name="bind_hours">已连接 %1$s 小时</string>
- <string name="bind_days">已连接 %1$s 天</string>
- <string name="console_copy_done">复制 %1$d 字节到剪贴板</string>
- <string name="console_copy_start">滑动手指\n或使用轨迹球\n选择要复制的区域</string>
+ <string name="bind_never">尚未连接</string>
+ <string name="console_copy_done">已复制%1$d位</string>
+ <string name="console_copy_start">点击拖动\n或使用触摸板\n来选择要复制的区域</string>
<string name="console_menu_close">关闭</string>
<string name="console_menu_copy">复制</string>
<string name="console_menu_paste">粘贴</string>
<string name="console_menu_portforwards">端口转发</string>
- <string name="console_menu_resize">字体大小</string>
+ <string name="console_menu_resize">指定大小</string>
<string name="console_menu_urlscan">URL 地址扫描</string>
- <string name="button_yes">是</string>
- <string name="button_no">不</string>
+ <string name="button_yes">确认</string>
+ <string name="button_no">取消</string>
<string name="portforward_local">本地</string>
<string name="portforward_remote">远端</string>
<string name="portforward_dynamic">动态套接字(SOCKS)</string>
<string name="portforward_pos">创建端口转发</string>
- <string name="portforward_done">成功创建端口转发</string>
- <string name="portforward_problem">创建端口转发发生故障,可能你选择的端口小于1024或该端口已经被使用?</string>
+ <string name="portforward_done">创建端口转发成功</string>
+ <string name="portforward_problem">创建端口转发失败,检查你选择的端口是否小于1024或该端口已被占用</string>
<string name="portforward_menu_add">添加端口转发</string>
<string name="hint_userhost">用户名\@主机名</string>
<string name="list_format_error">使用%1$s格式</string>
@@ -153,8 +155,8 @@
<string name="format_hostname">主机名</string>
<string name="format_port">端口</string>
<string name="list_menu_pubkeys">管理公钥</string>
- <string name="list_menu_sortcolor">使用颜色排序</string>
- <string name="list_menu_sortname">按名字排序</string>
+ <string name="list_menu_sortcolor">按颜色排序</string>
+ <string name="list_menu_sortname">按名称排序</string>
<string name="list_menu_settings">设置</string>
<string name="list_host_disconnect">断开连接</string>
<string name="list_host_edit">编辑主机</string>
@@ -166,7 +168,11 @@
<string name="list_rotation_port">强制竖屏显示</string>
<string name="list_rotation_auto">自动</string>
<string name="list_camera_ctrlaspace">Ctrl+A 然后 Space</string>
+ <string name="list_camera_ctrla">Ctrl+A</string>
+ <string name="list_camera_esc">Esc 键</string>
+ <string name="list_camera_esc_a">Esc+A</string>
<string name="list_camera_none">无</string>
+ <string name="list_delkey_backspace">退格键</string>
<string name="list_delkey_del">删除</string>
<string name="delete_message">确定要删除\'%1$s\'吗?</string>
<string name="delete_pos">是,删除</string>
@@ -174,8 +180,8 @@
<string name="wizard_agree">同意</string>
<string name="wizard_next">下一页</string>
<string name="wizard_back">返回</string>
- <string name="terminal_no_hosts_connected">当前没有已连接主机</string>
- <string name="terminal_connecting">连接到 %1$s:%2$d,通过 %3$s</string>
+ <string name="terminal_no_hosts_connected">当前没有连接主机</string>
+ <string name="terminal_connecting">正在通过%3$s连接到 %1$s:%2$d</string>
<string name="terminal_sucess">验证主机 %1$s 键值: %2$s</string>
<string name="terminal_failed">主机验证失败</string>
<string name="terminal_using_s2c_algorithm">服务器到客户端算法:%1$s %2$s</string>
@@ -185,27 +191,31 @@
<string name="terminal_auth_pass">尝试进行用户口令认证</string>
<string name="terminal_auth_pass_fail">密码验证失败</string>
<string name="terminal_auth_pubkey_any">尝试使用保存在内存中的公匙进行用户认证</string>
- <string name="terminal_auth_pubkey_invalid">所选择的公钥无法使用,请在主机编辑中尝试重新选择</string>
- <string name="terminal_auth_pubkey_specific">尝试进行与某个公匙相关的公匙用户认证</string>
- <string name="terminal_auth_pubkey_fail">认证方法 publickey, 键值 %1$s 失败</string>
+ <string name="terminal_auth_pubkey_invalid">所选公钥失效,请在主机中重新选择</string>
+ <string name="terminal_auth_pubkey_specific">正在以公钥认证方式认证所指定的公钥</string>
+ <string name="terminal_auth_pubkey_fail">公钥%1$s认证失败</string>
<string name="terminal_auth_ki">尝试键盘交互式认证</string>
<string name="terminal_auth_ki_fail">键盘交互式认证失败</string>
- <string name="terminal_auth_fail">[你的主机不支持用户口令认证或键盘交互式认证]</string>
- <string name="terminal_no_session">因为主机的相关设置,不会启动会话。</string>
+ <string name="terminal_auth_fail">[该主机不支持用户口令认证或键盘交互式认证]</string>
+ <string name="terminal_no_session">由于主机设定不起动该会话</string>
<string name="terminal_enable_portfoward">启用端口转发:%1$s</string>
<string name="local_shell_unavailable">失败! 本地Shell在该手机上不可用</string>
<string name="notification_text">%1$s 需要你的注意</string>
- <string name="upgrade">新版本</string>
- <string name="upgrade_pos">是,升级</string>
- <string name="upgrade_neg">暂时不</string>
<string name="no">不</string>
- <string name="with_confirmation">已确认过</string>
- <string name="yes">是</string>
- <string name="exceptions_submit_message">似乎ConnectBot在最后运行时出现异常. 请回报给ConnectBot开发者</string>
- <string name="menu_colors_reset">重设</string>
+ <string name="with_confirmation">需要确认</string>
+ <string name="yes">确定</string>
+ <string name="exceptions_submit_message">ConnectBot上次运行时出现异常, 是否提交报告至ConnectBot开发者?</string>
+ <string name="menu_colors_reset">重置</string>
<string name="app_is_running">ConnectBot 正在运行</string>
- <string name="color_red">红</string>
- <string name="color_green">绿</string>
- <string name="color_blue">蓝</string>
- <string name="color_gray">灰</string>
+ <string name="color_red">红色</string>
+ <string name="color_green">绿色</string>
+ <string name="color_blue">蓝色</string>
+ <string name="color_gray">灰色</string>
+ <string name="colors_fg_label">前景色:%1$d</string>
+ <string name="color_bg_label">背景色:%1$d</string>
+ <string name="image_description_connected">已连接。</string>
+ <string name="image_description_key_is_locked">密钥已锁。</string>
+ <string name="image_description_toggle_control_character">切换控制字符。</string>
+ <string name="image_description_send_escape_character">发送转义字符。</string>
+ <string name="image_description_show_keyboard">显示键盘。</string>
</resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 4f41a5d..59d0e6e 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -1,196 +1,224 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
- <string name="app_desc">簡單,強大,開源的SSH客戶端</string>
- <string name="title_hosts_list">主機列表</string>
+ <string name="app_desc">簡單、強大、開放原始碼的SSH用戶端。</string>
+ <string name="service_desc">管理 SSH 連線及載入公鑰</string>
+ <string name="title_hosts_list">主機清單</string>
<string name="title_pubkey_list">公鑰</string>
- <string name="title_port_forwards_list">端口映射</string>
+ <string name="title_port_forwards_list">連接埠轉址</string>
<string name="title_host_editor">編輯主機資訊</string>
- <string name="title_help">輔助說明</string>
+ <string name="title_help">說明</string>
<string name="title_colors">顏色</string>
+ <string name="title_color_picker">挑選顏色</string>
<string name="resolve_connect">連線</string>
<string name="resolve_entropy">收集用戶熵</string>
<string name="menu_insert">新增主機</string>
<string name="menu_delete">刪除主機</string>
<string name="menu_preferences">設定</string>
- <string name="help_intro">請在下面主題中選擇一個以查看更多內容。</string>
- <string name="help_about">關於ConnectBot</string>
+ <string name="help_intro">請在下列選擇一個主題以查看更多內容。</string>
+ <string name="help_about">關於 ConnectBot</string>
<string name="help_keyboard">鍵盤</string>
<string name="pubkey_generate">產生</string>
<string name="pubkey_import">匯入</string>
<string name="pubkey_delete">刪除金鑰</string>
<string name="pubkey_gather_entropy">正在收集用戶熵</string>
- <string name="pubkey_touch_prompt">觸摸方框來收集隨機數:%1$d%完成</string>
- <string name="pubkey_touch_hint">由於密鑰產生的時候需要隨機數據,請在下面的方框中隨便移動你的手指</string>
- <string name="pubkey_generating">正在產生密鑰配對</string>
+ <string name="pubkey_touch_prompt">觸摸此方框來收集隨機數:%1$d%完成</string>
+ <string name="pubkey_touch_hint">由於金鑰產生的時候需要隨機資訊,請在下面的方框中隨意移動您的手指。</string>
+ <string name="pubkey_generating">正在產生金鑰對...</string>
<string name="pubkey_copy_private">複製私鑰</string>
<string name="pubkey_copy_public">複製公鑰</string>
- <string name="pubkey_list_empty">按「菜單」鍵創建或導入密鑰。</string>
+ <string name="pubkey_list_empty">按「選單」鍵來產生\n或匯入金鑰對。</string>
<string name="pubkey_unknown_format">未知格式</string>
- <string name="pubkey_change_password">更改密碼</string>
- <string name="pubkey_list_pick">從/sdcard獲取</string>
- <string name="pubkey_import_parse_problem">導入私鑰的時候出現問題</string>
- <string name="pubkey_unlock">未鎖定私鑰</string>
- <string name="pubkey_failed_add">驗證失敗,錯誤的的密鑰密碼「%1$s」</string>
- <string name="pubkey_memory_load">載入內存中</string>
- <string name="pubkey_memory_unload">從內存中移除</string>
- <string name="pubkey_load_on_start">在啟動的時候載入密鑰</string>
+ <string name="pubkey_change_password">變更密碼</string>
+ <string name="pubkey_list_pick">從 /sdcard 選擇</string>
+ <string name="pubkey_import_parse_problem">匯入私鑰時發生問題</string>
+ <string name="pubkey_unlock">解鎖金鑰</string>
+ <string name="pubkey_failed_add">驗證失敗,錯誤的的金鑰密碼「%1$s」</string>
+ <string name="pubkey_memory_load">載入到記憶體中</string>
+ <string name="pubkey_memory_unload">從記憶體中卸載</string>
+ <string name="pubkey_load_on_start">啟動時載入金鑰</string>
<string name="pubkey_confirm_use">使用前確認</string>
- <string name="portforward_list_empty">按「菜單」鍵建端口轉發</string>
- <string name="portforward_edit">編輯端口轉發</string>
- <string name="portforward_delete">刪除端口轉發</string>
+ <string name="portforward_list_empty">按「選單」鍵來產生\n連接埠轉址。</string>
+ <string name="portforward_edit">編輯連接埠轉址</string>
+ <string name="portforward_delete">刪除連接埠轉址</string>
<string name="prompt_nickname">暱稱:</string>
- <string name="prompt_nickname_hint_pubkey">我的工作密鑰</string>
- <string name="prompt_source_port">源端口:</string>
- <string name="prompt_destination">目標端口:</string>
- <string name="prompt_old_password">舊密碼</string>
+ <string name="prompt_nickname_hint_pubkey">我的工作金鑰</string>
+ <string name="prompt_source_port">來源連接埠:</string>
+ <string name="prompt_destination">目的連接埠:</string>
+ <string name="prompt_old_password">舊密碼:</string>
<string name="prompt_password">密碼:</string>
<string name="prompt_again">(再輸入一遍密碼)</string>
- <string name="prompt_type">轉發類型:</string>
- <string name="prompt_password_can_be_blank">密碼可以為空</string>
- <string name="prompt_bits">位數:</string>
- <string name="prompt_pubkey_password">密鑰「%1$s」的密碼</string>
- <string name="host_verification_failure_warning_header">警告:遠程主機的標誌已經改變</string>
- <string name="host_verification_failure_warning">可能某人正在做非善意的事情\n可能有人正在偷聽你的會話(中間人攻擊)\n但也許僅僅是主密匙(host key)已被更改,你確信如此?</string>
- <string name="prompt_host_disconnected">主機已經斷開。\n是否關閉本次對話?</string>
- <string name="prompt_continue_connecting">是否要繼續連接?</string>
+ <string name="prompt_type">轉址類型:</string>
+ <string name="prompt_password_can_be_blank">注意:密碼可以為空白</string>
+ <string name="prompt_bits">位元:</string>
+ <string name="prompt_pubkey_password">公鑰「%1$s」的密碼</string>
+ <string name="prompt_allow_agent_to_use_key">允許遠端主機\n使用金鑰 \'%1$s\'?</string>
+ <string name="host_verification_failure_warning_header">警告:遠端主機的識別已變更!</string>
+ <string name="host_verification_failure_warning">可能某人正在做非善意的事情!\n可能有人正在側錄您的連線(中間攻擊法)!\n但也許僅僅是主機金鑰已變更。</string>
+ <string name="prompt_host_disconnected">主機已經中斷。\n是否關閉本次連線階段?</string>
+ <string name="prompt_continue_connecting">是否要繼續連線?</string>
<string name="host_authenticity_warning">主機「%1$s」的驗證無法建立</string>
- <string name="host_fingerprint">主機 %1$s 密鑰指紋是 %2$s</string>
- <string name="alert_passwords_do_not_match_msg">密碼不匹配!</string>
+ <string name="host_fingerprint">主機 %1$s 金鑰指紋是 %2$s</string>
+ <string name="alert_passwords_do_not_match_msg">密碼不符!</string>
<string name="alert_wrong_password_msg">錯誤的密碼!</string>
- <string name="alert_key_corrupted_msg">私鑰好像已經損壞!</string>
- <string name="alert_sdcard_absent">沒有插入SD卡!</string>
- <string name="button_add">添加</string>
- <string name="button_change">修改</string>
- <string name="button_generate">生成密鑰</string>
+ <string name="alert_key_corrupted_msg">私鑰已經損壞!</string>
+ <string name="alert_sdcard_absent">沒有插入 SD 卡!</string>
+ <string name="button_add">新增</string>
+ <string name="button_change">變更</string>
+ <string name="button_generate">產生金鑰</string>
<string name="button_resize">調整大小</string>
- <string name="alert_disconnect_msg">連接中斷</string>
- <string name="msg_copyright">版權所有2007-2008 Kenny Root http://the-b.org/ , Jeffrey Sharkey http://jsharkey.org/</string>
- <string name="pref_emulation_category">模擬終端</string>
+ <string name="alert_disconnect_msg">連線中斷</string>
+ <string name="msg_copyright">版權所有 © 2007-2008 Kenny Root http://the-b.org/ , Jeffrey Sharkey http://jsharkey.org/</string>
+ <string name="pref_emulation_category">終端模擬器</string>
<string name="pref_emulation_title">終端機類型</string>
- <string name="pref_emulation_summary">設置使用PTY連接時的終端模式</string>
- <string name="pref_scrollback_title">回溯存儲大小</string>
- <string name="pref_scrollback_summary">在內存中為每個控制台保留的回滾緩存長度</string>
- <string name="pref_ui_category">用戶界面</string>
- <string name="pref_rotation_title">行為的輪換</string>
- <string name="pref_fullscreen_title">全屏</string>
- <string name="pref_fullscreen_summary">處於控制台時隱藏狀態欄</string>
- <string name="pref_memkeys_title">在內存中記錄密鑰</string>
- <string name="pref_memkeys_summary">保存解鎖密匙在內存中,直到後端服務停止為止</string>
- <string name="pref_update_title">頻率檢查更新</string>
- <string name="pref_update_summary">最大頻率檢查ConnectBot更新</string>
- <string name="pref_conn_persist_title">保持聯機</string>
- <string name="pref_conn_persist_summary">強制並保持連接在後台</string>
- <string name="pref_keymode_title">目錄快捷鍵</string>
- <string name="pref_camera_title">攝像快捷鍵</string>
- <string name="pref_camera_summary">選擇當照相鍵按下時將要觸發的快捷鍵</string>
- <string name="pref_keepalive_title">保持清醒屏幕</string>
- <string name="pref_keepalive_summary">禁止屏幕睡眠當終端正在工作時</string>
- <string name="pref_wifilock_title">保持Wi-Fi激活</string>
- <string name="pref_wifilock_summary">禁止WiFi自動關閉當會話激活時</string>
+ <string name="pref_emulation_summary">設置使用 PTY 連接時的終端模式</string>
+ <string name="pref_scrollback_title">回溯記憶體大小</string>
+ <string name="pref_scrollback_summary">在記憶體中為每個控制台保留的回溯緩衝大小</string>
+ <string name="pref_ui_category">使用者介面</string>
+ <string name="pref_rotation_title">螢幕旋轉模式</string>
+ <string name="pref_rotation_summary">當鍵盤視窗出現或隱藏時要如何改變螢幕轉向</string>
+ <string name="pref_titlebarhide_title">自動隱藏標題列</string>
+ <string name="pref_titlebarhide_summary">輕觸終端機來顯示標題列並存取選單</string>
+ <string name="pref_fullscreen_title">全螢幕</string>
+ <string name="pref_fullscreen_summary">當在控制台時隱藏狀態列</string>
+ <string name="pref_keyboard_category">鍵盤</string>
+ <string name="pref_shiftfkeys_title">Shift+num 為功能鍵</string>
+ <string name="pref_shiftfkeys_summary">在硬體鍵盤上,shift 加數字鍵時傳送 F1-F10</string>
+ <string name="pref_ctrlfkeys_title">Ctrl+num 為功能鍵</string>
+ <string name="pref_ctrlfkeys_summary">在軟體鍵盤上,ctrl 加數字時傳送 F1-F10</string>
+ <string name="pref_volumefont_title">音量鍵更改字體大小</string>
+ <string name="pref_volumefont_summary">字體大小可以在各主機設定內更改</string>
+ <string name="pref_memkeys_title">將金鑰儲存在記憶體</string>
+ <string name="pref_memkeys_summary">保持記憶體中的金鑰是解鎖狀態,直到背景服務結束</string>
+ <string name="pref_conn_persist_title">保持連線</string>
+ <string name="pref_conn_persist_summary">在背景執行時仍保持連線狀態</string>
+ <string name="pref_keymode_title">快捷鍵</string>
+ <string name="pref_keymode_summary">選擇如何使用 Alt 來叫出 \'/\' 和用 Shift 呼叫 Tab</string>
+ <string name="only_alt">只有Alt</string>
+ <string name="pref_camera_title">照相快捷鍵</string>
+ <string name="pref_camera_summary">選擇當照相按鈕按下時將要觸發的快捷鍵</string>
+ <string name="pref_keepalive_title">保持螢幕喚醒</string>
+ <string name="pref_keepalive_summary">執行控制台時不關閉螢幕</string>
+ <string name="pref_wifilock_title">保持 Wi-Fi 連線</string>
+ <string name="pref_wifilock_summary">連線階段未結束前,避免 Wi-Fi 關閉</string>
<string name="pref_bumpyarrows_title">觸覺方向鍵</string>
- <string name="pref_bumpyarrows_summary">振動當用軌跡球發出模擬方曏鍵時; 對滯後連接特別有用</string>
+ <string name="pref_bumpyarrows_summary">當用軌跡球發出模擬方向鍵時振動; 對延遲連線特別有用</string>
<string name="pref_bell_category">終端鈴聲</string>
<string name="pref_bell_title">響鈴</string>
<string name="pref_bell_volume_title">鈴聲音量</string>
<string name="pref_bell_vibrate_title">振動同時響鈴</string>
<string name="pref_bell_notification_title">背景通知</string>
- <string name="pref_bell_notification_summary">當後台終端響鈴時發出通知</string>
+ <string name="pref_bell_notification_summary">當背景終端響鈴時發出通知。</string>
<string name="list_keymode_right">使用右側按鍵</string>
<string name="list_keymode_left">使用左側按鍵</string>
- <string name="list_keymode_none">禁用</string>
- <string name="list_pubkeyids_none">不使用密匙</string>
- <string name="list_pubkeyids_any">使用解鎖密匙</string>
- <string name="list_update_daily">每天</string>
- <string name="list_update_weekly">每週</string>
- <string name="list_update_never">永不</string>
- <string name="hostpref_nickname_title">昵稱</string>
+ <string name="list_keymode_none">停用</string>
+ <string name="list_pubkeyids_none">不使用公鑰</string>
+ <string name="list_pubkeyids_any">使用任何解鎖金鑰</string>
+ <string name="hostpref_nickname_title">暱稱</string>
<string name="hostpref_color_title">顏色類型</string>
- <string name="hostpref_fontsize_title">字型大小 (pt)</string>
+ <string name="hostpref_fontsize_title">字體大小 (pt)</string>
<string name="hostpref_pubkeyid_title">使用公鑰驗證</string>
<string name="hostpref_authagent_title">使用 SSH 認證</string>
- <string name="hostpref_postlogin_summary">每次驗證後在遠端服務器上運行的命令</string>
+ <string name="hostpref_postlogin_title">登入後自動執行</string>
+ <string name="hostpref_postlogin_summary">每次驗證後在遠端伺服器上執行的命令</string>
<string name="hostpref_compression_title">壓縮</string>
- <string name="hostpref_compression_summary">此選項對低速網絡會有幫助</string>
- <string name="hostpref_wantsession_title">開始Shell會話</string>
- <string name="hostpref_wantsession_summary">僅僅對端口轉發禁用偏好設置</string>
- <string name="hostpref_stayconnected_title">保持連接</string>
- <string name="hostpref_stayconnected_summary">斷線後自動重新連接到主機</string>
- <string name="hostpref_delkey_title">DEL鍵</string>
- <string name="hostpref_delkey_summary">當DEL按下時觸發的按鍵</string>
+ <string name="hostpref_compression_summary">此選項對低速網路會有幫助</string>
+ <string name="hostpref_wantsession_title">開始 Shell 連線階段</string>
+ <string name="hostpref_wantsession_summary">停用本偏好設定來僅使用連接埠轉址</string>
+ <string name="hostpref_stayconnected_title">保持連線</string>
+ <string name="hostpref_stayconnected_summary">斷線後與主機重新連線</string>
+ <string name="hostpref_delkey_title">DEL 鍵</string>
+ <string name="hostpref_delkey_summary">當按下 DEL 鍵時,傳送金鑰代碼</string>
<string name="hostpref_encoding_title">編碼</string>
- <string name="hostpref_encoding_summary">主機的字符編碼</string>
- <string name="hostpref_connection_category">連接設置</string>
- <string name="hostpref_username_title">用戶名</string>
+ <string name="hostpref_encoding_summary">主機的字元編碼</string>
+ <string name="hostpref_connection_category">連線設定</string>
+ <string name="hostpref_username_title">使用者名稱</string>
<string name="hostpref_hostname_title">主機</string>
- <string name="hostpref_port_title">端口</string>
- <string name="bind_never">從未連接</string>
- <string name="bind_minutes">已連接 %1$s 分鐘</string>
- <string name="bind_hours">已連接 %1$s 小時</string>
- <string name="bind_days">已連接 %1$s 天</string>
- <string name="console_copy_done">復制 %1$d 字節到剪貼板</string>
- <string name="console_copy_start">滑動手指\n或使用軌跡球\n選擇要復制的區域</string>
+ <string name="hostpref_port_title">連接埠</string>
+ <string name="bind_never">從未連線</string>
+ <string name="console_copy_done">複製 %1$d 位元組到剪貼簿</string>
+ <string name="console_copy_start">滑動手指\n或使用軌跡球\n選擇要複製的區域</string>
<string name="console_menu_close">關閉</string>
- <string name="console_menu_copy">復制</string>
- <string name="console_menu_paste">粘貼</string>
- <string name="console_menu_portforwards">端口轉發</string>
+ <string name="console_menu_copy">複製</string>
+ <string name="console_menu_paste">貼上</string>
+ <string name="console_menu_portforwards">連接埠轉址</string>
<string name="console_menu_resize">字體大小</string>
- <string name="console_menu_urlscan">URL 地址掃描</string>
+ <string name="console_menu_urlscan">URL 掃描</string>
<string name="button_yes">是</string>
- <string name="button_no">不</string>
- <string name="portforward_local">本地</string>
+ <string name="button_no">否</string>
+ <string name="portforward_local">本機</string>
<string name="portforward_remote">遠端</string>
- <string name="portforward_dynamic">動態套接字(SOCKS)</string>
- <string name="portforward_pos">創建端口轉發</string>
- <string name="portforward_done">成功創建端口轉發</string>
- <string name="portforward_menu_add">添加端口轉發</string>
- <string name="hint_userhost">用戶名\@主機名</string>
- <string name="list_format_error">使用%1$s格式</string>
- <string name="format_username">用戶名</string>
- <string name="format_hostname">主機名</string>
- <string name="format_port">端口</string>
+ <string name="portforward_dynamic">動態 (SOCKS)</string>
+ <string name="portforward_pos">建立連接埠轉址</string>
+ <string name="portforward_done">成功建立連接埠轉址</string>
+ <string name="portforward_problem">新增連接埠時發生問題,可能您正在使用 1024 以下的埠或是埠被佔用?</string>
+ <string name="portforward_menu_add">新增連接埠轉址</string>
+ <string name="hint_userhost">使用者名稱\@主機名稱</string>
+ <string name="list_format_error">使用 %1$s 格式</string>
+ <string name="format_username">使用者名稱</string>
+ <string name="format_hostname">主機名稱</string>
+ <string name="format_port">連接埠</string>
<string name="list_menu_pubkeys">管理公鑰</string>
- <string name="list_menu_sortcolor">使用顏色排序</string>
+ <string name="list_menu_sortcolor">按顏色排序</string>
<string name="list_menu_sortname">按名字排序</string>
- <string name="list_menu_settings">設置</string>
- <string name="list_host_disconnect">斷開連接</string>
+ <string name="list_menu_settings">設定</string>
+ <string name="list_host_disconnect">中斷連線</string>
<string name="list_host_edit">編輯主機</string>
- <string name="list_host_portforwards">編輯端口轉發</string>
+ <string name="list_host_portforwards">編輯連接埠轉址</string>
<string name="list_host_delete">刪除主機</string>
<string name="list_host_empty">使用下面的快速連接框連接主機</string>
- <string name="list_rotation_default">默認</string>
+ <string name="list_rotation_default">預設值</string>
<string name="list_rotation_land">強制橫屏顯示</string>
<string name="list_rotation_port">強制竪屏顯示</string>
<string name="list_rotation_auto">自動</string>
<string name="list_camera_ctrlaspace">Ctrl+A 然後 Space</string>
+ <string name="list_camera_ctrla">Ctrl+A</string>
+ <string name="list_camera_esc">Esc</string>
+ <string name="list_camera_esc_a">Esc+A</string>
<string name="list_camera_none">無</string>
+ <string name="list_delkey_backspace">退格</string>
<string name="list_delkey_del">刪除</string>
+ <string name="delete_message">您確定要刪除 \'%1$s\'</string>
<string name="delete_pos">是,刪除</string>
<string name="delete_neg">取消</string>
<string name="wizard_agree">同意</string>
- <string name="wizard_next">下一頁</string>
+ <string name="wizard_next">下一步</string>
<string name="wizard_back">返回</string>
- <string name="terminal_no_hosts_connected">當前沒有已連接主機</string>
+ <string name="terminal_no_hosts_connected">目前沒有已連線主機</string>
<string name="terminal_connecting">連接到 %1$s:%2$d,通過 %3$s</string>
+ <string name="terminal_sucess">驗證主機 \'%1$s\' 金鑰: %2$s</string>
<string name="terminal_failed">主機驗證失敗</string>
- <string name="terminal_using_s2c_algorithm">服務器到客戶端算法:%1$s %2$s</string>
- <string name="terminal_using_c2s_algorithm">客戶端到服務器算法:%1$s %2$s</string>
- <string name="terminal_using_algorithm">使用算法:%1$s %2$s</string>
+ <string name="terminal_using_s2c_algorithm">伺服器到用戶端算法:%1$s %2$s</string>
+ <string name="terminal_using_c2s_algorithm">用戶端到伺服器算法:%1$s %2$s</string>
+ <string name="terminal_using_algorithm">使用演算法:%1$s %2$s</string>
<string name="terminal_auth">嘗試驗證</string>
+ <string name="terminal_auth_pass">正在嘗試 \'密碼\' 認證</string>
+ <string name="terminal_auth_pass_fail">認證方法 \'密碼\' 失敗</string>
+ <string name="terminal_auth_pubkey_any">正在嘗試 \'公鑰\' 認證暨記憶體中任何公鑰</string>
<string name="terminal_auth_pubkey_invalid">所選擇的公鑰無法使用,請在主機編輯中嘗試重新選擇</string>
- <string name="terminal_no_session">因為主機的相關設置,不會啓動會話。</string>
- <string name="terminal_enable_portfoward">啓用端口轉發:%1$s</string>
- <string name="local_shell_unavailable">失敗! 本地Shell在該手機上不可用</string>
- <string name="notification_text">%1$s 需要你的註意</string>
- <string name="upgrade">新版本</string>
- <string name="upgrade_pos">是,昇級</string>
- <string name="upgrade_neg">暫時不</string>
- <string name="no">不</string>
+ <string name="terminal_auth_pubkey_specific">正在嘗試 \'公鑰\' 認證暨指定公鑰</string>
+ <string name="terminal_auth_pubkey_fail">認證方法 \'公鑰\' 暨金鑰 \'%1$s\' 失敗</string>
+ <string name="terminal_auth_ki">正在嘗試 \'keyboard-interactive\' 認證</string>
+ <string name="terminal_auth_ki_fail">認證方法 \'keyboard-interactive\' 失敗</string>
+ <string name="terminal_auth_fail">[您的主機不支援 \'密碼\' 或 \'keyboard-interactive\' 驗證機制。]</string>
+ <string name="terminal_no_session">因為主機的相關設置,不會啓動會話階段。</string>
+ <string name="terminal_enable_portfoward">啓用連接埠轉址:%1$s</string>
+ <string name="local_shell_unavailable">失敗!此手機 Shell 不可用。</string>
+ <string name="notification_text">%1$s 需要您的注意。</string>
+ <string name="no">否</string>
<string name="with_confirmation">已確認過</string>
<string name="yes">是</string>
- <string name="exceptions_submit_message">似乎ConnectBot在最後運行時出現異常. 請回報給ConnectBot開發者</string>
+ <string name="exceptions_submit_message">ConnectBot 在上次執行時出現異常。回報給 ConnectBot 開發者?</string>
<string name="menu_colors_reset">重設</string>
+ <string name="app_is_running">ConnectBot 執行中</string>
<string name="color_red">紅</string>
<string name="color_green">綠</string>
<string name="color_blue">藍</string>
<string name="color_gray">灰</string>
+ <string name="colors_fg_label">前景:%1$d</string>
+ <string name="color_bg_label">背景:%1$d</string>
+ <string name="image_description_connected">已連線。</string>
+ <string name="image_description_key_is_locked">金鑰已鎖定。</string>
+ <string name="image_description_toggle_control_character">切換控制字元</string>
+ <string name="image_description_send_escape_character">送出 Esc 字元。</string>
+ <string name="image_description_show_keyboard">顯示鍵盤。</string>
</resources>
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index c9090ea..4721490 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -71,16 +71,16 @@
<item>gray</item>
</string-array>
- <string-array name="list_update" translatable="false">
- <item>@string/list_update_daily</item>
- <item>@string/list_update_weekly</item>
- <item>@string/list_update_never</item>
+ <string-array name="list_stickymodifiers" translatable="false">
+ <item>@string/no</item>
+ <item>@string/only_alt</item>
+ <item>@string/yes</item>
</string-array>
- <string-array name="list_update_values" translatable="false">
- <item>Daily</item>
- <item>Weekly</item>
- <item>Never</item>
+ <string-array name="list_stickymodifiers_values" translatable="false">
+ <item>no</item>
+ <item>alt</item>
+ <item>yes</item>
</string-array>
<string-array name="list_keymode" translatable="false">
diff --git a/res/values/notrans.xml b/res/values/notrans.xml
index f28cffc..3142ed6 100644
--- a/res/values/notrans.xml
+++ b/res/values/notrans.xml
@@ -19,10 +19,6 @@
-->
<resources>
<string name="app_name" translatable="false">ConnectBot</string>
- <!-- DO NOT MANUALLY UPDATE VERSION!!!
- Updating is update by the ant task "update-version" in build.xml
- -->
- <string name="msg_version" translatable="false">ConnectBot (working copy)</string>
<string name="copyright_info" translatable="false">Before we get started, we need to get some legal information out of the way. ConnectBot is provided under the Apache License, Version 2.0 (the &#x201C;License&#x201D;). Here are a few key points:\n\nYou may not use this program except in compliance with the License. You may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under the License is distributed on an &#x201C;AS IS&#x201D; BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.</string>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 591662d..d8e3bb5 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -19,7 +19,10 @@
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Summary of what ConnectBot is; used as a short description in the Android running apps list -->
<string name="app_desc">"Simple, powerful, open-source SSH client."</string>
+
+ <!-- Summary of what the ConnectBot service does; displayed in the Android running apps list -->
<string name="service_desc">"Maintains SSH connections and loaded pubkeys"</string>
<!-- Window title for the Host List -->
@@ -34,6 +37,8 @@
<string name="title_help">"Help"</string>
<!-- Window title for color list editing screen -->
<string name="title_colors">"Colors"</string>
+ <!-- Dialog title for color picker dialog -->
+ <string name="title_color_picker">"Pick a color"</string>
<string name="resolve_connect">"Connect"</string>
<!-- Menu selection where user must move finger randomly over an area to gather entropy (collect random bits) -->
@@ -153,21 +158,39 @@
<!-- Summary for the rotation mode preference -->
<string name="pref_rotation_summary">"How to change rotation when keyboard popped in/out"</string>
+ <!-- Name for the titlebar hide preference -->
+ <string name="pref_titlebarhide_title">"Autohide title bar"</string>
+ <!-- Summary for the titlebar hide preference -->
+ <string name="pref_titlebarhide_summary">"Tap console to show title bar and access menu"</string>
+
<!-- Name for the full screen preference -->
<string name="pref_fullscreen_title">"Full screen"</string>
<!-- Summary for the full screen preference -->
<string name="pref_fullscreen_summary">"Hide status bar while in console"</string>
+ <!-- The category title for keyboard preferences -->
+ <string name="pref_keyboard_category">"Keyboard"</string>
+
+ <!-- Name for the shifted numbers are f-keys preference -->
+ <string name="pref_shiftfkeys_title">"Shift+num are F-keys"</string>
+ <!-- Summary for the shifted numbers are f-keys preference -->
+ <string name="pref_shiftfkeys_summary">"On hardware keyboards, number keys send F1-F10 with shift"</string>
+
+ <!-- Name for the ctrl'ed numbers are f-keys preference -->
+ <string name="pref_ctrlfkeys_title">"Ctrl+num are F-keys"</string>
+ <!-- Summary for the ctrl'ed numbers are f-keys preference -->
+ <string name="pref_ctrlfkeys_summary">"On software keyboards, number keys send F1-F10 with ctrl"</string>
+
+ <!-- Name for the volume keys control font size preference -->
+ <string name="pref_volumefont_title">"Volume keys change font size"</string>
+ <!-- Summary for the volume keys control font size preference -->
+ <string name="pref_volumefont_summary">"Font size can also be changed in per-host settings"</string>
+
<!-- Name for the memorize keys preference -->
<string name="pref_memkeys_title">"Remember keys in memory"</string>
<!-- Summary for the memorize keys preference -->
<string name="pref_memkeys_summary">"Keep unlocked keys in memory until backend service is terminated"</string>
- <!-- Name for the update check preference -->
- <string name="pref_update_title">"Update check"</string>
- <!-- Summary for the update check preference -->
- <string name="pref_update_summary">"Set the maximum frequency to check for ConnectBot updates"</string>
-
<!-- Name for the preference that forces the service to stay running in the background.-->
<string name="pref_conn_persist_title">"Persist connections"</string>
<!-- Summary for the preference that forces the service to stay running in the background. -->
@@ -178,6 +201,14 @@
<!-- Summary for the keyboard shortcuts preference -->
<string name="pref_keymode_summary">"Select how to use Alt for '/' and Shift for Tab"</string>
+ <!-- Name for the sticky modifiers preference -->
+ <string name="pref_stickymodifiers_title">"Sticky modifiers"</string>
+ <!-- Summary for the sticky modifiers preference -->
+ <string name="pref_stickymodifiers_summary">"Modifier keys remain enabled until another key is pressed"</string>
+
+ <!-- Sticky modifier preference value description for when only the Alt key should be a sticky modifier. -->
+ <string name="only_alt">"Only alt"</string>
+
<!-- Name for the camera shortcut usage preference -->
<string name="pref_camera_title">"Camera shortcut"</string>
<!-- Summary for the camera shortcut usage preference -->
@@ -227,13 +258,6 @@
<!-- Preference to use any pubkey to authenticate to this host. -->
<string name="list_pubkeyids_any">"Use any unlocked key"</string>
- <!-- Frequency for which to check for program updates. -->
- <string name="list_update_daily">"Daily"</string>
- <!-- Frequency for which to check for program updates. -->
- <string name="list_update_weekly">"Weekly"</string>
- <!-- Frequency for which to check for program updates. -->
- <string name="list_update_never">"Never"</string>
-
<!-- Host nickname field preference title -->
<string name="hostpref_nickname_title">"Nickname"</string>
@@ -269,6 +293,10 @@
<!-- Summary for preference asking whether the host should be reconnected to when it disconnects -->
<string name="hostpref_stayconnected_summary">"Try to reconnect to host if disconnected"</string>
+ <!-- Setting for whether we should prompt to close after getting disconnected -->
+ <string name="hostpref_quickdisconnect_title">"Close on disconnect"</string>
+ <string name="hostpref_quickdisconnect_summary">"Close immediately after remote disconnect without prompting."</string>
+
<!-- Setting for what key code is sent to the server when DEL key is pressed. -->
<string name="hostpref_delkey_title">"DEL Key"</string>
<!-- Summary for field asking what key code is sent to the server when DEL key is pressed. -->
@@ -293,12 +321,6 @@
<!-- Displayed to indicate a host has never been connected to. -->
<string name="bind_never">"Never connected"</string>
- <!-- The time that has elapsed since a host was connected to when it has been less than an hour. -->
- <string name="bind_minutes">"%1$s minutes ago"</string>
- <!-- The time that has elapsed since a host was connected to when it has been less than a day. -->
- <string name="bind_hours">"%1$s hours ago"</string>
- <!-- The time that has elapsed since a host was connected to when it has been a day or more. -->
- <string name="bind_days">"%1$s days ago"</string>
<!-- Message given when user copies from the terminal. -->
<string name="console_copy_done">"Copied %1$d bytes to clipboard"</string>
@@ -415,6 +437,11 @@
<!-- Displayed on the terminal describing the cryptographic algorithm names -->
<string name="terminal_using_algorithm">"Using algorithm: %1$s %2$s"</string>
+ <!-- Displayed on the terminal during a SSH connection describing the cryptographic key
+ exchange algorithm used to establish a shared secret between this program and the
+ server. -->
+ <string name="terminal_kex_algorithm">Key exchange algorithm: %s</string>
+
<string name="terminal_auth">"Trying to authenticate"</string>
<string name="terminal_auth_pass">"Attempting 'password' authentication"</string>
@@ -438,13 +465,6 @@
<!-- Text sent to the user to alert them that a Terminal Bell is received in a background session -->
<string name="notification_text">"%1$s wants your attention."</string>
- <!-- Dialog title when a new version of ConnectBot is detected. -->
- <string name="upgrade">"New version"</string>
- <!-- Button selection to upgrade to the latest ConnectBot when one is available. -->
- <string name="upgrade_pos">"Yes, upgrade"</string>
- <!-- Button selection to skip upgrading to the latest ConnectBot when one is available. -->
- <string name="upgrade_neg">"Not right now"</string>
-
<!-- Preference selection for SSH Authentication Agent to never use pubkeys -->
<string name="no">"No"</string>
<!-- Preference selection for SSH Authentication Agent to be able to use pubkeys "with confirmation" only -->
@@ -465,4 +485,28 @@
<string name="color_green">"green"</string>
<string name="color_blue">"blue"</string>
<string name="color_gray">"gray"</string>
+
+ <!-- Very short label indicating the number next to it is "foreground color" number. -->
+ <string name="colors_fg_label">"FG: %1$d"</string>
+
+ <!-- Very short label indicating the number next to it is "background color" number. -->
+ <string name="color_bg_label">"BG: %1$d"</string>
+
+ <!-- Describes the image of the "connected to host" icon for accessibility purposes. -->
+ <string name="image_description_connected">"Connected."</string>
+
+ <!-- Describes the icon of the "locked pubkey" icon for accessibility purposes. -->
+ <string name="image_description_key_is_locked">"Key is locked."</string>
+
+ <!-- Describes the icon of the "send control character" button in the terminal view for
+ accessibility purposes. -->
+ <string name="image_description_toggle_control_character">Toggle control character.</string>
+
+ <!-- Describes the icon of the "send escape character" button in the terminal view for
+ accessibility purposes. -->
+ <string name="image_description_send_escape_character">Send escape character.</string>
+
+ <!-- Describes the icon of the "show keyboard" button in the terminal view for accessibility
+ purposes. -->
+ <string name="image_description_show_keyboard">Show keyboard.</string>
</resources>
diff --git a/res/values/version.xml b/res/values/version.xml
new file mode 100644
index 0000000..94227c9
--- /dev/null
+++ b/res/values/version.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="msg_version" translatable="false">ConnectBot (working copy)</string>
+</resources>
diff --git a/res/xml/host_prefs.xml b/res/xml/host_prefs.xml
index fea90cb..a2fc1a4 100644
--- a/res/xml/host_prefs.xml
+++ b/res/xml/host_prefs.xml
@@ -85,6 +85,12 @@
android:summary="@string/hostpref_stayconnected_summary"
/>
+ <CheckBoxPreference
+ android:key="quickdisconnect"
+ android:title="@string/hostpref_quickdisconnect_title"
+ android:summary="@string/hostpref_quickdisconnect_summary"
+ />
+
<ListPreference
android:key="delkey"
android:title="@string/hostpref_delkey_title"
diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml
index 8726ea4..70bdb5b 100644
--- a/res/xml/preferences.xml
+++ b/res/xml/preferences.xml
@@ -27,15 +27,6 @@
android:defaultValue="true"
/>
- <ListPreference
- android:key="update"
- android:title="@string/pref_update_title"
- android:summary="@string/pref_update_summary"
- android:entries="@array/list_update"
- android:entryValues="@array/list_update_values"
- android:defaultValue="Daily"
- />
-
<CheckBoxPreference
android:key="connPersist"
android:title="@string/pref_conn_persist_title"
@@ -43,6 +34,13 @@
android:defaultValue="true"
/>
+ <CheckBoxPreference
+ android:key="wifilock"
+ android:title="@string/pref_wifilock_title"
+ android:summary="@string/pref_wifilock_summary"
+ android:defaultValue="true"
+ />
+
<PreferenceCategory
android:title="@string/pref_emulation_category">
@@ -78,19 +76,67 @@
/>
<CheckBoxPreference
+ android:key="titlebarhide"
+ android:title="@string/pref_titlebarhide_title"
+ android:summary="@string/pref_titlebarhide_summary"
+ android:defaultValue="false"
+ />
+
+ <CheckBoxPreference
android:key="fullscreen"
android:title="@string/pref_fullscreen_title"
android:summary="@string/pref_fullscreen_summary"
android:defaultValue="false"
/>
+ <CheckBoxPreference
+ android:key="volumefont"
+ android:title="@string/pref_volumefont_title"
+ android:summary="@string/pref_volumefont_summary"
+ android:defaultValue="true"
+ />
+
+ <CheckBoxPreference
+ android:key="keepalive"
+ android:title="@string/pref_keepalive_title"
+ android:summary="@string/pref_keepalive_summary"
+ android:defaultValue="true"
+ />
+ </PreferenceCategory>
+
+ <PreferenceCategory
+ android:title="@string/pref_keyboard_category">
+
+ <CheckBoxPreference
+ android:key="shiftfkeys"
+ android:title="@string/pref_shiftfkeys_title"
+ android:summary="@string/pref_shiftfkeys_summary"
+ android:defaultValue="false"
+ />
+
+ <CheckBoxPreference
+ android:key="ctrlfkeys"
+ android:title="@string/pref_ctrlfkeys_title"
+ android:summary="@string/pref_ctrlfkeys_summary"
+ android:defaultValue="false"
+ />
+
+ <ListPreference
+ android:key="stickymodifiers"
+ android:title="@string/pref_stickymodifiers_title"
+ android:summary="@string/pref_stickymodifiers_summary"
+ android:entries="@array/list_stickymodifiers"
+ android:entryValues="@array/list_stickymodifiers_values"
+ android:defaultValue="no"
+ />
+
<ListPreference
android:key="keymode"
android:title="@string/pref_keymode_title"
android:summary="@string/pref_keymode_summary"
android:entries="@array/list_keymode"
android:entryValues="@array/list_keymode_values"
- android:defaultValue="Use right-side keys"
+ android:defaultValue="none"
/>
<ListPreference
@@ -103,20 +149,6 @@
/>
<CheckBoxPreference
- android:key="keepalive"
- android:title="@string/pref_keepalive_title"
- android:summary="@string/pref_keepalive_summary"
- android:defaultValue="true"
- />
-
- <CheckBoxPreference
- android:key="wifilock"
- android:title="@string/pref_wifilock_title"
- android:summary="@string/pref_wifilock_summary"
- android:defaultValue="true"
- />
-
- <CheckBoxPreference
android:key="bumpyarrows"
android:title="@string/pref_bumpyarrows_title"
android:summary="@string/pref_bumpyarrows_summary"
diff --git a/src/com/jcraft/jzlib/ZStreamException.java b/src/com/jcraft/jzlib/ZStreamException.java
index 424b74b..308bb8a 100644
--- a/src/com/jcraft/jzlib/ZStreamException.java
+++ b/src/com/jcraft/jzlib/ZStreamException.java
@@ -1,44 +1,44 @@
-/* -*-mode:java; c-basic-offset:2; -*- */
-/*
-Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- 1. Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the distribution.
-
- 3. The names of the authors may not be used to endorse or promote products
- derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
-INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
-FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
-INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
-OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
-EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-/*
- * This program is based on zlib-1.1.3, so all credit should go authors
- * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
- * and contributors of zlib.
- */
-
-package com.jcraft.jzlib;
-
-public class ZStreamException extends java.io.IOException {
- public ZStreamException() {
- super();
- }
- public ZStreamException(String s) {
- super(s);
- }
-}
+/* -*-mode:java; c-basic-offset:2; -*- */
+/*
+Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the distribution.
+
+ 3. The names of the authors may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * This program is based on zlib-1.1.3, so all credit should go authors
+ * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
+ * and contributors of zlib.
+ */
+
+package com.jcraft.jzlib;
+
+public class ZStreamException extends java.io.IOException {
+ public ZStreamException() {
+ super();
+ }
+ public ZStreamException(String s) {
+ super(s);
+ }
+}
diff --git a/src/com/nullwire/trace/DefaultExceptionHandler.java b/src/com/nullwire/trace/DefaultExceptionHandler.java
deleted file mode 100644
index f9abe70..0000000
--- a/src/com/nullwire/trace/DefaultExceptionHandler.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package com.nullwire.trace;
-
-import java.io.BufferedWriter;
-import java.io.FileWriter;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.io.Writer;
-import java.lang.Thread.UncaughtExceptionHandler;
-import java.util.Random;
-
-import android.util.Log;
-
-public class DefaultExceptionHandler implements UncaughtExceptionHandler {
-
- private UncaughtExceptionHandler defaultExceptionHandler;
-
- private static final String TAG = "UNHANDLED_EXCEPTION";
-
- // constructor
- public DefaultExceptionHandler(UncaughtExceptionHandler pDefaultExceptionHandler)
- {
- defaultExceptionHandler = pDefaultExceptionHandler;
- }
-
- // Default exception handler
- public void uncaughtException(Thread t, Throwable e) {
- // Here you should have a more robust, permanent record of problems
- final Writer result = new StringWriter();
- final PrintWriter printWriter = new PrintWriter(result);
- e.printStackTrace(printWriter);
- try {
- // Random number to avoid duplicate files
- Random generator = new Random();
- int random = generator.nextInt(99999);
- // Embed version in stacktrace filename
- String filename = G.APP_VERSION + "-" + Integer.toString(random);
- Log.d(TAG, "Writing unhandled exception to: " + G.FILES_PATH + "/"
- + filename + ".stacktrace");
- // Write the stacktrace to disk
- BufferedWriter bos = new BufferedWriter(new FileWriter(G.FILES_PATH
- + "/" + filename + ".stacktrace"));
- bos.write(G.APP_VERSION + "\n");
- bos.write(G.APP_DESCRIPTION + "\n");
- bos.write(G.ANDROID_VERSION + "\n");
- bos.write(G.PHONE_MODEL + "\n");
- bos.write(result.toString());
- bos.flush();
- // Close up everything
- bos.close();
- } catch (Exception ebos) {
- // Nothing much we can do about this - the game is over
- ebos.printStackTrace();
- }
- Log.d(TAG, result.toString());
- //call original handler
- defaultExceptionHandler.uncaughtException(t, e);
- }
-}
diff --git a/src/com/nullwire/trace/ExceptionClickListener.java b/src/com/nullwire/trace/ExceptionClickListener.java
deleted file mode 100644
index 525b755..0000000
--- a/src/com/nullwire/trace/ExceptionClickListener.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/**
- *
- */
-package com.nullwire.trace;
-
-import java.lang.ref.WeakReference;
-
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnClickListener;
-import android.util.Log;
-
-/**
- * @author Kenny Root
- *
- */
-public class ExceptionClickListener implements OnClickListener {
- public static String TAG = "com.nullwire.trace.ExceptionClickListener";
-
- WeakReference<Context> context;
-
- public ExceptionClickListener() { }
-
- public void onClick(DialogInterface dialog, int whichButton) {
- switch (whichButton) {
- case DialogInterface.BUTTON_POSITIVE:
- dialog.dismiss();
- Log.d(TAG, "Trying to submit stack traces");
- new Thread(new Runnable() {
- public void run() {
- ExceptionHandler.submitStackTraces();
- }
- }).start();
- break;
- case DialogInterface.BUTTON_NEGATIVE:
- dialog.dismiss();
- Log.d(TAG, "Deleting old stack traces.");
- new Thread(new Runnable() {
- public void run() {
- ExceptionHandler.removeStackTraces();
- }
- }).start();
- break;
- default:
- Log.d("ExceptionClickListener", "Got unknown button click: " + whichButton);
- dialog.cancel();
- }
- }
-}
diff --git a/src/com/nullwire/trace/ExceptionHandler.java b/src/com/nullwire/trace/ExceptionHandler.java
deleted file mode 100644
index 0fabe68..0000000
--- a/src/com/nullwire/trace/ExceptionHandler.java
+++ /dev/null
@@ -1,216 +0,0 @@
-package com.nullwire.trace;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.FilenameFilter;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.http.NameValuePair;
-import org.apache.http.client.entity.UrlEncodedFormEntity;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.message.BasicNameValuePair;
-import org.apache.http.protocol.HTTP;
-import org.connectbot.R;
-
-import android.app.AlertDialog;
-import android.content.Context;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.Handler;
-import android.os.Message;
-import android.util.Log;
-
-public class ExceptionHandler {
- public static String TAG = "com.nullwire.trace.ExceptionsHandler";
-
- private static String[] stackTraceFileList = null;
-
- /**
- * @param context
- */
- public static void checkForTraces(final Context context) {
- new Thread(new Runnable() {
- public void run() {
- String[] stackTraces = searchForStackTraces();
- if (stackTraces != null && stackTraces.length > 0) {
- Log.d(TAG, "number of stack traces: " + stackTraces.length);
- submissionHandler.sendMessage(submissionHandler
- .obtainMessage(-1, context));
- }
- }
- }).start();
- }
-
-
- /**
- * Register handler for unhandled exceptions.
- *
- * @param context
- */
- public static boolean register(Context context) {
- Log.i(TAG, "Registering default exceptions handler");
- // Get information about the Package
- PackageManager pm = context.getPackageManager();
- try {
- PackageInfo pi;
- // Version
- pi = pm.getPackageInfo(context.getPackageName(), 0);
- // Package name
- G.APP_PACKAGE = pi.packageName;
- // Version information
- G.APP_VERSION = pi.versionName;
- G.APP_DESCRIPTION = context.getString(R.string.msg_version);
- // Files dir for storing the stack traces
- G.FILES_PATH = context.getFilesDir().getAbsolutePath();
- // Device model
- G.PHONE_MODEL = android.os.Build.MODEL;
- // Android version
- G.ANDROID_VERSION = android.os.Build.VERSION.RELEASE;
- } catch (NameNotFoundException e) {
- e.printStackTrace();
- }
-
- Log.d(TAG, "APP_VERSION: " + G.APP_VERSION);
- Log.d(TAG, "APP_PACKAGE: " + G.APP_PACKAGE);
- Log.d(TAG, "FILES_PATH: " + G.FILES_PATH);
- Log.d(TAG, "URL: " + G.URL);
-
- boolean stackTracesFound = false;
-
- // We'll return true if any stack traces were found
- String[] list = searchForStackTraces();
- if (list != null && list.length > 0) {
- stackTracesFound = true;
- }
-
- new Thread() {
- @Override
- public void run() {
- UncaughtExceptionHandler currentHandler = Thread.getDefaultUncaughtExceptionHandler();
-
- if (currentHandler != null) {
- Log.d(TAG, "current handler class="+currentHandler.getClass().getName());
- }
- // don't register again if already registered
- if (!(currentHandler instanceof DefaultExceptionHandler)) {
- // Register default exceptions handler
- Thread.setDefaultUncaughtExceptionHandler(
- new DefaultExceptionHandler(currentHandler));
- }
- }
- }.start();
-
- return stackTracesFound;
- }
-
- /**
- * Search for stack trace files.
- * @return
- */
- private static String[] searchForStackTraces() {
- if (stackTraceFileList != null) {
- return stackTraceFileList;
- }
- File dir = new File(G.FILES_PATH + "/");
- // Try to create the files folder if it doesn't exist
- dir.mkdir();
- // Filter for ".stacktrace" files
- FilenameFilter filter = new FilenameFilter() {
- public boolean accept(File dir, String name) {
- return name.endsWith(".stacktrace");
- }
- };
- return (stackTraceFileList = dir.list(filter));
- }
-
- private static Handler submissionHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- Context context = (Context) msg.obj;
- ExceptionClickListener clickListener = new ExceptionClickListener();
- new AlertDialog.Builder(context)
- .setMessage(R.string.exceptions_submit_message)
- .setPositiveButton(android.R.string.yes, clickListener)
- .setNegativeButton(android.R.string.no, clickListener)
- .create().show();
- }
- };
-
- /**
- * Look into the files folder to see if there are any "*.stacktrace" files.
- * If any are present, submit them to the trace server.
- */
- public static void submitStackTraces() {
- try {
- Log.d(TAG, "Looking for exceptions in: " + G.FILES_PATH);
- String[] list = searchForStackTraces();
- if (list != null && list.length > 0) {
- Log.d(TAG, "Found " + list.length + " stacktrace(s)");
- StringBuilder contents = new StringBuilder();
- for (int i = 0; i < list.length; i++) {
- String filePath = G.FILES_PATH + "/" + list[i];
- // Extract the version from the filename:
- // "packagename-version-...."
- String version = list[i].split("-")[0];
- Log.d(TAG, "Stacktrace in file '" + filePath
- + "' belongs to version " + version);
- // Read contents of stacktrace
- contents.setLength(0);
- BufferedReader input;
- try {
- input = new BufferedReader(new FileReader(filePath));
- } catch (FileNotFoundException fnf) {
- continue;
- }
- String line = null;
- while ((line = input.readLine()) != null) {
- contents.append(line);
- contents.append(System.getProperty("line.separator"));
- }
- input.close();
- String stacktrace;
- stacktrace = contents.toString();
- Log.d(TAG, "Transmitting stack trace: " + stacktrace);
- // Transmit stack trace with POST request
- DefaultHttpClient httpClient = new DefaultHttpClient();
- HttpPost httpPost = new HttpPost(G.URL);
- List<NameValuePair> nvps = new ArrayList<NameValuePair>();
- nvps.add(new BasicNameValuePair("package_name", G.APP_PACKAGE));
- nvps.add(new BasicNameValuePair("package_version", version));
- nvps.add(new BasicNameValuePair("stacktrace", stacktrace));
- httpPost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
- // We don't care about the response, so we just hope it went
- // well and on with it
- httpClient.execute(httpPost);
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- removeStackTraces();
- }
- }
-
- public synchronized static void removeStackTraces() {
- try {
- String[] list = searchForStackTraces();
-
- if (list == null)
- return;
-
- for (int i = 0; i < list.length; i++) {
- File file = new File(G.FILES_PATH + "/" + list[i]);
- file.delete();
- }
-
- stackTraceFileList = null;
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-}
diff --git a/src/com/nullwire/trace/G.java b/src/com/nullwire/trace/G.java
deleted file mode 100644
index 2e63aa1..0000000
--- a/src/com/nullwire/trace/G.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.nullwire.trace;
-
-public class G {
- // This must be set by the application - it used to automatically
- // transmit exceptions to the trace server
- public static String FILES_PATH = null;
- public static String APP_PACKAGE = "unknown";
- public static String APP_VERSION = "unknown";
- public static String APP_DESCRIPTION = "unknown";
- public static String PHONE_MODEL = "unknown";
- public static String ANDROID_VERSION = "unknown";
- // Where are the stack traces posted?
- public static String URL = "http://connectbot.the-b.org/trace/";
-}
diff --git a/src/com/trilead/ssh2/AuthAgentCallback.java b/src/com/trilead/ssh2/AuthAgentCallback.java
index c395198..7fe270b 100644
--- a/src/com/trilead/ssh2/AuthAgentCallback.java
+++ b/src/com/trilead/ssh2/AuthAgentCallback.java
@@ -1,5 +1,6 @@
package com.trilead.ssh2;
+import java.security.KeyPair;
import java.util.Map;
/**
@@ -24,7 +25,7 @@ public interface AuthAgentCallback {
* @param lifetime lifetime in seconds for key to be remembered
* @return success or failure
*/
- boolean addIdentity(Object key, String comment, boolean confirmUse, int lifetime);
+ boolean addIdentity(KeyPair pair, String comment, boolean confirmUse, int lifetime);
/**
* @param publicKey byte blob containing the OpenSSH-format encoded public key
@@ -43,7 +44,7 @@ public interface AuthAgentCallback {
* containing a DSA or RSA private key of
* the user in Trilead object format.
*/
- Object getPrivateKey(byte[] publicKey);
+ KeyPair getKeyPair(byte[] publicKey);
/**
* @return
diff --git a/src/com/trilead/ssh2/ChannelCondition.java b/src/com/trilead/ssh2/ChannelCondition.java
index f3a0755..df1ad50 100644
--- a/src/com/trilead/ssh2/ChannelCondition.java
+++ b/src/com/trilead/ssh2/ChannelCondition.java
@@ -1,61 +1,61 @@
-
-package com.trilead.ssh2;
-
-/**
- * Contains constants that can be used to specify what conditions to wait for on
- * a SSH-2 channel (e.g., represented by a {@link Session}).
- *
- * @see Session#waitForCondition(int, long)
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: ChannelCondition.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-
-public abstract interface ChannelCondition
-{
- /**
- * A timeout has occurred, none of your requested conditions is fulfilled.
- * However, other conditions may be true - therefore, NEVER use the "=="
- * operator to test for this (or any other) condition. Always use
- * something like <code>((cond & ChannelCondition.CLOSED) != 0)</code>.
- */
- public static final int TIMEOUT = 1;
-
- /**
- * The underlying SSH-2 channel, however not necessarily the whole connection,
- * has been closed. This implies <code>EOF</code>. Note that there may still
- * be unread stdout or stderr data in the local window, i.e, <code>STDOUT_DATA</code>
- * or/and <code>STDERR_DATA</code> may be set at the same time.
- */
- public static final int CLOSED = 2;
-
- /**
- * There is stdout data available that is ready to be consumed.
- */
- public static final int STDOUT_DATA = 4;
-
- /**
- * There is stderr data available that is ready to be consumed.
- */
- public static final int STDERR_DATA = 8;
-
- /**
- * EOF on has been reached, no more _new_ stdout or stderr data will arrive
- * from the remote server. However, there may be unread stdout or stderr
- * data, i.e, <code>STDOUT_DATA</code> or/and <code>STDERR_DATA</code>
- * may be set at the same time.
- */
- public static final int EOF = 16;
-
- /**
- * The exit status of the remote process is available.
- * Some servers never send the exist status, or occasionally "forget" to do so.
- */
- public static final int EXIT_STATUS = 32;
-
- /**
- * The exit signal of the remote process is available.
- */
- public static final int EXIT_SIGNAL = 64;
-
-}
+
+package com.trilead.ssh2;
+
+/**
+ * Contains constants that can be used to specify what conditions to wait for on
+ * a SSH-2 channel (e.g., represented by a {@link Session}).
+ *
+ * @see Session#waitForCondition(int, long)
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: ChannelCondition.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+
+public abstract interface ChannelCondition
+{
+ /**
+ * A timeout has occurred, none of your requested conditions is fulfilled.
+ * However, other conditions may be true - therefore, NEVER use the "=="
+ * operator to test for this (or any other) condition. Always use
+ * something like <code>((cond & ChannelCondition.CLOSED) != 0)</code>.
+ */
+ public static final int TIMEOUT = 1;
+
+ /**
+ * The underlying SSH-2 channel, however not necessarily the whole connection,
+ * has been closed. This implies <code>EOF</code>. Note that there may still
+ * be unread stdout or stderr data in the local window, i.e, <code>STDOUT_DATA</code>
+ * or/and <code>STDERR_DATA</code> may be set at the same time.
+ */
+ public static final int CLOSED = 2;
+
+ /**
+ * There is stdout data available that is ready to be consumed.
+ */
+ public static final int STDOUT_DATA = 4;
+
+ /**
+ * There is stderr data available that is ready to be consumed.
+ */
+ public static final int STDERR_DATA = 8;
+
+ /**
+ * EOF on has been reached, no more _new_ stdout or stderr data will arrive
+ * from the remote server. However, there may be unread stdout or stderr
+ * data, i.e, <code>STDOUT_DATA</code> or/and <code>STDERR_DATA</code>
+ * may be set at the same time.
+ */
+ public static final int EOF = 16;
+
+ /**
+ * The exit status of the remote process is available.
+ * Some servers never send the exist status, or occasionally "forget" to do so.
+ */
+ public static final int EXIT_STATUS = 32;
+
+ /**
+ * The exit signal of the remote process is available.
+ */
+ public static final int EXIT_SIGNAL = 64;
+
+}
diff --git a/src/com/trilead/ssh2/Connection.java b/src/com/trilead/ssh2/Connection.java
index 98e5fdd..163fdb5 100644
--- a/src/com/trilead/ssh2/Connection.java
+++ b/src/com/trilead/ssh2/Connection.java
@@ -1,1625 +1,1628 @@
-
-package com.trilead.ssh2;
-
-import java.io.CharArrayWriter;
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.net.SocketTimeoutException;
-import java.security.SecureRandom;
-import java.util.Vector;
-
-import com.trilead.ssh2.auth.AuthenticationManager;
-import com.trilead.ssh2.channel.ChannelManager;
-import com.trilead.ssh2.crypto.CryptoWishList;
-import com.trilead.ssh2.crypto.cipher.BlockCipherFactory;
-import com.trilead.ssh2.crypto.digest.MAC;
-import com.trilead.ssh2.log.Logger;
-import com.trilead.ssh2.packets.PacketIgnore;
-import com.trilead.ssh2.transport.KexManager;
-import com.trilead.ssh2.transport.TransportManager;
-import com.trilead.ssh2.util.TimeoutService;
-import com.trilead.ssh2.util.TimeoutService.TimeoutToken;
-
-/**
- * A <code>Connection</code> is used to establish an encrypted TCP/IP
- * connection to a SSH-2 server.
- * <p>
- * Typically, one
- * <ol>
- * <li>creates a {@link #Connection(String) Connection} object.</li>
- * <li>calls the {@link #connect() connect()} method.</li>
- * <li>calls some of the authentication methods (e.g.,
- * {@link #authenticateWithPublicKey(String, File, String) authenticateWithPublicKey()}).</li>
- * <li>calls one or several times the {@link #openSession() openSession()}
- * method.</li>
- * <li>finally, one must close the connection and release resources with the
- * {@link #close() close()} method.</li>
- * </ol>
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: Connection.java,v 1.3 2008/04/01 12:38:09 cplattne Exp $
- */
-
-public class Connection
-{
- /**
- * The identifier presented to the SSH-2 server.
- */
- public final static String identification = "TrileadSSH2Java_213";
-
- /**
- * Will be used to generate all random data needed for the current
- * connection. Note: SecureRandom.nextBytes() is thread safe.
- */
- private SecureRandom generator;
-
- /**
- * Unless you know what you are doing, you will never need this.
- *
- * @return The list of supported cipher algorithms by this implementation.
- */
- public static synchronized String[] getAvailableCiphers()
- {
- return BlockCipherFactory.getDefaultCipherList();
- }
-
- /**
- * Unless you know what you are doing, you will never need this.
- *
- * @return The list of supported MAC algorthims by this implementation.
- */
- public static synchronized String[] getAvailableMACs()
- {
- return MAC.getMacList();
- }
-
- /**
- * Unless you know what you are doing, you will never need this.
- *
- * @return The list of supported server host key algorthims by this
- * implementation.
- */
- public static synchronized String[] getAvailableServerHostKeyAlgorithms()
- {
- return KexManager.getDefaultServerHostkeyAlgorithmList();
- }
-
- private AuthenticationManager am;
-
- private boolean authenticated = false;
- private boolean compression = false;
- private ChannelManager cm;
-
- private CryptoWishList cryptoWishList = new CryptoWishList();
-
- private DHGexParameters dhgexpara = new DHGexParameters();
-
- private final String hostname;
-
- private final int port;
-
- private TransportManager tm;
-
- private boolean tcpNoDelay = false;
-
- private ProxyData proxyData = null;
-
- private Vector<ConnectionMonitor> connectionMonitors = new Vector<ConnectionMonitor>();
-
- /**
- * Prepares a fresh <code>Connection</code> object which can then be used
- * to establish a connection to the specified SSH-2 server.
- * <p>
- * Same as {@link #Connection(String, int) Connection(hostname, 22)}.
- *
- * @param hostname
- * the hostname of the SSH-2 server.
- */
- public Connection(String hostname)
- {
- this(hostname, 22);
- }
-
- /**
- * Prepares a fresh <code>Connection</code> object which can then be used
- * to establish a connection to the specified SSH-2 server.
- *
- * @param hostname
- * the host where we later want to connect to.
- * @param port
- * port on the server, normally 22.
- */
- public Connection(String hostname, int port)
- {
- this.hostname = hostname;
- this.port = port;
- }
-
- /**
- * After a successful connect, one has to authenticate oneself. This method
- * is based on DSA (it uses DSA to sign a challenge sent by the server).
- * <p>
- * If the authentication phase is complete, <code>true</code> will be
- * returned. If the server does not accept the request (or if further
- * authentication steps are needed), <code>false</code> is returned and
- * one can retry either by using this or any other authentication method
- * (use the <code>getRemainingAuthMethods</code> method to get a list of
- * the remaining possible methods).
- *
- * @param user
- * A <code>String</code> holding the username.
- * @param pem
- * A <code>String</code> containing the DSA private key of the
- * user in OpenSSH key format (PEM, you can't miss the
- * "-----BEGIN DSA PRIVATE KEY-----" tag). The string may contain
- * linefeeds.
- * @param password
- * If the PEM string is 3DES encrypted ("DES-EDE3-CBC"), then you
- * must specify the password. Otherwise, this argument will be
- * ignored and can be set to <code>null</code>.
- *
- * @return whether the connection is now authenticated.
- * @throws IOException
- *
- * @deprecated You should use one of the
- * {@link #authenticateWithPublicKey(String, File, String) authenticateWithPublicKey()}
- * methods, this method is just a wrapper for it and will
- * disappear in future builds.
- *
- */
- public synchronized boolean authenticateWithDSA(String user, String pem, String password) throws IOException
- {
- if (tm == null)
- throw new IllegalStateException("Connection is not established!");
-
- if (authenticated)
- throw new IllegalStateException("Connection is already authenticated!");
-
- if (am == null)
- am = new AuthenticationManager(tm);
-
- if (cm == null)
- cm = new ChannelManager(tm);
-
- if (user == null)
- throw new IllegalArgumentException("user argument is null");
-
- if (pem == null)
- throw new IllegalArgumentException("pem argument is null");
-
- authenticated = am.authenticatePublicKey(user, pem.toCharArray(), password, getOrCreateSecureRND());
-
- return authenticated;
- }
-
- /**
- * A wrapper that calls
- * {@link #authenticateWithKeyboardInteractive(String, String[], InteractiveCallback)
- * authenticateWithKeyboardInteractivewith} a <code>null</code> submethod
- * list.
- *
- * @param user
- * A <code>String</code> holding the username.
- * @param cb
- * An <code>InteractiveCallback</code> which will be used to
- * determine the responses to the questions asked by the server.
- * @return whether the connection is now authenticated.
- * @throws IOException
- */
- public synchronized boolean authenticateWithKeyboardInteractive(String user, InteractiveCallback cb)
- throws IOException
- {
- return authenticateWithKeyboardInteractive(user, null, cb);
- }
-
- /**
- * After a successful connect, one has to authenticate oneself. This method
- * is based on "keyboard-interactive", specified in
- * draft-ietf-secsh-auth-kbdinteract-XX. Basically, you have to define a
- * callback object which will be feeded with challenges generated by the
- * server. Answers are then sent back to the server. It is possible that the
- * callback will be called several times during the invocation of this
- * method (e.g., if the server replies to the callback's answer(s) with
- * another challenge...)
- * <p>
- * If the authentication phase is complete, <code>true</code> will be
- * returned. If the server does not accept the request (or if further
- * authentication steps are needed), <code>false</code> is returned and
- * one can retry either by using this or any other authentication method
- * (use the <code>getRemainingAuthMethods</code> method to get a list of
- * the remaining possible methods).
- * <p>
- * Note: some SSH servers advertise "keyboard-interactive", however, any
- * interactive request will be denied (without having sent any challenge to
- * the client).
- *
- * @param user
- * A <code>String</code> holding the username.
- * @param submethods
- * An array of submethod names, see
- * draft-ietf-secsh-auth-kbdinteract-XX. May be <code>null</code>
- * to indicate an empty list.
- * @param cb
- * An <code>InteractiveCallback</code> which will be used to
- * determine the responses to the questions asked by the server.
- *
- * @return whether the connection is now authenticated.
- * @throws IOException
- */
- public synchronized boolean authenticateWithKeyboardInteractive(String user, String[] submethods,
- InteractiveCallback cb) throws IOException
- {
- if (cb == null)
- throw new IllegalArgumentException("Callback may not ne NULL!");
-
- if (tm == null)
- throw new IllegalStateException("Connection is not established!");
-
- if (authenticated)
- throw new IllegalStateException("Connection is already authenticated!");
-
- if (am == null)
- am = new AuthenticationManager(tm);
-
- if (cm == null)
- cm = new ChannelManager(tm);
-
- if (user == null)
- throw new IllegalArgumentException("user argument is null");
-
- authenticated = am.authenticateInteractive(user, submethods, cb);
-
- return authenticated;
- }
-
- /**
- * After a successful connect, one has to authenticate oneself. This method
- * sends username and password to the server.
- * <p>
- * If the authentication phase is complete, <code>true</code> will be
- * returned. If the server does not accept the request (or if further
- * authentication steps are needed), <code>false</code> is returned and
- * one can retry either by using this or any other authentication method
- * (use the <code>getRemainingAuthMethods</code> method to get a list of
- * the remaining possible methods).
- * <p>
- * Note: if this method fails, then please double-check that it is actually
- * offered by the server (use
- * {@link #getRemainingAuthMethods(String) getRemainingAuthMethods()}.
- * <p>
- * Often, password authentication is disabled, but users are not aware of
- * it. Many servers only offer "publickey" and "keyboard-interactive".
- * However, even though "keyboard-interactive" *feels* like password
- * authentication (e.g., when using the putty or openssh clients) it is
- * *not* the same mechanism.
- *
- * @param user
- * @param password
- * @return if the connection is now authenticated.
- * @throws IOException
- */
- public synchronized boolean authenticateWithPassword(String user, String password) throws IOException
- {
- if (tm == null)
- throw new IllegalStateException("Connection is not established!");
-
- if (authenticated)
- throw new IllegalStateException("Connection is already authenticated!");
-
- if (am == null)
- am = new AuthenticationManager(tm);
-
- if (cm == null)
- cm = new ChannelManager(tm);
-
- if (user == null)
- throw new IllegalArgumentException("user argument is null");
-
- if (password == null)
- throw new IllegalArgumentException("password argument is null");
-
- authenticated = am.authenticatePassword(user, password);
-
- return authenticated;
- }
-
- /**
- * After a successful connect, one has to authenticate oneself. This method
- * can be used to explicitly use the special "none" authentication method
- * (where only a username has to be specified).
- * <p>
- * Note 1: The "none" method may always be tried by clients, however as by
- * the specs, the server will not explicitly announce it. In other words,
- * the "none" token will never show up in the list returned by
- * {@link #getRemainingAuthMethods(String)}.
- * <p>
- * Note 2: no matter which one of the authenticateWithXXX() methods you
- * call, the library will always issue exactly one initial "none"
- * authentication request to retrieve the initially allowed list of
- * authentication methods by the server. Please read RFC 4252 for the
- * details.
- * <p>
- * If the authentication phase is complete, <code>true</code> will be
- * returned. If further authentication steps are needed, <code>false</code>
- * is returned and one can retry by any other authentication method (use the
- * <code>getRemainingAuthMethods</code> method to get a list of the
- * remaining possible methods).
- *
- * @param user
- * @return if the connection is now authenticated.
- * @throws IOException
- */
- public synchronized boolean authenticateWithNone(String user) throws IOException
- {
- if (tm == null)
- throw new IllegalStateException("Connection is not established!");
-
- if (authenticated)
- throw new IllegalStateException("Connection is already authenticated!");
-
- if (am == null)
- am = new AuthenticationManager(tm);
-
- if (cm == null)
- cm = new ChannelManager(tm);
-
- if (user == null)
- throw new IllegalArgumentException("user argument is null");
-
- /* Trigger the sending of the PacketUserauthRequestNone packet */
- /* (if not already done) */
-
- authenticated = am.authenticateNone(user);
-
- return authenticated;
- }
-
- /**
- * After a successful connect, one has to authenticate oneself. The
- * authentication method "publickey" works by signing a challenge sent by
- * the server. The signature is either DSA or RSA based - it just depends on
- * the type of private key you specify, either a DSA or RSA private key in
- * PEM format. And yes, this is may seem to be a little confusing, the
- * method is called "publickey" in the SSH-2 protocol specification, however
- * since we need to generate a signature, you actually have to supply a
- * private key =).
- * <p>
- * The private key contained in the PEM file may also be encrypted
- * ("Proc-Type: 4,ENCRYPTED"). The library supports DES-CBC and DES-EDE3-CBC
- * encryption, as well as the more exotic PEM encrpytions AES-128-CBC,
- * AES-192-CBC and AES-256-CBC.
- * <p>
- * If the authentication phase is complete, <code>true</code> will be
- * returned. If the server does not accept the request (or if further
- * authentication steps are needed), <code>false</code> is returned and
- * one can retry either by using this or any other authentication method
- * (use the <code>getRemainingAuthMethods</code> method to get a list of
- * the remaining possible methods).
- * <p>
- * NOTE PUTTY USERS: Event though your key file may start with
- * "-----BEGIN..." it is not in the expected format. You have to convert it
- * to the OpenSSH key format by using the "puttygen" tool (can be downloaded
- * from the Putty website). Simply load your key and then use the
- * "Conversions/Export OpenSSH key" functionality to get a proper PEM file.
- *
- * @param user
- * A <code>String</code> holding the username.
- * @param pemPrivateKey
- * A <code>char[]</code> containing a DSA or RSA private key of
- * the user in OpenSSH key format (PEM, you can't miss the
- * "-----BEGIN DSA PRIVATE KEY-----" or "-----BEGIN RSA PRIVATE
- * KEY-----" tag). The char array may contain
- * linebreaks/linefeeds.
- * @param password
- * If the PEM structure is encrypted ("Proc-Type: 4,ENCRYPTED")
- * then you must specify a password. Otherwise, this argument
- * will be ignored and can be set to <code>null</code>.
- *
- * @return whether the connection is now authenticated.
- * @throws IOException
- */
- public synchronized boolean authenticateWithPublicKey(String user, char[] pemPrivateKey, String password)
- throws IOException
- {
- if (tm == null)
- throw new IllegalStateException("Connection is not established!");
-
- if (authenticated)
- throw new IllegalStateException("Connection is already authenticated!");
-
- if (am == null)
- am = new AuthenticationManager(tm);
-
- if (cm == null)
- cm = new ChannelManager(tm);
-
- if (user == null)
- throw new IllegalArgumentException("user argument is null");
-
- if (pemPrivateKey == null)
- throw new IllegalArgumentException("pemPrivateKey argument is null");
-
- authenticated = am.authenticatePublicKey(user, pemPrivateKey, password, getOrCreateSecureRND());
-
- return authenticated;
- }
-
- /**
- * After a successful connect, one has to authenticate oneself. The
- * authentication method "publickey" works by signing a challenge sent by
- * the server. The signature is either DSA or RSA based - it just depends on
- * the type of private key you specify, either a DSA or RSA private key in
- * PEM format. And yes, this is may seem to be a little confusing, the
- * method is called "publickey" in the SSH-2 protocol specification, however
- * since we need to generate a signature, you actually have to supply a
- * private key =).
- * <p>
- * If the authentication phase is complete, <code>true</code> will be
- * returned. If the server does not accept the request (or if further
- * authentication steps are needed), <code>false</code> is returned and
- * one can retry either by using this or any other authentication method
- * (use the <code>getRemainingAuthMethods</code> method to get a list of
- * the remaining possible methods).
- *
- * @param user
- * A <code>String</code> holding the username.
- * @param key
- * A <code>RSAPrivateKey</code> or <code>DSAPrivateKey</code>
- * containing a DSA or RSA private key of
- * the user in Trilead object format.
- *
- * @return whether the connection is now authenticated.
- * @throws IOException
- */
- public synchronized boolean authenticateWithPublicKey(String user, Object key)
- throws IOException
- {
- if (tm == null)
- throw new IllegalStateException("Connection is not established!");
-
- if (authenticated)
- throw new IllegalStateException("Connection is already authenticated!");
-
- if (am == null)
- am = new AuthenticationManager(tm);
-
- if (cm == null)
- cm = new ChannelManager(tm);
-
- if (user == null)
- throw new IllegalArgumentException("user argument is null");
-
- if (key == null)
- throw new IllegalArgumentException("Key argument is null");
-
- authenticated = am.authenticatePublicKey(user, key, getOrCreateSecureRND());
-
- return authenticated;
- }
- /**
- * A convenience wrapper function which reads in a private key (PEM format,
- * either DSA or RSA) and then calls
- * <code>authenticateWithPublicKey(String, char[], String)</code>.
- * <p>
- * NOTE PUTTY USERS: Event though your key file may start with
- * "-----BEGIN..." it is not in the expected format. You have to convert it
- * to the OpenSSH key format by using the "puttygen" tool (can be downloaded
- * from the Putty website). Simply load your key and then use the
- * "Conversions/Export OpenSSH key" functionality to get a proper PEM file.
- *
- * @param user
- * A <code>String</code> holding the username.
- * @param pemFile
- * A <code>File</code> object pointing to a file containing a
- * DSA or RSA private key of the user in OpenSSH key format (PEM,
- * you can't miss the "-----BEGIN DSA PRIVATE KEY-----" or
- * "-----BEGIN RSA PRIVATE KEY-----" tag).
- * @param password
- * If the PEM file is encrypted then you must specify the
- * password. Otherwise, this argument will be ignored and can be
- * set to <code>null</code>.
- *
- * @return whether the connection is now authenticated.
- * @throws IOException
- */
- public synchronized boolean authenticateWithPublicKey(String user, File pemFile, String password)
- throws IOException
- {
- if (pemFile == null)
- throw new IllegalArgumentException("pemFile argument is null");
-
- char[] buff = new char[256];
-
- CharArrayWriter cw = new CharArrayWriter();
-
- FileReader fr = new FileReader(pemFile);
-
- while (true)
- {
- int len = fr.read(buff);
- if (len < 0)
- break;
- cw.write(buff, 0, len);
- }
-
- fr.close();
-
- return authenticateWithPublicKey(user, cw.toCharArray(), password);
- }
-
- /**
- * Add a {@link ConnectionMonitor} to this connection. Can be invoked at any
- * time, but it is best to add connection monitors before invoking
- * <code>connect()</code> to avoid glitches (e.g., you add a connection
- * monitor after a successful connect(), but the connection has died in the
- * mean time. Then, your connection monitor won't be notified.)
- * <p>
- * You can add as many monitors as you like.
- *
- * @see ConnectionMonitor
- *
- * @param cmon
- * An object implementing the <code>ConnectionMonitor</code>
- * interface.
- */
- public synchronized void addConnectionMonitor(ConnectionMonitor cmon)
- {
- if (cmon == null)
- throw new IllegalArgumentException("cmon argument is null");
-
- connectionMonitors.addElement(cmon);
-
- if (tm != null)
- tm.setConnectionMonitors(connectionMonitors);
- }
-
- /**
- * Controls whether compression is used on the link or not.
- * <p>
- * Note: This can only be called before connect()
- * @param enabled whether to enable compression
- * @throws IOException
- */
- public synchronized void setCompression(boolean enabled) throws IOException {
- if (tm != null)
- throw new IOException("Connection to " + hostname + " is already in connected state!");
-
- compression = enabled;
- }
-
- /**
- * Close the connection to the SSH-2 server. All assigned sessions will be
- * closed, too. Can be called at any time. Don't forget to call this once
- * you don't need a connection anymore - otherwise the receiver thread may
- * run forever.
- */
- public synchronized void close()
- {
- Throwable t = new Throwable("Closed due to user request.");
- close(t, false);
- }
-
- private void close(Throwable t, boolean hard)
- {
- if (cm != null)
- cm.closeAllChannels();
-
- if (tm != null)
- {
- tm.close(t, hard == false);
- tm = null;
- }
- am = null;
- cm = null;
- authenticated = false;
- }
-
- /**
- * Same as
- * {@link #connect(ServerHostKeyVerifier, int, int) connect(null, 0, 0)}.
- *
- * @return see comments for the
- * {@link #connect(ServerHostKeyVerifier, int, int) connect(ServerHostKeyVerifier, int, int)}
- * method.
- * @throws IOException
- */
- public synchronized ConnectionInfo connect() throws IOException
- {
- return connect(null, 0, 0);
- }
-
- /**
- * Same as
- * {@link #connect(ServerHostKeyVerifier, int, int) connect(verifier, 0, 0)}.
- *
- * @return see comments for the
- * {@link #connect(ServerHostKeyVerifier, int, int) connect(ServerHostKeyVerifier, int, int)}
- * method.
- * @throws IOException
- */
- public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier) throws IOException
- {
- return connect(verifier, 0, 0);
- }
-
- /**
- * Connect to the SSH-2 server and, as soon as the server has presented its
- * host key, use the
- * {@link ServerHostKeyVerifier#verifyServerHostKey(String, int, String,
- * byte[]) ServerHostKeyVerifier.verifyServerHostKey()} method of the
- * <code>verifier</code> to ask for permission to proceed. If
- * <code>verifier</code> is <code>null</code>, then any host key will
- * be accepted - this is NOT recommended, since it makes man-in-the-middle
- * attackes VERY easy (somebody could put a proxy SSH server between you and
- * the real server).
- * <p>
- * Note: The verifier will be called before doing any crypto calculations
- * (i.e., diffie-hellman). Therefore, if you don't like the presented host
- * key then no CPU cycles are wasted (and the evil server has less
- * information about us).
- * <p>
- * However, it is still possible that the server presented a fake host key:
- * the server cheated (typically a sign for a man-in-the-middle attack) and
- * is not able to generate a signature that matches its host key. Don't
- * worry, the library will detect such a scenario later when checking the
- * signature (the signature cannot be checked before having completed the
- * diffie-hellman exchange).
- * <p>
- * Note 2: The {@link ServerHostKeyVerifier#verifyServerHostKey(String, int,
- * String, byte[]) ServerHostKeyVerifier.verifyServerHostKey()} method will
- * *NOT* be called from the current thread, the call is being made from a
- * background thread (there is a background dispatcher thread for every
- * established connection).
- * <p>
- * Note 3: This method will block as long as the key exchange of the
- * underlying connection has not been completed (and you have not specified
- * any timeouts).
- * <p>
- * Note 4: If you want to re-use a connection object that was successfully
- * connected, then you must call the {@link #close()} method before invoking
- * <code>connect()</code> again.
- *
- * @param verifier
- * An object that implements the {@link ServerHostKeyVerifier}
- * interface. Pass <code>null</code> to accept any server host
- * key - NOT recommended.
- *
- * @param connectTimeout
- * Connect the underlying TCP socket to the server with the given
- * timeout value (non-negative, in milliseconds). Zero means no
- * timeout. If a proxy is being used (see
- * {@link #setProxyData(ProxyData)}), then this timeout is used
- * for the connection establishment to the proxy.
- *
- * @param kexTimeout
- * Timeout for complete connection establishment (non-negative,
- * in milliseconds). Zero means no timeout. The timeout counts
- * from the moment you invoke the connect() method and is
- * cancelled as soon as the first key-exchange round has
- * finished. It is possible that the timeout event will be fired
- * during the invocation of the <code>verifier</code> callback,
- * but it will only have an effect after the
- * <code>verifier</code> returns.
- *
- * @return A {@link ConnectionInfo} object containing the details of the
- * established connection.
- *
- * @throws IOException
- * If any problem occurs, e.g., the server's host key is not
- * accepted by the <code>verifier</code> or there is problem
- * during the initial crypto setup (e.g., the signature sent by
- * the server is wrong).
- * <p>
- * In case of a timeout (either connectTimeout or kexTimeout) a
- * SocketTimeoutException is thrown.
- * <p>
- * An exception may also be thrown if the connection was already
- * successfully connected (no matter if the connection broke in
- * the mean time) and you invoke <code>connect()</code> again
- * without having called {@link #close()} first.
- * <p>
- * If a HTTP proxy is being used and the proxy refuses the
- * connection, then a {@link HTTPProxyException} may be thrown,
- * which contains the details returned by the proxy. If the
- * proxy is buggy and does not return a proper HTTP response,
- * then a normal IOException is thrown instead.
- */
- public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier, int connectTimeout, int kexTimeout)
- throws IOException
- {
- final class TimeoutState
- {
- boolean isCancelled = false;
- boolean timeoutSocketClosed = false;
- }
-
- if (tm != null)
- throw new IOException("Connection to " + hostname + " is already in connected state!");
-
- if (connectTimeout < 0)
- throw new IllegalArgumentException("connectTimeout must be non-negative!");
-
- if (kexTimeout < 0)
- throw new IllegalArgumentException("kexTimeout must be non-negative!");
-
- final TimeoutState state = new TimeoutState();
-
- tm = new TransportManager(hostname, port);
-
- tm.setConnectionMonitors(connectionMonitors);
-
- // Don't offer compression if not requested
- if (!compression) {
- cryptoWishList.c2s_comp_algos = new String[] { "none" };
- cryptoWishList.s2c_comp_algos = new String[] { "none" };
- }
-
- /*
- * Make sure that the runnable below will observe the new value of "tm"
- * and "state" (the runnable will be executed in a different thread,
- * which may be already running, that is why we need a memory barrier
- * here). See also the comment in Channel.java if you are interested in
- * the details.
- *
- * OKOK, this is paranoid since adding the runnable to the todo list of
- * the TimeoutService will ensure that all writes have been flushed
- * before the Runnable reads anything (there is a synchronized block in
- * TimeoutService.addTimeoutHandler).
- */
-
- synchronized (tm)
- {
- /* We could actually synchronize on anything. */
- }
-
- try
- {
- TimeoutToken token = null;
-
- if (kexTimeout > 0)
- {
- final Runnable timeoutHandler = new Runnable()
- {
- public void run()
- {
- synchronized (state)
- {
- if (state.isCancelled)
- return;
- state.timeoutSocketClosed = true;
- tm.close(new SocketTimeoutException("The connect timeout expired"), false);
- }
- }
- };
-
- long timeoutHorizont = System.currentTimeMillis() + kexTimeout;
-
- token = TimeoutService.addTimeoutHandler(timeoutHorizont, timeoutHandler);
- }
-
- try
- {
- tm.initialize(cryptoWishList, verifier, dhgexpara, connectTimeout, getOrCreateSecureRND(), proxyData);
- }
- catch (SocketTimeoutException se)
- {
- throw (SocketTimeoutException) new SocketTimeoutException(
- "The connect() operation on the socket timed out.").initCause(se);
- }
-
- tm.setTcpNoDelay(tcpNoDelay);
-
- /* Wait until first KEX has finished */
-
- ConnectionInfo ci = tm.getConnectionInfo(1);
-
- /* Now try to cancel the timeout, if needed */
-
- if (token != null)
- {
- TimeoutService.cancelTimeoutHandler(token);
-
- /* Were we too late? */
-
- synchronized (state)
- {
- if (state.timeoutSocketClosed)
- throw new IOException("This exception will be replaced by the one below =)");
- /*
- * Just in case the "cancelTimeoutHandler" invocation came
- * just a little bit too late but the handler did not enter
- * the semaphore yet - we can still stop it.
- */
- state.isCancelled = true;
- }
- }
-
- return ci;
- }
- catch (SocketTimeoutException ste)
- {
- throw ste;
- }
- catch (IOException e1)
- {
- /* This will also invoke any registered connection monitors */
- close(new Throwable("There was a problem during connect."), false);
-
- synchronized (state)
- {
- /*
- * Show a clean exception, not something like "the socket is
- * closed!?!"
- */
- if (state.timeoutSocketClosed)
- throw new SocketTimeoutException("The kexTimeout (" + kexTimeout + " ms) expired.");
- }
-
- /* Do not wrap a HTTPProxyException */
- if (e1 instanceof HTTPProxyException)
- throw e1;
-
- throw (IOException) new IOException("There was a problem while connecting to " + hostname + ":" + port)
- .initCause(e1);
- }
- }
-
- /**
- * Creates a new {@link LocalPortForwarder}. A
- * <code>LocalPortForwarder</code> forwards TCP/IP connections that arrive
- * at a local port via the secure tunnel to another host (which may or may
- * not be identical to the remote SSH-2 server).
- * <p>
- * This method must only be called after one has passed successfully the
- * authentication step. There is no limit on the number of concurrent
- * forwardings.
- *
- * @param local_port
- * the local port the LocalPortForwarder shall bind to.
- * @param host_to_connect
- * target address (IP or hostname)
- * @param port_to_connect
- * target port
- * @return A {@link LocalPortForwarder} object.
- * @throws IOException
- */
- public synchronized LocalPortForwarder createLocalPortForwarder(int local_port, String host_to_connect,
- int port_to_connect) throws IOException
- {
- if (tm == null)
- throw new IllegalStateException("Cannot forward ports, you need to establish a connection first.");
-
- if (!authenticated)
- throw new IllegalStateException("Cannot forward ports, connection is not authenticated.");
-
- return new LocalPortForwarder(cm, local_port, host_to_connect, port_to_connect);
- }
-
- /**
- * Creates a new {@link LocalPortForwarder}. A
- * <code>LocalPortForwarder</code> forwards TCP/IP connections that arrive
- * at a local port via the secure tunnel to another host (which may or may
- * not be identical to the remote SSH-2 server).
- * <p>
- * This method must only be called after one has passed successfully the
- * authentication step. There is no limit on the number of concurrent
- * forwardings.
- *
- * @param addr
- * specifies the InetSocketAddress where the local socket shall
- * be bound to.
- * @param host_to_connect
- * target address (IP or hostname)
- * @param port_to_connect
- * target port
- * @return A {@link LocalPortForwarder} object.
- * @throws IOException
- */
- public synchronized LocalPortForwarder createLocalPortForwarder(InetSocketAddress addr, String host_to_connect,
- int port_to_connect) throws IOException
- {
- if (tm == null)
- throw new IllegalStateException("Cannot forward ports, you need to establish a connection first.");
-
- if (!authenticated)
- throw new IllegalStateException("Cannot forward ports, connection is not authenticated.");
-
- return new LocalPortForwarder(cm, addr, host_to_connect, port_to_connect);
- }
-
- /**
- * Creates a new {@link LocalStreamForwarder}. A
- * <code>LocalStreamForwarder</code> manages an Input/Outputstream pair
- * that is being forwarded via the secure tunnel into a TCP/IP connection to
- * another host (which may or may not be identical to the remote SSH-2
- * server).
- *
- * @param host_to_connect
- * @param port_to_connect
- * @return A {@link LocalStreamForwarder} object.
- * @throws IOException
- */
- public synchronized LocalStreamForwarder createLocalStreamForwarder(String host_to_connect, int port_to_connect)
- throws IOException
- {
- if (tm == null)
- throw new IllegalStateException("Cannot forward, you need to establish a connection first.");
-
- if (!authenticated)
- throw new IllegalStateException("Cannot forward, connection is not authenticated.");
-
- return new LocalStreamForwarder(cm, host_to_connect, port_to_connect);
- }
-
- /**
- * Creates a new {@link DynamicPortForwarder}. A
- * <code>DynamicPortForwarder</code> forwards TCP/IP connections that arrive
- * at a local port via the secure tunnel to another host that is chosen via
- * the SOCKS protocol.
- * <p>
- * This method must only be called after one has passed successfully the
- * authentication step. There is no limit on the number of concurrent
- * forwardings.
- *
- * @param local_port
- * @return A {@link DynamicPortForwarder} object.
- * @throws IOException
- */
- public synchronized DynamicPortForwarder createDynamicPortForwarder(int local_port) throws IOException
- {
- if (tm == null)
- throw new IllegalStateException("Cannot forward ports, you need to establish a connection first.");
-
- if (!authenticated)
- throw new IllegalStateException("Cannot forward ports, connection is not authenticated.");
-
- return new DynamicPortForwarder(cm, local_port);
- }
-
- /**
- * Creates a new {@link DynamicPortForwarder}. A
- * <code>DynamicPortForwarder</code> forwards TCP/IP connections that arrive
- * at a local port via the secure tunnel to another host that is chosen via
- * the SOCKS protocol.
- * <p>
- * This method must only be called after one has passed successfully the
- * authentication step. There is no limit on the number of concurrent
- * forwardings.
- *
- * @param addr
- * specifies the InetSocketAddress where the local socket shall
- * be bound to.
- * @return A {@link DynamicPortForwarder} object.
- * @throws IOException
- */
- public synchronized DynamicPortForwarder createDynamicPortForwarder(InetSocketAddress addr) throws IOException
- {
- if (tm == null)
- throw new IllegalStateException("Cannot forward ports, you need to establish a connection first.");
-
- if (!authenticated)
- throw new IllegalStateException("Cannot forward ports, connection is not authenticated.");
-
- return new DynamicPortForwarder(cm, addr);
- }
-
- /**
- * Create a very basic {@link SCPClient} that can be used to copy files
- * from/to the SSH-2 server.
- * <p>
- * Works only after one has passed successfully the authentication step.
- * There is no limit on the number of concurrent SCP clients.
- * <p>
- * Note: This factory method will probably disappear in the future.
- *
- * @return A {@link SCPClient} object.
- * @throws IOException
- */
- public synchronized SCPClient createSCPClient() throws IOException
- {
- if (tm == null)
- throw new IllegalStateException("Cannot create SCP client, you need to establish a connection first.");
-
- if (!authenticated)
- throw new IllegalStateException("Cannot create SCP client, connection is not authenticated.");
-
- return new SCPClient(this);
- }
-
- /**
- * Force an asynchronous key re-exchange (the call does not block). The
- * latest values set for MAC, Cipher and DH group exchange parameters will
- * be used. If a key exchange is currently in progress, then this method has
- * the only effect that the so far specified parameters will be used for the
- * next (server driven) key exchange.
- * <p>
- * Note: This implementation will never start a key exchange (other than the
- * initial one) unless you or the SSH-2 server ask for it.
- *
- * @throws IOException
- * In case of any failure behind the scenes.
- */
- public synchronized void forceKeyExchange() throws IOException
- {
- if (tm == null)
- throw new IllegalStateException("You need to establish a connection first.");
-
- tm.forceKeyExchange(cryptoWishList, dhgexpara);
- }
-
- /**
- * Returns the hostname that was passed to the constructor.
- *
- * @return the hostname
- */
- public synchronized String getHostname()
- {
- return hostname;
- }
-
- /**
- * Returns the port that was passed to the constructor.
- *
- * @return the TCP port
- */
- public synchronized int getPort()
- {
- return port;
- }
-
- /**
- * Returns a {@link ConnectionInfo} object containing the details of the
- * connection. Can be called as soon as the connection has been established
- * (successfully connected).
- *
- * @return A {@link ConnectionInfo} object.
- * @throws IOException
- * In case of any failure behind the scenes.
- */
- public synchronized ConnectionInfo getConnectionInfo() throws IOException
- {
- if (tm == null)
- throw new IllegalStateException(
- "Cannot get details of connection, you need to establish a connection first.");
- return tm.getConnectionInfo(1);
- }
-
- /**
- * After a successful connect, one has to authenticate oneself. This method
- * can be used to tell which authentication methods are supported by the
- * server at a certain stage of the authentication process (for the given
- * username).
- * <p>
- * Note 1: the username will only be used if no authentication step was done
- * so far (it will be used to ask the server for a list of possible
- * authentication methods by sending the initial "none" request). Otherwise,
- * this method ignores the user name and returns a cached method list (which
- * is based on the information contained in the last negative server
- * response).
- * <p>
- * Note 2: the server may return method names that are not supported by this
- * implementation.
- * <p>
- * After a successful authentication, this method must not be called
- * anymore.
- *
- * @param user
- * A <code>String</code> holding the username.
- *
- * @return a (possibly emtpy) array holding authentication method names.
- * @throws IOException
- */
- public synchronized String[] getRemainingAuthMethods(String user) throws IOException
- {
- if (user == null)
- throw new IllegalArgumentException("user argument may not be NULL!");
-
- if (tm == null)
- throw new IllegalStateException("Connection is not established!");
-
- if (authenticated)
- throw new IllegalStateException("Connection is already authenticated!");
-
- if (am == null)
- am = new AuthenticationManager(tm);
-
- if (cm == null)
- cm = new ChannelManager(tm);
-
- return am.getRemainingMethods(user);
- }
-
- /**
- * Determines if the authentication phase is complete. Can be called at any
- * time.
- *
- * @return <code>true</code> if no further authentication steps are
- * needed.
- */
- public synchronized boolean isAuthenticationComplete()
- {
- return authenticated;
- }
-
- /**
- * Returns true if there was at least one failed authentication request and
- * the last failed authentication request was marked with "partial success"
- * by the server. This is only needed in the rare case of SSH-2 server
- * setups that cannot be satisfied with a single successful authentication
- * request (i.e., multiple authentication steps are needed.)
- * <p>
- * If you are interested in the details, then have a look at RFC4252.
- *
- * @return if the there was a failed authentication step and the last one
- * was marked as a "partial success".
- */
- public synchronized boolean isAuthenticationPartialSuccess()
- {
- if (am == null)
- return false;
-
- return am.getPartialSuccess();
- }
-
- /**
- * Checks if a specified authentication method is available. This method is
- * actually just a wrapper for {@link #getRemainingAuthMethods(String)
- * getRemainingAuthMethods()}.
- *
- * @param user
- * A <code>String</code> holding the username.
- * @param method
- * An authentication method name (e.g., "publickey", "password",
- * "keyboard-interactive") as specified by the SSH-2 standard.
- * @return if the specified authentication method is currently available.
- * @throws IOException
- */
- public synchronized boolean isAuthMethodAvailable(String user, String method) throws IOException
- {
- if (method == null)
- throw new IllegalArgumentException("method argument may not be NULL!");
-
- String methods[] = getRemainingAuthMethods(user);
-
- for (int i = 0; i < methods.length; i++)
- {
- if (methods[i].compareTo(method) == 0)
- return true;
- }
-
- return false;
- }
-
- private final SecureRandom getOrCreateSecureRND()
- {
- if (generator == null)
- generator = new SecureRandom();
-
- return generator;
- }
-
- /**
- * Open a new {@link Session} on this connection. Works only after one has
- * passed successfully the authentication step. There is no limit on the
- * number of concurrent sessions.
- *
- * @return A {@link Session} object.
- * @throws IOException
- */
- public synchronized Session openSession() throws IOException
- {
- if (tm == null)
- throw new IllegalStateException("Cannot open session, you need to establish a connection first.");
-
- if (!authenticated)
- throw new IllegalStateException("Cannot open session, connection is not authenticated.");
-
- return new Session(cm, getOrCreateSecureRND());
- }
-
- /**
- * Send an SSH_MSG_IGNORE packet. This method will generate a random data
- * attribute (length between 0 (invlusive) and 16 (exclusive) bytes,
- * contents are random bytes).
- * <p>
- * This method must only be called once the connection is established.
- *
- * @throws IOException
- */
- public synchronized void sendIgnorePacket() throws IOException
- {
- SecureRandom rnd = getOrCreateSecureRND();
-
- byte[] data = new byte[rnd.nextInt(16)];
- rnd.nextBytes(data);
-
- sendIgnorePacket(data);
- }
-
- /**
- * Send an SSH_MSG_IGNORE packet with the given data attribute.
- * <p>
- * This method must only be called once the connection is established.
- *
- * @throws IOException
- */
- public synchronized void sendIgnorePacket(byte[] data) throws IOException
- {
- if (data == null)
- throw new IllegalArgumentException("data argument must not be null.");
-
- if (tm == null)
- throw new IllegalStateException(
- "Cannot send SSH_MSG_IGNORE packet, you need to establish a connection first.");
-
- PacketIgnore pi = new PacketIgnore();
- pi.setData(data);
-
- tm.sendMessage(pi.getPayload());
- }
-
- /**
- * Removes duplicates from a String array, keeps only first occurence of
- * each element. Does not destroy order of elements; can handle nulls. Uses
- * a very efficient O(N^2) algorithm =)
- *
- * @param list
- * a String array.
- * @return a cleaned String array.
- */
- private String[] removeDuplicates(String[] list)
- {
- if ((list == null) || (list.length < 2))
- return list;
-
- String[] list2 = new String[list.length];
-
- int count = 0;
-
- for (int i = 0; i < list.length; i++)
- {
- boolean duplicate = false;
-
- String element = list[i];
-
- for (int j = 0; j < count; j++)
- {
- if (((element == null) && (list2[j] == null)) || ((element != null) && (element.equals(list2[j]))))
- {
- duplicate = true;
- break;
- }
- }
-
- if (duplicate)
- continue;
-
- list2[count++] = list[i];
- }
-
- if (count == list2.length)
- return list2;
-
- String[] tmp = new String[count];
- System.arraycopy(list2, 0, tmp, 0, count);
-
- return tmp;
- }
-
- /**
- * Unless you know what you are doing, you will never need this.
- *
- * @param ciphers
- */
- public synchronized void setClient2ServerCiphers(String[] ciphers)
- {
- if ((ciphers == null) || (ciphers.length == 0))
- throw new IllegalArgumentException();
- ciphers = removeDuplicates(ciphers);
- BlockCipherFactory.checkCipherList(ciphers);
- cryptoWishList.c2s_enc_algos = ciphers;
- }
-
- /**
- * Unless you know what you are doing, you will never need this.
- *
- * @param macs
- */
- public synchronized void setClient2ServerMACs(String[] macs)
- {
- if ((macs == null) || (macs.length == 0))
- throw new IllegalArgumentException();
- macs = removeDuplicates(macs);
- MAC.checkMacList(macs);
- cryptoWishList.c2s_mac_algos = macs;
- }
-
- /**
- * Sets the parameters for the diffie-hellman group exchange. Unless you
- * know what you are doing, you will never need this. Default values are
- * defined in the {@link DHGexParameters} class.
- *
- * @param dgp
- * {@link DHGexParameters}, non null.
- *
- */
- public synchronized void setDHGexParameters(DHGexParameters dgp)
- {
- if (dgp == null)
- throw new IllegalArgumentException();
-
- dhgexpara = dgp;
- }
-
- /**
- * Unless you know what you are doing, you will never need this.
- *
- * @param ciphers
- */
- public synchronized void setServer2ClientCiphers(String[] ciphers)
- {
- if ((ciphers == null) || (ciphers.length == 0))
- throw new IllegalArgumentException();
- ciphers = removeDuplicates(ciphers);
- BlockCipherFactory.checkCipherList(ciphers);
- cryptoWishList.s2c_enc_algos = ciphers;
- }
-
- /**
- * Unless you know what you are doing, you will never need this.
- *
- * @param macs
- */
- public synchronized void setServer2ClientMACs(String[] macs)
- {
- if ((macs == null) || (macs.length == 0))
- throw new IllegalArgumentException();
-
- macs = removeDuplicates(macs);
- MAC.checkMacList(macs);
- cryptoWishList.s2c_mac_algos = macs;
- }
-
- /**
- * Define the set of allowed server host key algorithms to be used for the
- * following key exchange operations.
- * <p>
- * Unless you know what you are doing, you will never need this.
- *
- * @param algos
- * An array of allowed server host key algorithms. SSH-2 defines
- * <code>ssh-dss</code> and <code>ssh-rsa</code>. The
- * entries of the array must be ordered after preference, i.e.,
- * the entry at index 0 is the most preferred one. You must
- * specify at least one entry.
- */
- public synchronized void setServerHostKeyAlgorithms(String[] algos)
- {
- if ((algos == null) || (algos.length == 0))
- throw new IllegalArgumentException();
-
- algos = removeDuplicates(algos);
- KexManager.checkServerHostkeyAlgorithmsList(algos);
- cryptoWishList.serverHostKeyAlgorithms = algos;
- }
-
- /**
- * Enable/disable TCP_NODELAY (disable/enable Nagle's algorithm) on the
- * underlying socket.
- * <p>
- * Can be called at any time. If the connection has not yet been established
- * then the passed value will be stored and set after the socket has been
- * set up. The default value that will be used is <code>false</code>.
- *
- * @param enable
- * the argument passed to the <code>Socket.setTCPNoDelay()</code>
- * method.
- * @throws IOException
- */
- public synchronized void setTCPNoDelay(boolean enable) throws IOException
- {
- tcpNoDelay = enable;
-
- if (tm != null)
- tm.setTcpNoDelay(enable);
- }
-
- /**
- * Used to tell the library that the connection shall be established through
- * a proxy server. It only makes sense to call this method before calling
- * the {@link #connect() connect()} method.
- * <p>
- * At the moment, only HTTP proxies are supported.
- * <p>
- * Note: This method can be called any number of times. The
- * {@link #connect() connect()} method will use the value set in the last
- * preceding invocation of this method.
- *
- * @see HTTPProxyData
- *
- * @param proxyData
- * Connection information about the proxy. If <code>null</code>,
- * then no proxy will be used (non surprisingly, this is also the
- * default).
- */
- public synchronized void setProxyData(ProxyData proxyData)
- {
- this.proxyData = proxyData;
- }
-
- /**
- * Request a remote port forwarding. If successful, then forwarded
- * connections will be redirected to the given target address. You can
- * cancle a requested remote port forwarding by calling
- * {@link #cancelRemotePortForwarding(int) cancelRemotePortForwarding()}.
- * <p>
- * A call of this method will block until the peer either agreed or
- * disagreed to your request-
- * <p>
- * Note 1: this method typically fails if you
- * <ul>
- * <li>pass a port number for which the used remote user has not enough
- * permissions (i.e., port &lt; 1024)</li>
- * <li>or pass a port number that is already in use on the remote server</li>
- * <li>or if remote port forwarding is disabled on the server.</li>
- * </ul>
- * <p>
- * Note 2: (from the openssh man page): By default, the listening socket on
- * the server will be bound to the loopback interface only. This may be
- * overriden by specifying a bind address. Specifying a remote bind address
- * will only succeed if the server's <b>GatewayPorts</b> option is enabled
- * (see sshd_config(5)).
- *
- * @param bindAddress
- * address to bind to on the server:
- * <ul>
- * <li>"" means that connections are to be accepted on all
- * protocol families supported by the SSH implementation</li>
- * <li>"0.0.0.0" means to listen on all IPv4 addresses</li>
- * <li>"::" means to listen on all IPv6 addresses</li>
- * <li>"localhost" means to listen on all protocol families
- * supported by the SSH implementation on loopback addresses
- * only, [RFC3330] and RFC3513]</li>
- * <li>"127.0.0.1" and "::1" indicate listening on the loopback
- * interfaces for IPv4 and IPv6 respectively</li>
- * </ul>
- * @param bindPort
- * port number to bind on the server (must be &gt; 0)
- * @param targetAddress
- * the target address (IP or hostname)
- * @param targetPort
- * the target port
- * @throws IOException
- */
- public synchronized void requestRemotePortForwarding(String bindAddress, int bindPort, String targetAddress,
- int targetPort) throws IOException
- {
- if (tm == null)
- throw new IllegalStateException("You need to establish a connection first.");
-
- if (!authenticated)
- throw new IllegalStateException("The connection is not authenticated.");
-
- if ((bindAddress == null) || (targetAddress == null) || (bindPort <= 0) || (targetPort <= 0))
- throw new IllegalArgumentException();
-
- cm.requestGlobalForward(bindAddress, bindPort, targetAddress, targetPort);
- }
-
- /**
- * Cancel an earlier requested remote port forwarding. Currently active
- * forwardings will not be affected (e.g., disrupted). Note that further
- * connection forwarding requests may be received until this method has
- * returned.
- *
- * @param bindPort
- * the allocated port number on the server
- * @throws IOException
- * if the remote side refuses the cancel request or another low
- * level error occurs (e.g., the underlying connection is
- * closed)
- */
- public synchronized void cancelRemotePortForwarding(int bindPort) throws IOException
- {
- if (tm == null)
- throw new IllegalStateException("You need to establish a connection first.");
-
- if (!authenticated)
- throw new IllegalStateException("The connection is not authenticated.");
-
- cm.requestCancelGlobalForward(bindPort);
- }
-
- /**
- * Provide your own instance of SecureRandom. Can be used, e.g., if you want
- * to seed the used SecureRandom generator manually.
- * <p>
- * The SecureRandom instance is used during key exchanges, public key
- * authentication, x11 cookie generation and the like.
- *
- * @param rnd
- * a SecureRandom instance
- */
- public synchronized void setSecureRandom(SecureRandom rnd)
- {
- if (rnd == null)
- throw new IllegalArgumentException();
-
- this.generator = rnd;
- }
-
- /**
- * Enable/disable debug logging. <b>Only do this when requested by Trilead
- * support.</b>
- * <p>
- * For speed reasons, some static variables used to check whether debugging
- * is enabled are not protected with locks. In other words, if you
- * dynamicaly enable/disable debug logging, then some threads may still use
- * the old setting. To be on the safe side, enable debugging before doing
- * the <code>connect()</code> call.
- *
- * @param enable
- * on/off
- * @param logger
- * a {@link DebugLogger DebugLogger} instance, <code>null</code>
- * means logging using the simple logger which logs all messages
- * to to stderr. Ignored if enabled is <code>false</code>
- */
- public synchronized void enableDebugging(boolean enable, DebugLogger logger)
- {
- Logger.enabled = enable;
-
- if (enable == false)
- {
- Logger.logger = null;
- }
- else
- {
- if (logger == null)
- {
- logger = new DebugLogger()
- {
-
- public void log(int level, String className, String message)
- {
- long now = System.currentTimeMillis();
- System.err.println(now + " : " + className + ": " + message);
- }
- };
- }
-
- Logger.logger = logger;
- }
- }
-
- /**
- * This method can be used to perform end-to-end connection testing. It
- * sends a 'ping' message to the server and waits for the 'pong' from the
- * server.
- * <p>
- * When this method throws an exception, then you can assume that the
- * connection should be abandoned.
- * <p>
- * Note: Works only after one has passed successfully the authentication
- * step.
- * <p>
- * Implementation details: this method sends a SSH_MSG_GLOBAL_REQUEST
- * request ('trilead-ping') to the server and waits for the
- * SSH_MSG_REQUEST_FAILURE reply packet from the server.
- *
- * @throws IOException
- * in case of any problem
- */
- public synchronized void ping() throws IOException
- {
- if (tm == null)
- throw new IllegalStateException("You need to establish a connection first.");
-
- if (!authenticated)
- throw new IllegalStateException("The connection is not authenticated.");
-
- cm.requestGlobalTrileadPing();
- }
-}
+
+package com.trilead.ssh2;
+
+import java.io.CharArrayWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.SocketTimeoutException;
+import java.security.KeyPair;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.util.Set;
+import java.util.Vector;
+
+import com.trilead.ssh2.auth.AuthenticationManager;
+import com.trilead.ssh2.channel.ChannelManager;
+import com.trilead.ssh2.crypto.CryptoWishList;
+import com.trilead.ssh2.crypto.cipher.BlockCipherFactory;
+import com.trilead.ssh2.crypto.digest.MAC;
+import com.trilead.ssh2.log.Logger;
+import com.trilead.ssh2.packets.PacketIgnore;
+import com.trilead.ssh2.transport.KexManager;
+import com.trilead.ssh2.transport.TransportManager;
+import com.trilead.ssh2.util.TimeoutService;
+import com.trilead.ssh2.util.TimeoutService.TimeoutToken;
+
+/**
+ * A <code>Connection</code> is used to establish an encrypted TCP/IP
+ * connection to a SSH-2 server.
+ * <p>
+ * Typically, one
+ * <ol>
+ * <li>creates a {@link #Connection(String) Connection} object.</li>
+ * <li>calls the {@link #connect() connect()} method.</li>
+ * <li>calls some of the authentication methods (e.g.,
+ * {@link #authenticateWithPublicKey(String, File, String) authenticateWithPublicKey()}).</li>
+ * <li>calls one or several times the {@link #openSession() openSession()}
+ * method.</li>
+ * <li>finally, one must close the connection and release resources with the
+ * {@link #close() close()} method.</li>
+ * </ol>
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: Connection.java,v 1.3 2008/04/01 12:38:09 cplattne Exp $
+ */
+
+public class Connection
+{
+ /**
+ * The identifier presented to the SSH-2 server.
+ */
+ public final static String identification = "TrileadSSH2Java_213";
+
+ /**
+ * Will be used to generate all random data needed for the current
+ * connection. Note: SecureRandom.nextBytes() is thread safe.
+ */
+ private SecureRandom generator;
+
+ /**
+ * Unless you know what you are doing, you will never need this.
+ *
+ * @return The list of supported cipher algorithms by this implementation.
+ */
+ public static synchronized String[] getAvailableCiphers()
+ {
+ return BlockCipherFactory.getDefaultCipherList();
+ }
+
+ /**
+ * Unless you know what you are doing, you will never need this.
+ *
+ * @return The list of supported MAC algorthims by this implementation.
+ */
+ public static synchronized String[] getAvailableMACs()
+ {
+ return MAC.getMacList();
+ }
+
+ /**
+ * Unless you know what you are doing, you will never need this.
+ *
+ * @return The list of supported server host key algorthims by this
+ * implementation.
+ */
+ public static synchronized String[] getAvailableServerHostKeyAlgorithms()
+ {
+ return KexManager.getDefaultServerHostkeyAlgorithmList();
+ }
+
+ private AuthenticationManager am;
+
+ private boolean authenticated = false;
+ private boolean compression = false;
+ private ChannelManager cm;
+
+ private CryptoWishList cryptoWishList = new CryptoWishList();
+
+ private DHGexParameters dhgexpara = new DHGexParameters();
+
+ private final String hostname;
+
+ private final int port;
+
+ private TransportManager tm;
+
+ private boolean tcpNoDelay = false;
+
+ private ProxyData proxyData = null;
+
+ private Vector<ConnectionMonitor> connectionMonitors = new Vector<ConnectionMonitor>();
+
+ /**
+ * Prepares a fresh <code>Connection</code> object which can then be used
+ * to establish a connection to the specified SSH-2 server.
+ * <p>
+ * Same as {@link #Connection(String, int) Connection(hostname, 22)}.
+ *
+ * @param hostname
+ * the hostname of the SSH-2 server.
+ */
+ public Connection(String hostname)
+ {
+ this(hostname, 22);
+ }
+
+ /**
+ * Prepares a fresh <code>Connection</code> object which can then be used
+ * to establish a connection to the specified SSH-2 server.
+ *
+ * @param hostname
+ * the host where we later want to connect to.
+ * @param port
+ * port on the server, normally 22.
+ */
+ public Connection(String hostname, int port)
+ {
+ this.hostname = hostname;
+ this.port = port;
+ }
+
+ /**
+ * After a successful connect, one has to authenticate oneself. This method
+ * is based on DSA (it uses DSA to sign a challenge sent by the server).
+ * <p>
+ * If the authentication phase is complete, <code>true</code> will be
+ * returned. If the server does not accept the request (or if further
+ * authentication steps are needed), <code>false</code> is returned and
+ * one can retry either by using this or any other authentication method
+ * (use the <code>getRemainingAuthMethods</code> method to get a list of
+ * the remaining possible methods).
+ *
+ * @param user
+ * A <code>String</code> holding the username.
+ * @param pem
+ * A <code>String</code> containing the DSA private key of the
+ * user in OpenSSH key format (PEM, you can't miss the
+ * "-----BEGIN DSA PRIVATE KEY-----" tag). The string may contain
+ * linefeeds.
+ * @param password
+ * If the PEM string is 3DES encrypted ("DES-EDE3-CBC"), then you
+ * must specify the password. Otherwise, this argument will be
+ * ignored and can be set to <code>null</code>.
+ *
+ * @return whether the connection is now authenticated.
+ * @throws IOException
+ *
+ * @deprecated You should use one of the
+ * {@link #authenticateWithPublicKey(String, File, String) authenticateWithPublicKey()}
+ * methods, this method is just a wrapper for it and will
+ * disappear in future builds.
+ *
+ */
+ public synchronized boolean authenticateWithDSA(String user, String pem, String password) throws IOException
+ {
+ if (tm == null)
+ throw new IllegalStateException("Connection is not established!");
+
+ if (authenticated)
+ throw new IllegalStateException("Connection is already authenticated!");
+
+ if (am == null)
+ am = new AuthenticationManager(tm);
+
+ if (cm == null)
+ cm = new ChannelManager(tm);
+
+ if (user == null)
+ throw new IllegalArgumentException("user argument is null");
+
+ if (pem == null)
+ throw new IllegalArgumentException("pem argument is null");
+
+ authenticated = am.authenticatePublicKey(user, pem.toCharArray(), password, getOrCreateSecureRND());
+
+ return authenticated;
+ }
+
+ /**
+ * A wrapper that calls
+ * {@link #authenticateWithKeyboardInteractive(String, String[], InteractiveCallback)
+ * authenticateWithKeyboardInteractivewith} a <code>null</code> submethod
+ * list.
+ *
+ * @param user
+ * A <code>String</code> holding the username.
+ * @param cb
+ * An <code>InteractiveCallback</code> which will be used to
+ * determine the responses to the questions asked by the server.
+ * @return whether the connection is now authenticated.
+ * @throws IOException
+ */
+ public synchronized boolean authenticateWithKeyboardInteractive(String user, InteractiveCallback cb)
+ throws IOException
+ {
+ return authenticateWithKeyboardInteractive(user, null, cb);
+ }
+
+ /**
+ * After a successful connect, one has to authenticate oneself. This method
+ * is based on "keyboard-interactive", specified in
+ * draft-ietf-secsh-auth-kbdinteract-XX. Basically, you have to define a
+ * callback object which will be feeded with challenges generated by the
+ * server. Answers are then sent back to the server. It is possible that the
+ * callback will be called several times during the invocation of this
+ * method (e.g., if the server replies to the callback's answer(s) with
+ * another challenge...)
+ * <p>
+ * If the authentication phase is complete, <code>true</code> will be
+ * returned. If the server does not accept the request (or if further
+ * authentication steps are needed), <code>false</code> is returned and
+ * one can retry either by using this or any other authentication method
+ * (use the <code>getRemainingAuthMethods</code> method to get a list of
+ * the remaining possible methods).
+ * <p>
+ * Note: some SSH servers advertise "keyboard-interactive", however, any
+ * interactive request will be denied (without having sent any challenge to
+ * the client).
+ *
+ * @param user
+ * A <code>String</code> holding the username.
+ * @param submethods
+ * An array of submethod names, see
+ * draft-ietf-secsh-auth-kbdinteract-XX. May be <code>null</code>
+ * to indicate an empty list.
+ * @param cb
+ * An <code>InteractiveCallback</code> which will be used to
+ * determine the responses to the questions asked by the server.
+ *
+ * @return whether the connection is now authenticated.
+ * @throws IOException
+ */
+ public synchronized boolean authenticateWithKeyboardInteractive(String user, String[] submethods,
+ InteractiveCallback cb) throws IOException
+ {
+ if (cb == null)
+ throw new IllegalArgumentException("Callback may not ne NULL!");
+
+ if (tm == null)
+ throw new IllegalStateException("Connection is not established!");
+
+ if (authenticated)
+ throw new IllegalStateException("Connection is already authenticated!");
+
+ if (am == null)
+ am = new AuthenticationManager(tm);
+
+ if (cm == null)
+ cm = new ChannelManager(tm);
+
+ if (user == null)
+ throw new IllegalArgumentException("user argument is null");
+
+ authenticated = am.authenticateInteractive(user, submethods, cb);
+
+ return authenticated;
+ }
+
+ /**
+ * After a successful connect, one has to authenticate oneself. This method
+ * sends username and password to the server.
+ * <p>
+ * If the authentication phase is complete, <code>true</code> will be
+ * returned. If the server does not accept the request (or if further
+ * authentication steps are needed), <code>false</code> is returned and
+ * one can retry either by using this or any other authentication method
+ * (use the <code>getRemainingAuthMethods</code> method to get a list of
+ * the remaining possible methods).
+ * <p>
+ * Note: if this method fails, then please double-check that it is actually
+ * offered by the server (use
+ * {@link #getRemainingAuthMethods(String) getRemainingAuthMethods()}.
+ * <p>
+ * Often, password authentication is disabled, but users are not aware of
+ * it. Many servers only offer "publickey" and "keyboard-interactive".
+ * However, even though "keyboard-interactive" *feels* like password
+ * authentication (e.g., when using the putty or openssh clients) it is
+ * *not* the same mechanism.
+ *
+ * @param user
+ * @param password
+ * @return if the connection is now authenticated.
+ * @throws IOException
+ */
+ public synchronized boolean authenticateWithPassword(String user, String password) throws IOException
+ {
+ if (tm == null)
+ throw new IllegalStateException("Connection is not established!");
+
+ if (authenticated)
+ throw new IllegalStateException("Connection is already authenticated!");
+
+ if (am == null)
+ am = new AuthenticationManager(tm);
+
+ if (cm == null)
+ cm = new ChannelManager(tm);
+
+ if (user == null)
+ throw new IllegalArgumentException("user argument is null");
+
+ if (password == null)
+ throw new IllegalArgumentException("password argument is null");
+
+ authenticated = am.authenticatePassword(user, password);
+
+ return authenticated;
+ }
+
+ /**
+ * After a successful connect, one has to authenticate oneself. This method
+ * can be used to explicitly use the special "none" authentication method
+ * (where only a username has to be specified).
+ * <p>
+ * Note 1: The "none" method may always be tried by clients, however as by
+ * the specs, the server will not explicitly announce it. In other words,
+ * the "none" token will never show up in the list returned by
+ * {@link #getRemainingAuthMethods(String)}.
+ * <p>
+ * Note 2: no matter which one of the authenticateWithXXX() methods you
+ * call, the library will always issue exactly one initial "none"
+ * authentication request to retrieve the initially allowed list of
+ * authentication methods by the server. Please read RFC 4252 for the
+ * details.
+ * <p>
+ * If the authentication phase is complete, <code>true</code> will be
+ * returned. If further authentication steps are needed, <code>false</code>
+ * is returned and one can retry by any other authentication method (use the
+ * <code>getRemainingAuthMethods</code> method to get a list of the
+ * remaining possible methods).
+ *
+ * @param user
+ * @return if the connection is now authenticated.
+ * @throws IOException
+ */
+ public synchronized boolean authenticateWithNone(String user) throws IOException
+ {
+ if (tm == null)
+ throw new IllegalStateException("Connection is not established!");
+
+ if (authenticated)
+ throw new IllegalStateException("Connection is already authenticated!");
+
+ if (am == null)
+ am = new AuthenticationManager(tm);
+
+ if (cm == null)
+ cm = new ChannelManager(tm);
+
+ if (user == null)
+ throw new IllegalArgumentException("user argument is null");
+
+ /* Trigger the sending of the PacketUserauthRequestNone packet */
+ /* (if not already done) */
+
+ authenticated = am.authenticateNone(user);
+
+ return authenticated;
+ }
+
+ /**
+ * After a successful connect, one has to authenticate oneself. The
+ * authentication method "publickey" works by signing a challenge sent by
+ * the server. The signature is either DSA or RSA based - it just depends on
+ * the type of private key you specify, either a DSA or RSA private key in
+ * PEM format. And yes, this is may seem to be a little confusing, the
+ * method is called "publickey" in the SSH-2 protocol specification, however
+ * since we need to generate a signature, you actually have to supply a
+ * private key =).
+ * <p>
+ * The private key contained in the PEM file may also be encrypted
+ * ("Proc-Type: 4,ENCRYPTED"). The library supports DES-CBC and DES-EDE3-CBC
+ * encryption, as well as the more exotic PEM encrpytions AES-128-CBC,
+ * AES-192-CBC and AES-256-CBC.
+ * <p>
+ * If the authentication phase is complete, <code>true</code> will be
+ * returned. If the server does not accept the request (or if further
+ * authentication steps are needed), <code>false</code> is returned and
+ * one can retry either by using this or any other authentication method
+ * (use the <code>getRemainingAuthMethods</code> method to get a list of
+ * the remaining possible methods).
+ * <p>
+ * NOTE PUTTY USERS: Event though your key file may start with
+ * "-----BEGIN..." it is not in the expected format. You have to convert it
+ * to the OpenSSH key format by using the "puttygen" tool (can be downloaded
+ * from the Putty website). Simply load your key and then use the
+ * "Conversions/Export OpenSSH key" functionality to get a proper PEM file.
+ *
+ * @param user
+ * A <code>String</code> holding the username.
+ * @param pemPrivateKey
+ * A <code>char[]</code> containing a DSA or RSA private key of
+ * the user in OpenSSH key format (PEM, you can't miss the
+ * "-----BEGIN DSA PRIVATE KEY-----" or "-----BEGIN RSA PRIVATE
+ * KEY-----" tag). The char array may contain
+ * linebreaks/linefeeds.
+ * @param password
+ * If the PEM structure is encrypted ("Proc-Type: 4,ENCRYPTED")
+ * then you must specify a password. Otherwise, this argument
+ * will be ignored and can be set to <code>null</code>.
+ *
+ * @return whether the connection is now authenticated.
+ * @throws IOException
+ */
+ public synchronized boolean authenticateWithPublicKey(String user, char[] pemPrivateKey, String password)
+ throws IOException
+ {
+ if (tm == null)
+ throw new IllegalStateException("Connection is not established!");
+
+ if (authenticated)
+ throw new IllegalStateException("Connection is already authenticated!");
+
+ if (am == null)
+ am = new AuthenticationManager(tm);
+
+ if (cm == null)
+ cm = new ChannelManager(tm);
+
+ if (user == null)
+ throw new IllegalArgumentException("user argument is null");
+
+ if (pemPrivateKey == null)
+ throw new IllegalArgumentException("pemPrivateKey argument is null");
+
+ authenticated = am.authenticatePublicKey(user, pemPrivateKey, password, getOrCreateSecureRND());
+
+ return authenticated;
+ }
+
+ /**
+ * After a successful connect, one has to authenticate oneself. The
+ * authentication method "publickey" works by signing a challenge sent by
+ * the server. The signature is either DSA or RSA based - it just depends on
+ * the type of private key you specify, either a DSA or RSA private key in
+ * PEM format. And yes, this is may seem to be a little confusing, the
+ * method is called "publickey" in the SSH-2 protocol specification, however
+ * since we need to generate a signature, you actually have to supply a
+ * private key =).
+ * <p>
+ * If the authentication phase is complete, <code>true</code> will be
+ * returned. If the server does not accept the request (or if further
+ * authentication steps are needed), <code>false</code> is returned and
+ * one can retry either by using this or any other authentication method
+ * (use the <code>getRemainingAuthMethods</code> method to get a list of
+ * the remaining possible methods).
+ *
+ * @param user
+ * A <code>String</code> holding the username.
+ * @param key
+ * A <code>RSAPrivateKey</code> or <code>DSAPrivateKey</code>
+ * containing a DSA or RSA private key of
+ * the user in Trilead object format.
+ *
+ * @return whether the connection is now authenticated.
+ * @throws IOException
+ */
+ public synchronized boolean authenticateWithPublicKey(String user, KeyPair pair)
+ throws IOException
+ {
+ if (tm == null)
+ throw new IllegalStateException("Connection is not established!");
+
+ if (authenticated)
+ throw new IllegalStateException("Connection is already authenticated!");
+
+ if (am == null)
+ am = new AuthenticationManager(tm);
+
+ if (cm == null)
+ cm = new ChannelManager(tm);
+
+ if (user == null)
+ throw new IllegalArgumentException("user argument is null");
+
+ if (pair == null)
+ throw new IllegalArgumentException("Key pair argument is null");
+
+ authenticated = am.authenticatePublicKey(user, pair, getOrCreateSecureRND());
+
+ return authenticated;
+ }
+ /**
+ * A convenience wrapper function which reads in a private key (PEM format,
+ * either DSA or RSA) and then calls
+ * <code>authenticateWithPublicKey(String, char[], String)</code>.
+ * <p>
+ * NOTE PUTTY USERS: Event though your key file may start with
+ * "-----BEGIN..." it is not in the expected format. You have to convert it
+ * to the OpenSSH key format by using the "puttygen" tool (can be downloaded
+ * from the Putty website). Simply load your key and then use the
+ * "Conversions/Export OpenSSH key" functionality to get a proper PEM file.
+ *
+ * @param user
+ * A <code>String</code> holding the username.
+ * @param pemFile
+ * A <code>File</code> object pointing to a file containing a
+ * DSA or RSA private key of the user in OpenSSH key format (PEM,
+ * you can't miss the "-----BEGIN DSA PRIVATE KEY-----" or
+ * "-----BEGIN RSA PRIVATE KEY-----" tag).
+ * @param password
+ * If the PEM file is encrypted then you must specify the
+ * password. Otherwise, this argument will be ignored and can be
+ * set to <code>null</code>.
+ *
+ * @return whether the connection is now authenticated.
+ * @throws IOException
+ */
+ public synchronized boolean authenticateWithPublicKey(String user, File pemFile, String password)
+ throws IOException
+ {
+ if (pemFile == null)
+ throw new IllegalArgumentException("pemFile argument is null");
+
+ char[] buff = new char[256];
+
+ CharArrayWriter cw = new CharArrayWriter();
+
+ FileReader fr = new FileReader(pemFile);
+
+ while (true)
+ {
+ int len = fr.read(buff);
+ if (len < 0)
+ break;
+ cw.write(buff, 0, len);
+ }
+
+ fr.close();
+
+ return authenticateWithPublicKey(user, cw.toCharArray(), password);
+ }
+
+ /**
+ * Add a {@link ConnectionMonitor} to this connection. Can be invoked at any
+ * time, but it is best to add connection monitors before invoking
+ * <code>connect()</code> to avoid glitches (e.g., you add a connection
+ * monitor after a successful connect(), but the connection has died in the
+ * mean time. Then, your connection monitor won't be notified.)
+ * <p>
+ * You can add as many monitors as you like.
+ *
+ * @see ConnectionMonitor
+ *
+ * @param cmon
+ * An object implementing the <code>ConnectionMonitor</code>
+ * interface.
+ */
+ public synchronized void addConnectionMonitor(ConnectionMonitor cmon)
+ {
+ if (cmon == null)
+ throw new IllegalArgumentException("cmon argument is null");
+
+ connectionMonitors.addElement(cmon);
+
+ if (tm != null)
+ tm.setConnectionMonitors(connectionMonitors);
+ }
+
+ /**
+ * Controls whether compression is used on the link or not.
+ * <p>
+ * Note: This can only be called before connect()
+ * @param enabled whether to enable compression
+ * @throws IOException
+ */
+ public synchronized void setCompression(boolean enabled) throws IOException {
+ if (tm != null)
+ throw new IOException("Connection to " + hostname + " is already in connected state!");
+
+ compression = enabled;
+ }
+
+ /**
+ * Close the connection to the SSH-2 server. All assigned sessions will be
+ * closed, too. Can be called at any time. Don't forget to call this once
+ * you don't need a connection anymore - otherwise the receiver thread may
+ * run forever.
+ */
+ public synchronized void close()
+ {
+ Throwable t = new Throwable("Closed due to user request.");
+ close(t, false);
+ }
+
+ private void close(Throwable t, boolean hard)
+ {
+ if (cm != null)
+ cm.closeAllChannels();
+
+ if (tm != null)
+ {
+ tm.close(t, hard == false);
+ tm = null;
+ }
+ am = null;
+ cm = null;
+ authenticated = false;
+ }
+
+ /**
+ * Same as
+ * {@link #connect(ServerHostKeyVerifier, int, int) connect(null, 0, 0)}.
+ *
+ * @return see comments for the
+ * {@link #connect(ServerHostKeyVerifier, int, int) connect(ServerHostKeyVerifier, int, int)}
+ * method.
+ * @throws IOException
+ */
+ public synchronized ConnectionInfo connect() throws IOException
+ {
+ return connect(null, 0, 0);
+ }
+
+ /**
+ * Same as
+ * {@link #connect(ServerHostKeyVerifier, int, int) connect(verifier, 0, 0)}.
+ *
+ * @return see comments for the
+ * {@link #connect(ServerHostKeyVerifier, int, int) connect(ServerHostKeyVerifier, int, int)}
+ * method.
+ * @throws IOException
+ */
+ public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier) throws IOException
+ {
+ return connect(verifier, 0, 0);
+ }
+
+ /**
+ * Connect to the SSH-2 server and, as soon as the server has presented its
+ * host key, use the
+ * {@link ServerHostKeyVerifier#verifyServerHostKey(String, int, String,
+ * byte[]) ServerHostKeyVerifier.verifyServerHostKey()} method of the
+ * <code>verifier</code> to ask for permission to proceed. If
+ * <code>verifier</code> is <code>null</code>, then any host key will
+ * be accepted - this is NOT recommended, since it makes man-in-the-middle
+ * attackes VERY easy (somebody could put a proxy SSH server between you and
+ * the real server).
+ * <p>
+ * Note: The verifier will be called before doing any crypto calculations
+ * (i.e., diffie-hellman). Therefore, if you don't like the presented host
+ * key then no CPU cycles are wasted (and the evil server has less
+ * information about us).
+ * <p>
+ * However, it is still possible that the server presented a fake host key:
+ * the server cheated (typically a sign for a man-in-the-middle attack) and
+ * is not able to generate a signature that matches its host key. Don't
+ * worry, the library will detect such a scenario later when checking the
+ * signature (the signature cannot be checked before having completed the
+ * diffie-hellman exchange).
+ * <p>
+ * Note 2: The {@link ServerHostKeyVerifier#verifyServerHostKey(String, int,
+ * String, byte[]) ServerHostKeyVerifier.verifyServerHostKey()} method will
+ * *NOT* be called from the current thread, the call is being made from a
+ * background thread (there is a background dispatcher thread for every
+ * established connection).
+ * <p>
+ * Note 3: This method will block as long as the key exchange of the
+ * underlying connection has not been completed (and you have not specified
+ * any timeouts).
+ * <p>
+ * Note 4: If you want to re-use a connection object that was successfully
+ * connected, then you must call the {@link #close()} method before invoking
+ * <code>connect()</code> again.
+ *
+ * @param verifier
+ * An object that implements the {@link ServerHostKeyVerifier}
+ * interface. Pass <code>null</code> to accept any server host
+ * key - NOT recommended.
+ *
+ * @param connectTimeout
+ * Connect the underlying TCP socket to the server with the given
+ * timeout value (non-negative, in milliseconds). Zero means no
+ * timeout. If a proxy is being used (see
+ * {@link #setProxyData(ProxyData)}), then this timeout is used
+ * for the connection establishment to the proxy.
+ *
+ * @param kexTimeout
+ * Timeout for complete connection establishment (non-negative,
+ * in milliseconds). Zero means no timeout. The timeout counts
+ * from the moment you invoke the connect() method and is
+ * cancelled as soon as the first key-exchange round has
+ * finished. It is possible that the timeout event will be fired
+ * during the invocation of the <code>verifier</code> callback,
+ * but it will only have an effect after the
+ * <code>verifier</code> returns.
+ *
+ * @return A {@link ConnectionInfo} object containing the details of the
+ * established connection.
+ *
+ * @throws IOException
+ * If any problem occurs, e.g., the server's host key is not
+ * accepted by the <code>verifier</code> or there is problem
+ * during the initial crypto setup (e.g., the signature sent by
+ * the server is wrong).
+ * <p>
+ * In case of a timeout (either connectTimeout or kexTimeout) a
+ * SocketTimeoutException is thrown.
+ * <p>
+ * An exception may also be thrown if the connection was already
+ * successfully connected (no matter if the connection broke in
+ * the mean time) and you invoke <code>connect()</code> again
+ * without having called {@link #close()} first.
+ * <p>
+ * If a HTTP proxy is being used and the proxy refuses the
+ * connection, then a {@link HTTPProxyException} may be thrown,
+ * which contains the details returned by the proxy. If the
+ * proxy is buggy and does not return a proper HTTP response,
+ * then a normal IOException is thrown instead.
+ */
+ public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier, int connectTimeout, int kexTimeout)
+ throws IOException
+ {
+ final class TimeoutState
+ {
+ boolean isCancelled = false;
+ boolean timeoutSocketClosed = false;
+ }
+
+ if (tm != null)
+ throw new IOException("Connection to " + hostname + " is already in connected state!");
+
+ if (connectTimeout < 0)
+ throw new IllegalArgumentException("connectTimeout must be non-negative!");
+
+ if (kexTimeout < 0)
+ throw new IllegalArgumentException("kexTimeout must be non-negative!");
+
+ final TimeoutState state = new TimeoutState();
+
+ tm = new TransportManager(hostname, port);
+
+ tm.setConnectionMonitors(connectionMonitors);
+
+ // Don't offer compression if not requested
+ if (!compression) {
+ cryptoWishList.c2s_comp_algos = new String[] { "none" };
+ cryptoWishList.s2c_comp_algos = new String[] { "none" };
+ }
+
+ /*
+ * Make sure that the runnable below will observe the new value of "tm"
+ * and "state" (the runnable will be executed in a different thread,
+ * which may be already running, that is why we need a memory barrier
+ * here). See also the comment in Channel.java if you are interested in
+ * the details.
+ *
+ * OKOK, this is paranoid since adding the runnable to the todo list of
+ * the TimeoutService will ensure that all writes have been flushed
+ * before the Runnable reads anything (there is a synchronized block in
+ * TimeoutService.addTimeoutHandler).
+ */
+
+ synchronized (tm)
+ {
+ /* We could actually synchronize on anything. */
+ }
+
+ try
+ {
+ TimeoutToken token = null;
+
+ if (kexTimeout > 0)
+ {
+ final Runnable timeoutHandler = new Runnable()
+ {
+ public void run()
+ {
+ synchronized (state)
+ {
+ if (state.isCancelled)
+ return;
+ state.timeoutSocketClosed = true;
+ tm.close(new SocketTimeoutException("The connect timeout expired"), false);
+ }
+ }
+ };
+
+ long timeoutHorizont = System.currentTimeMillis() + kexTimeout;
+
+ token = TimeoutService.addTimeoutHandler(timeoutHorizont, timeoutHandler);
+ }
+
+ try
+ {
+ tm.initialize(cryptoWishList, verifier, dhgexpara, connectTimeout, getOrCreateSecureRND(), proxyData);
+ }
+ catch (SocketTimeoutException se)
+ {
+ throw (SocketTimeoutException) new SocketTimeoutException(
+ "The connect() operation on the socket timed out.").initCause(se);
+ }
+
+ tm.setTcpNoDelay(tcpNoDelay);
+
+ /* Wait until first KEX has finished */
+
+ ConnectionInfo ci = tm.getConnectionInfo(1);
+
+ /* Now try to cancel the timeout, if needed */
+
+ if (token != null)
+ {
+ TimeoutService.cancelTimeoutHandler(token);
+
+ /* Were we too late? */
+
+ synchronized (state)
+ {
+ if (state.timeoutSocketClosed)
+ throw new IOException("This exception will be replaced by the one below =)");
+ /*
+ * Just in case the "cancelTimeoutHandler" invocation came
+ * just a little bit too late but the handler did not enter
+ * the semaphore yet - we can still stop it.
+ */
+ state.isCancelled = true;
+ }
+ }
+
+ return ci;
+ }
+ catch (SocketTimeoutException ste)
+ {
+ throw ste;
+ }
+ catch (IOException e1)
+ {
+ /* This will also invoke any registered connection monitors */
+ close(new Throwable("There was a problem during connect."), false);
+
+ synchronized (state)
+ {
+ /*
+ * Show a clean exception, not something like "the socket is
+ * closed!?!"
+ */
+ if (state.timeoutSocketClosed)
+ throw new SocketTimeoutException("The kexTimeout (" + kexTimeout + " ms) expired.");
+ }
+
+ /* Do not wrap a HTTPProxyException */
+ if (e1 instanceof HTTPProxyException)
+ throw e1;
+
+ throw (IOException) new IOException("There was a problem while connecting to " + hostname + ":" + port)
+ .initCause(e1);
+ }
+ }
+
+ /**
+ * Creates a new {@link LocalPortForwarder}. A
+ * <code>LocalPortForwarder</code> forwards TCP/IP connections that arrive
+ * at a local port via the secure tunnel to another host (which may or may
+ * not be identical to the remote SSH-2 server).
+ * <p>
+ * This method must only be called after one has passed successfully the
+ * authentication step. There is no limit on the number of concurrent
+ * forwardings.
+ *
+ * @param local_port
+ * the local port the LocalPortForwarder shall bind to.
+ * @param host_to_connect
+ * target address (IP or hostname)
+ * @param port_to_connect
+ * target port
+ * @return A {@link LocalPortForwarder} object.
+ * @throws IOException
+ */
+ public synchronized LocalPortForwarder createLocalPortForwarder(int local_port, String host_to_connect,
+ int port_to_connect) throws IOException
+ {
+ if (tm == null)
+ throw new IllegalStateException("Cannot forward ports, you need to establish a connection first.");
+
+ if (!authenticated)
+ throw new IllegalStateException("Cannot forward ports, connection is not authenticated.");
+
+ return new LocalPortForwarder(cm, local_port, host_to_connect, port_to_connect);
+ }
+
+ /**
+ * Creates a new {@link LocalPortForwarder}. A
+ * <code>LocalPortForwarder</code> forwards TCP/IP connections that arrive
+ * at a local port via the secure tunnel to another host (which may or may
+ * not be identical to the remote SSH-2 server).
+ * <p>
+ * This method must only be called after one has passed successfully the
+ * authentication step. There is no limit on the number of concurrent
+ * forwardings.
+ *
+ * @param addr
+ * specifies the InetSocketAddress where the local socket shall
+ * be bound to.
+ * @param host_to_connect
+ * target address (IP or hostname)
+ * @param port_to_connect
+ * target port
+ * @return A {@link LocalPortForwarder} object.
+ * @throws IOException
+ */
+ public synchronized LocalPortForwarder createLocalPortForwarder(InetSocketAddress addr, String host_to_connect,
+ int port_to_connect) throws IOException
+ {
+ if (tm == null)
+ throw new IllegalStateException("Cannot forward ports, you need to establish a connection first.");
+
+ if (!authenticated)
+ throw new IllegalStateException("Cannot forward ports, connection is not authenticated.");
+
+ return new LocalPortForwarder(cm, addr, host_to_connect, port_to_connect);
+ }
+
+ /**
+ * Creates a new {@link LocalStreamForwarder}. A
+ * <code>LocalStreamForwarder</code> manages an Input/Outputstream pair
+ * that is being forwarded via the secure tunnel into a TCP/IP connection to
+ * another host (which may or may not be identical to the remote SSH-2
+ * server).
+ *
+ * @param host_to_connect
+ * @param port_to_connect
+ * @return A {@link LocalStreamForwarder} object.
+ * @throws IOException
+ */
+ public synchronized LocalStreamForwarder createLocalStreamForwarder(String host_to_connect, int port_to_connect)
+ throws IOException
+ {
+ if (tm == null)
+ throw new IllegalStateException("Cannot forward, you need to establish a connection first.");
+
+ if (!authenticated)
+ throw new IllegalStateException("Cannot forward, connection is not authenticated.");
+
+ return new LocalStreamForwarder(cm, host_to_connect, port_to_connect);
+ }
+
+ /**
+ * Creates a new {@link DynamicPortForwarder}. A
+ * <code>DynamicPortForwarder</code> forwards TCP/IP connections that arrive
+ * at a local port via the secure tunnel to another host that is chosen via
+ * the SOCKS protocol.
+ * <p>
+ * This method must only be called after one has passed successfully the
+ * authentication step. There is no limit on the number of concurrent
+ * forwardings.
+ *
+ * @param local_port
+ * @return A {@link DynamicPortForwarder} object.
+ * @throws IOException
+ */
+ public synchronized DynamicPortForwarder createDynamicPortForwarder(int local_port) throws IOException
+ {
+ if (tm == null)
+ throw new IllegalStateException("Cannot forward ports, you need to establish a connection first.");
+
+ if (!authenticated)
+ throw new IllegalStateException("Cannot forward ports, connection is not authenticated.");
+
+ return new DynamicPortForwarder(cm, local_port);
+ }
+
+ /**
+ * Creates a new {@link DynamicPortForwarder}. A
+ * <code>DynamicPortForwarder</code> forwards TCP/IP connections that arrive
+ * at a local port via the secure tunnel to another host that is chosen via
+ * the SOCKS protocol.
+ * <p>
+ * This method must only be called after one has passed successfully the
+ * authentication step. There is no limit on the number of concurrent
+ * forwardings.
+ *
+ * @param addr
+ * specifies the InetSocketAddress where the local socket shall
+ * be bound to.
+ * @return A {@link DynamicPortForwarder} object.
+ * @throws IOException
+ */
+ public synchronized DynamicPortForwarder createDynamicPortForwarder(InetSocketAddress addr) throws IOException
+ {
+ if (tm == null)
+ throw new IllegalStateException("Cannot forward ports, you need to establish a connection first.");
+
+ if (!authenticated)
+ throw new IllegalStateException("Cannot forward ports, connection is not authenticated.");
+
+ return new DynamicPortForwarder(cm, addr);
+ }
+
+ /**
+ * Create a very basic {@link SCPClient} that can be used to copy files
+ * from/to the SSH-2 server.
+ * <p>
+ * Works only after one has passed successfully the authentication step.
+ * There is no limit on the number of concurrent SCP clients.
+ * <p>
+ * Note: This factory method will probably disappear in the future.
+ *
+ * @return A {@link SCPClient} object.
+ * @throws IOException
+ */
+ public synchronized SCPClient createSCPClient() throws IOException
+ {
+ if (tm == null)
+ throw new IllegalStateException("Cannot create SCP client, you need to establish a connection first.");
+
+ if (!authenticated)
+ throw new IllegalStateException("Cannot create SCP client, connection is not authenticated.");
+
+ return new SCPClient(this);
+ }
+
+ /**
+ * Force an asynchronous key re-exchange (the call does not block). The
+ * latest values set for MAC, Cipher and DH group exchange parameters will
+ * be used. If a key exchange is currently in progress, then this method has
+ * the only effect that the so far specified parameters will be used for the
+ * next (server driven) key exchange.
+ * <p>
+ * Note: This implementation will never start a key exchange (other than the
+ * initial one) unless you or the SSH-2 server ask for it.
+ *
+ * @throws IOException
+ * In case of any failure behind the scenes.
+ */
+ public synchronized void forceKeyExchange() throws IOException
+ {
+ if (tm == null)
+ throw new IllegalStateException("You need to establish a connection first.");
+
+ tm.forceKeyExchange(cryptoWishList, dhgexpara);
+ }
+
+ /**
+ * Returns the hostname that was passed to the constructor.
+ *
+ * @return the hostname
+ */
+ public synchronized String getHostname()
+ {
+ return hostname;
+ }
+
+ /**
+ * Returns the port that was passed to the constructor.
+ *
+ * @return the TCP port
+ */
+ public synchronized int getPort()
+ {
+ return port;
+ }
+
+ /**
+ * Returns a {@link ConnectionInfo} object containing the details of the
+ * connection. Can be called as soon as the connection has been established
+ * (successfully connected).
+ *
+ * @return A {@link ConnectionInfo} object.
+ * @throws IOException
+ * In case of any failure behind the scenes.
+ */
+ public synchronized ConnectionInfo getConnectionInfo() throws IOException
+ {
+ if (tm == null)
+ throw new IllegalStateException(
+ "Cannot get details of connection, you need to establish a connection first.");
+ return tm.getConnectionInfo(1);
+ }
+
+ /**
+ * After a successful connect, one has to authenticate oneself. This method
+ * can be used to tell which authentication methods are supported by the
+ * server at a certain stage of the authentication process (for the given
+ * username).
+ * <p>
+ * Note 1: the username will only be used if no authentication step was done
+ * so far (it will be used to ask the server for a list of possible
+ * authentication methods by sending the initial "none" request). Otherwise,
+ * this method ignores the user name and returns a cached method list (which
+ * is based on the information contained in the last negative server
+ * response).
+ * <p>
+ * Note 2: the server may return method names that are not supported by this
+ * implementation.
+ * <p>
+ * After a successful authentication, this method must not be called
+ * anymore.
+ *
+ * @param user
+ * A <code>String</code> holding the username.
+ *
+ * @return a (possibly emtpy) array holding authentication method names.
+ * @throws IOException
+ */
+ public synchronized String[] getRemainingAuthMethods(String user) throws IOException
+ {
+ if (user == null)
+ throw new IllegalArgumentException("user argument may not be NULL!");
+
+ if (tm == null)
+ throw new IllegalStateException("Connection is not established!");
+
+ if (authenticated)
+ throw new IllegalStateException("Connection is already authenticated!");
+
+ if (am == null)
+ am = new AuthenticationManager(tm);
+
+ if (cm == null)
+ cm = new ChannelManager(tm);
+
+ return am.getRemainingMethods(user);
+ }
+
+ /**
+ * Determines if the authentication phase is complete. Can be called at any
+ * time.
+ *
+ * @return <code>true</code> if no further authentication steps are
+ * needed.
+ */
+ public synchronized boolean isAuthenticationComplete()
+ {
+ return authenticated;
+ }
+
+ /**
+ * Returns true if there was at least one failed authentication request and
+ * the last failed authentication request was marked with "partial success"
+ * by the server. This is only needed in the rare case of SSH-2 server
+ * setups that cannot be satisfied with a single successful authentication
+ * request (i.e., multiple authentication steps are needed.)
+ * <p>
+ * If you are interested in the details, then have a look at RFC4252.
+ *
+ * @return if the there was a failed authentication step and the last one
+ * was marked as a "partial success".
+ */
+ public synchronized boolean isAuthenticationPartialSuccess()
+ {
+ if (am == null)
+ return false;
+
+ return am.getPartialSuccess();
+ }
+
+ /**
+ * Checks if a specified authentication method is available. This method is
+ * actually just a wrapper for {@link #getRemainingAuthMethods(String)
+ * getRemainingAuthMethods()}.
+ *
+ * @param user
+ * A <code>String</code> holding the username.
+ * @param method
+ * An authentication method name (e.g., "publickey", "password",
+ * "keyboard-interactive") as specified by the SSH-2 standard.
+ * @return if the specified authentication method is currently available.
+ * @throws IOException
+ */
+ public synchronized boolean isAuthMethodAvailable(String user, String method) throws IOException
+ {
+ if (method == null)
+ throw new IllegalArgumentException("method argument may not be NULL!");
+
+ String methods[] = getRemainingAuthMethods(user);
+
+ for (int i = 0; i < methods.length; i++)
+ {
+ if (methods[i].compareTo(method) == 0)
+ return true;
+ }
+
+ return false;
+ }
+
+ private final SecureRandom getOrCreateSecureRND()
+ {
+ if (generator == null)
+ generator = new SecureRandom();
+
+ return generator;
+ }
+
+ /**
+ * Open a new {@link Session} on this connection. Works only after one has
+ * passed successfully the authentication step. There is no limit on the
+ * number of concurrent sessions.
+ *
+ * @return A {@link Session} object.
+ * @throws IOException
+ */
+ public synchronized Session openSession() throws IOException
+ {
+ if (tm == null)
+ throw new IllegalStateException("Cannot open session, you need to establish a connection first.");
+
+ if (!authenticated)
+ throw new IllegalStateException("Cannot open session, connection is not authenticated.");
+
+ return new Session(cm, getOrCreateSecureRND());
+ }
+
+ /**
+ * Send an SSH_MSG_IGNORE packet. This method will generate a random data
+ * attribute (length between 0 (invlusive) and 16 (exclusive) bytes,
+ * contents are random bytes).
+ * <p>
+ * This method must only be called once the connection is established.
+ *
+ * @throws IOException
+ */
+ public synchronized void sendIgnorePacket() throws IOException
+ {
+ SecureRandom rnd = getOrCreateSecureRND();
+
+ byte[] data = new byte[rnd.nextInt(16)];
+ rnd.nextBytes(data);
+
+ sendIgnorePacket(data);
+ }
+
+ /**
+ * Send an SSH_MSG_IGNORE packet with the given data attribute.
+ * <p>
+ * This method must only be called once the connection is established.
+ *
+ * @throws IOException
+ */
+ public synchronized void sendIgnorePacket(byte[] data) throws IOException
+ {
+ if (data == null)
+ throw new IllegalArgumentException("data argument must not be null.");
+
+ if (tm == null)
+ throw new IllegalStateException(
+ "Cannot send SSH_MSG_IGNORE packet, you need to establish a connection first.");
+
+ PacketIgnore pi = new PacketIgnore();
+ pi.setData(data);
+
+ tm.sendMessage(pi.getPayload());
+ }
+
+ /**
+ * Removes duplicates from a String array, keeps only first occurence of
+ * each element. Does not destroy order of elements; can handle nulls. Uses
+ * a very efficient O(N^2) algorithm =)
+ *
+ * @param list
+ * a String array.
+ * @return a cleaned String array.
+ */
+ private String[] removeDuplicates(String[] list)
+ {
+ if ((list == null) || (list.length < 2))
+ return list;
+
+ String[] list2 = new String[list.length];
+
+ int count = 0;
+
+ for (int i = 0; i < list.length; i++)
+ {
+ boolean duplicate = false;
+
+ String element = list[i];
+
+ for (int j = 0; j < count; j++)
+ {
+ if (((element == null) && (list2[j] == null)) || ((element != null) && (element.equals(list2[j]))))
+ {
+ duplicate = true;
+ break;
+ }
+ }
+
+ if (duplicate)
+ continue;
+
+ list2[count++] = list[i];
+ }
+
+ if (count == list2.length)
+ return list2;
+
+ String[] tmp = new String[count];
+ System.arraycopy(list2, 0, tmp, 0, count);
+
+ return tmp;
+ }
+
+ /**
+ * Unless you know what you are doing, you will never need this.
+ *
+ * @param ciphers
+ */
+ public synchronized void setClient2ServerCiphers(String[] ciphers)
+ {
+ if ((ciphers == null) || (ciphers.length == 0))
+ throw new IllegalArgumentException();
+ ciphers = removeDuplicates(ciphers);
+ BlockCipherFactory.checkCipherList(ciphers);
+ cryptoWishList.c2s_enc_algos = ciphers;
+ }
+
+ /**
+ * Unless you know what you are doing, you will never need this.
+ *
+ * @param macs
+ */
+ public synchronized void setClient2ServerMACs(String[] macs)
+ {
+ if ((macs == null) || (macs.length == 0))
+ throw new IllegalArgumentException();
+ macs = removeDuplicates(macs);
+ MAC.checkMacList(macs);
+ cryptoWishList.c2s_mac_algos = macs;
+ }
+
+ /**
+ * Sets the parameters for the diffie-hellman group exchange. Unless you
+ * know what you are doing, you will never need this. Default values are
+ * defined in the {@link DHGexParameters} class.
+ *
+ * @param dgp
+ * {@link DHGexParameters}, non null.
+ *
+ */
+ public synchronized void setDHGexParameters(DHGexParameters dgp)
+ {
+ if (dgp == null)
+ throw new IllegalArgumentException();
+
+ dhgexpara = dgp;
+ }
+
+ /**
+ * Unless you know what you are doing, you will never need this.
+ *
+ * @param ciphers
+ */
+ public synchronized void setServer2ClientCiphers(String[] ciphers)
+ {
+ if ((ciphers == null) || (ciphers.length == 0))
+ throw new IllegalArgumentException();
+ ciphers = removeDuplicates(ciphers);
+ BlockCipherFactory.checkCipherList(ciphers);
+ cryptoWishList.s2c_enc_algos = ciphers;
+ }
+
+ /**
+ * Unless you know what you are doing, you will never need this.
+ *
+ * @param macs
+ */
+ public synchronized void setServer2ClientMACs(String[] macs)
+ {
+ if ((macs == null) || (macs.length == 0))
+ throw new IllegalArgumentException();
+
+ macs = removeDuplicates(macs);
+ MAC.checkMacList(macs);
+ cryptoWishList.s2c_mac_algos = macs;
+ }
+
+ /**
+ * Define the set of allowed server host key algorithms to be used for the
+ * following key exchange operations.
+ * <p>
+ * Unless you know what you are doing, you will never need this.
+ *
+ * @param algos
+ * An array of allowed server host key algorithms. SSH-2 defines
+ * <code>ssh-dss</code> and <code>ssh-rsa</code>. The
+ * entries of the array must be ordered after preference, i.e.,
+ * the entry at index 0 is the most preferred one. You must
+ * specify at least one entry.
+ */
+ public synchronized void setServerHostKeyAlgorithms(String[] algos)
+ {
+ if ((algos == null) || (algos.length == 0))
+ throw new IllegalArgumentException();
+
+ algos = removeDuplicates(algos);
+ KexManager.checkServerHostkeyAlgorithmsList(algos);
+ cryptoWishList.serverHostKeyAlgorithms = algos;
+ }
+
+ /**
+ * Enable/disable TCP_NODELAY (disable/enable Nagle's algorithm) on the
+ * underlying socket.
+ * <p>
+ * Can be called at any time. If the connection has not yet been established
+ * then the passed value will be stored and set after the socket has been
+ * set up. The default value that will be used is <code>false</code>.
+ *
+ * @param enable
+ * the argument passed to the <code>Socket.setTCPNoDelay()</code>
+ * method.
+ * @throws IOException
+ */
+ public synchronized void setTCPNoDelay(boolean enable) throws IOException
+ {
+ tcpNoDelay = enable;
+
+ if (tm != null)
+ tm.setTcpNoDelay(enable);
+ }
+
+ /**
+ * Used to tell the library that the connection shall be established through
+ * a proxy server. It only makes sense to call this method before calling
+ * the {@link #connect() connect()} method.
+ * <p>
+ * At the moment, only HTTP proxies are supported.
+ * <p>
+ * Note: This method can be called any number of times. The
+ * {@link #connect() connect()} method will use the value set in the last
+ * preceding invocation of this method.
+ *
+ * @see HTTPProxyData
+ *
+ * @param proxyData
+ * Connection information about the proxy. If <code>null</code>,
+ * then no proxy will be used (non surprisingly, this is also the
+ * default).
+ */
+ public synchronized void setProxyData(ProxyData proxyData)
+ {
+ this.proxyData = proxyData;
+ }
+
+ /**
+ * Request a remote port forwarding. If successful, then forwarded
+ * connections will be redirected to the given target address. You can
+ * cancle a requested remote port forwarding by calling
+ * {@link #cancelRemotePortForwarding(int) cancelRemotePortForwarding()}.
+ * <p>
+ * A call of this method will block until the peer either agreed or
+ * disagreed to your request-
+ * <p>
+ * Note 1: this method typically fails if you
+ * <ul>
+ * <li>pass a port number for which the used remote user has not enough
+ * permissions (i.e., port &lt; 1024)</li>
+ * <li>or pass a port number that is already in use on the remote server</li>
+ * <li>or if remote port forwarding is disabled on the server.</li>
+ * </ul>
+ * <p>
+ * Note 2: (from the openssh man page): By default, the listening socket on
+ * the server will be bound to the loopback interface only. This may be
+ * overriden by specifying a bind address. Specifying a remote bind address
+ * will only succeed if the server's <b>GatewayPorts</b> option is enabled
+ * (see sshd_config(5)).
+ *
+ * @param bindAddress
+ * address to bind to on the server:
+ * <ul>
+ * <li>"" means that connections are to be accepted on all
+ * protocol families supported by the SSH implementation</li>
+ * <li>"0.0.0.0" means to listen on all IPv4 addresses</li>
+ * <li>"::" means to listen on all IPv6 addresses</li>
+ * <li>"localhost" means to listen on all protocol families
+ * supported by the SSH implementation on loopback addresses
+ * only, [RFC3330] and RFC3513]</li>
+ * <li>"127.0.0.1" and "::1" indicate listening on the loopback
+ * interfaces for IPv4 and IPv6 respectively</li>
+ * </ul>
+ * @param bindPort
+ * port number to bind on the server (must be &gt; 0)
+ * @param targetAddress
+ * the target address (IP or hostname)
+ * @param targetPort
+ * the target port
+ * @throws IOException
+ */
+ public synchronized void requestRemotePortForwarding(String bindAddress, int bindPort, String targetAddress,
+ int targetPort) throws IOException
+ {
+ if (tm == null)
+ throw new IllegalStateException("You need to establish a connection first.");
+
+ if (!authenticated)
+ throw new IllegalStateException("The connection is not authenticated.");
+
+ if ((bindAddress == null) || (targetAddress == null) || (bindPort <= 0) || (targetPort <= 0))
+ throw new IllegalArgumentException();
+
+ cm.requestGlobalForward(bindAddress, bindPort, targetAddress, targetPort);
+ }
+
+ /**
+ * Cancel an earlier requested remote port forwarding. Currently active
+ * forwardings will not be affected (e.g., disrupted). Note that further
+ * connection forwarding requests may be received until this method has
+ * returned.
+ *
+ * @param bindPort
+ * the allocated port number on the server
+ * @throws IOException
+ * if the remote side refuses the cancel request or another low
+ * level error occurs (e.g., the underlying connection is
+ * closed)
+ */
+ public synchronized void cancelRemotePortForwarding(int bindPort) throws IOException
+ {
+ if (tm == null)
+ throw new IllegalStateException("You need to establish a connection first.");
+
+ if (!authenticated)
+ throw new IllegalStateException("The connection is not authenticated.");
+
+ cm.requestCancelGlobalForward(bindPort);
+ }
+
+ /**
+ * Provide your own instance of SecureRandom. Can be used, e.g., if you want
+ * to seed the used SecureRandom generator manually.
+ * <p>
+ * The SecureRandom instance is used during key exchanges, public key
+ * authentication, x11 cookie generation and the like.
+ *
+ * @param rnd
+ * a SecureRandom instance
+ */
+ public synchronized void setSecureRandom(SecureRandom rnd)
+ {
+ if (rnd == null)
+ throw new IllegalArgumentException();
+
+ this.generator = rnd;
+ }
+
+ /**
+ * Enable/disable debug logging. <b>Only do this when requested by Trilead
+ * support.</b>
+ * <p>
+ * For speed reasons, some static variables used to check whether debugging
+ * is enabled are not protected with locks. In other words, if you
+ * dynamicaly enable/disable debug logging, then some threads may still use
+ * the old setting. To be on the safe side, enable debugging before doing
+ * the <code>connect()</code> call.
+ *
+ * @param enable
+ * on/off
+ * @param logger
+ * a {@link DebugLogger DebugLogger} instance, <code>null</code>
+ * means logging using the simple logger which logs all messages
+ * to to stderr. Ignored if enabled is <code>false</code>
+ */
+ public synchronized void enableDebugging(boolean enable, DebugLogger logger)
+ {
+ Logger.enabled = enable;
+
+ if (enable == false)
+ {
+ Logger.logger = null;
+ }
+ else
+ {
+ if (logger == null)
+ {
+ logger = new DebugLogger()
+ {
+
+ public void log(int level, String className, String message)
+ {
+ long now = System.currentTimeMillis();
+ System.err.println(now + " : " + className + ": " + message);
+ }
+ };
+ }
+
+ Logger.logger = logger;
+ }
+ }
+
+ /**
+ * This method can be used to perform end-to-end connection testing. It
+ * sends a 'ping' message to the server and waits for the 'pong' from the
+ * server.
+ * <p>
+ * When this method throws an exception, then you can assume that the
+ * connection should be abandoned.
+ * <p>
+ * Note: Works only after one has passed successfully the authentication
+ * step.
+ * <p>
+ * Implementation details: this method sends a SSH_MSG_GLOBAL_REQUEST
+ * request ('trilead-ping') to the server and waits for the
+ * SSH_MSG_REQUEST_FAILURE reply packet from the server.
+ *
+ * @throws IOException
+ * in case of any problem
+ */
+ public synchronized void ping() throws IOException
+ {
+ if (tm == null)
+ throw new IllegalStateException("You need to establish a connection first.");
+
+ if (!authenticated)
+ throw new IllegalStateException("The connection is not authenticated.");
+
+ cm.requestGlobalTrileadPing();
+ }
+}
diff --git a/src/com/trilead/ssh2/ConnectionInfo.java b/src/com/trilead/ssh2/ConnectionInfo.java
index 34d9453..b0ddbcf 100644
--- a/src/com/trilead/ssh2/ConnectionInfo.java
+++ b/src/com/trilead/ssh2/ConnectionInfo.java
@@ -1,53 +1,53 @@
-
-package com.trilead.ssh2;
-
-/**
- * In most cases you probably do not need the information contained in here.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: ConnectionInfo.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public class ConnectionInfo
-{
- /**
- * The used key exchange (KEX) algorithm in the latest key exchange.
- */
- public String keyExchangeAlgorithm;
-
- /**
- * The currently used crypto algorithm for packets from to the client to the
- * server.
- */
- public String clientToServerCryptoAlgorithm;
- /**
- * The currently used crypto algorithm for packets from to the server to the
- * client.
- */
- public String serverToClientCryptoAlgorithm;
-
- /**
- * The currently used MAC algorithm for packets from to the client to the
- * server.
- */
- public String clientToServerMACAlgorithm;
- /**
- * The currently used MAC algorithm for packets from to the server to the
- * client.
- */
- public String serverToClientMACAlgorithm;
-
- /**
- * The type of the server host key (currently either "ssh-dss" or
- * "ssh-rsa").
- */
- public String serverHostKeyAlgorithm;
- /**
- * The server host key that was sent during the latest key exchange.
- */
- public byte[] serverHostKey;
-
- /**
- * Number of kex exchanges performed on this connection so far.
- */
- public int keyExchangeCounter = 0;
-}
+
+package com.trilead.ssh2;
+
+/**
+ * In most cases you probably do not need the information contained in here.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: ConnectionInfo.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+public class ConnectionInfo
+{
+ /**
+ * The used key exchange (KEX) algorithm in the latest key exchange.
+ */
+ public String keyExchangeAlgorithm;
+
+ /**
+ * The currently used crypto algorithm for packets from to the client to the
+ * server.
+ */
+ public String clientToServerCryptoAlgorithm;
+ /**
+ * The currently used crypto algorithm for packets from to the server to the
+ * client.
+ */
+ public String serverToClientCryptoAlgorithm;
+
+ /**
+ * The currently used MAC algorithm for packets from to the client to the
+ * server.
+ */
+ public String clientToServerMACAlgorithm;
+ /**
+ * The currently used MAC algorithm for packets from to the server to the
+ * client.
+ */
+ public String serverToClientMACAlgorithm;
+
+ /**
+ * The type of the server host key (currently either "ssh-dss" or
+ * "ssh-rsa").
+ */
+ public String serverHostKeyAlgorithm;
+ /**
+ * The server host key that was sent during the latest key exchange.
+ */
+ public byte[] serverHostKey;
+
+ /**
+ * Number of kex exchanges performed on this connection so far.
+ */
+ public int keyExchangeCounter = 0;
+}
diff --git a/src/com/trilead/ssh2/ConnectionMonitor.java b/src/com/trilead/ssh2/ConnectionMonitor.java
index d378be5..2f96d25 100644
--- a/src/com/trilead/ssh2/ConnectionMonitor.java
+++ b/src/com/trilead/ssh2/ConnectionMonitor.java
@@ -1,34 +1,34 @@
-
-package com.trilead.ssh2;
-
-/**
- * A <code>ConnectionMonitor</code> is used to get notified when the
- * underlying socket of a connection is closed.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: ConnectionMonitor.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-
-public interface ConnectionMonitor
-{
- /**
- * This method is called after the connection's underlying
- * socket has been closed. E.g., due to the {@link Connection#close()} request of the
- * user, if the peer closed the connection, due to a fatal error during connect()
- * (also if the socket cannot be established) or if a fatal error occured on
- * an established connection.
- * <p>
- * This is an experimental feature.
- * <p>
- * You MUST NOT make any assumption about the thread that invokes this method.
- * <p>
- * <b>Please note: if the connection is not connected (e.g., there was no successful
- * connect() call), then the invocation of {@link Connection#close()} will NOT trigger
- * this method.</b>
- *
- * @see Connection#addConnectionMonitor(ConnectionMonitor)
- *
- * @param reason Includes an indication why the socket was closed.
- */
- public void connectionLost(Throwable reason);
+
+package com.trilead.ssh2;
+
+/**
+ * A <code>ConnectionMonitor</code> is used to get notified when the
+ * underlying socket of a connection is closed.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: ConnectionMonitor.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+
+public interface ConnectionMonitor
+{
+ /**
+ * This method is called after the connection's underlying
+ * socket has been closed. E.g., due to the {@link Connection#close()} request of the
+ * user, if the peer closed the connection, due to a fatal error during connect()
+ * (also if the socket cannot be established) or if a fatal error occured on
+ * an established connection.
+ * <p>
+ * This is an experimental feature.
+ * <p>
+ * You MUST NOT make any assumption about the thread that invokes this method.
+ * <p>
+ * <b>Please note: if the connection is not connected (e.g., there was no successful
+ * connect() call), then the invocation of {@link Connection#close()} will NOT trigger
+ * this method.</b>
+ *
+ * @see Connection#addConnectionMonitor(ConnectionMonitor)
+ *
+ * @param reason Includes an indication why the socket was closed.
+ */
+ public void connectionLost(Throwable reason);
} \ No newline at end of file
diff --git a/src/com/trilead/ssh2/DHGexParameters.java b/src/com/trilead/ssh2/DHGexParameters.java
index 94afb30..a2945ee 100644
--- a/src/com/trilead/ssh2/DHGexParameters.java
+++ b/src/com/trilead/ssh2/DHGexParameters.java
@@ -1,121 +1,121 @@
-
-package com.trilead.ssh2;
-
-/**
- * A <code>DHGexParameters</code> object can be used to specify parameters for
- * the diffie-hellman group exchange.
- * <p>
- * Depending on which constructor is used, either the use of a
- * <code>SSH_MSG_KEX_DH_GEX_REQUEST</code> or <code>SSH_MSG_KEX_DH_GEX_REQUEST_OLD</code>
- * can be forced.
- *
- * @see Connection#setDHGexParameters(DHGexParameters)
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: DHGexParameters.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-
-public class DHGexParameters
-{
- private final int min_group_len;
- private final int pref_group_len;
- private final int max_group_len;
-
- private static final int MIN_ALLOWED = 1024;
- private static final int MAX_ALLOWED = 8192;
-
- /**
- * Same as calling {@link #DHGexParameters(int, int, int) DHGexParameters(1024, 1024, 4096)}.
- * This is also the default used by the Connection class.
- *
- */
- public DHGexParameters()
- {
- this(1024, 1024, 4096);
- }
-
- /**
- * This constructor can be used to force the sending of a
- * <code>SSH_MSG_KEX_DH_GEX_REQUEST_OLD</code> request.
- * Internally, the minimum and maximum group lengths will
- * be set to zero.
- *
- * @param pref_group_len has to be &gt= 1024 and &lt;= 8192
- */
- public DHGexParameters(int pref_group_len)
- {
- if ((pref_group_len < MIN_ALLOWED) || (pref_group_len > MAX_ALLOWED))
- throw new IllegalArgumentException("pref_group_len out of range!");
-
- this.pref_group_len = pref_group_len;
- this.min_group_len = 0;
- this.max_group_len = 0;
- }
-
- /**
- * This constructor can be used to force the sending of a
- * <code>SSH_MSG_KEX_DH_GEX_REQUEST</code> request.
- * <p>
- * Note: older OpenSSH servers don't understand this request, in which
- * case you should use the {@link #DHGexParameters(int)} constructor.
- * <p>
- * All values have to be &gt= 1024 and &lt;= 8192. Furthermore,
- * min_group_len &lt;= pref_group_len &lt;= max_group_len.
- *
- * @param min_group_len
- * @param pref_group_len
- * @param max_group_len
- */
- public DHGexParameters(int min_group_len, int pref_group_len, int max_group_len)
- {
- if ((min_group_len < MIN_ALLOWED) || (min_group_len > MAX_ALLOWED))
- throw new IllegalArgumentException("min_group_len out of range!");
-
- if ((pref_group_len < MIN_ALLOWED) || (pref_group_len > MAX_ALLOWED))
- throw new IllegalArgumentException("pref_group_len out of range!");
-
- if ((max_group_len < MIN_ALLOWED) || (max_group_len > MAX_ALLOWED))
- throw new IllegalArgumentException("max_group_len out of range!");
-
- if ((pref_group_len < min_group_len) || (pref_group_len > max_group_len))
- throw new IllegalArgumentException("pref_group_len is incompatible with min and max!");
-
- if (max_group_len < min_group_len)
- throw new IllegalArgumentException("max_group_len must not be smaller than min_group_len!");
-
- this.min_group_len = min_group_len;
- this.pref_group_len = pref_group_len;
- this.max_group_len = max_group_len;
- }
-
- /**
- * Get the maximum group length.
- *
- * @return the maximum group length, may be <code>zero</code> if
- * SSH_MSG_KEX_DH_GEX_REQUEST_OLD should be requested
- */
- public int getMax_group_len()
- {
- return max_group_len;
- }
-
- /**
- * Get the minimum group length.
- *
- * @return minimum group length, may be <code>zero</code> if
- * SSH_MSG_KEX_DH_GEX_REQUEST_OLD should be requested
- */
- public int getMin_group_len()
- {
- return min_group_len;
- }
-
- /**
- * Get the preferred group length.
- *
- * @return the preferred group length
- */
- public int getPref_group_len()
- {
- return pref_group_len;
- }
-}
+
+package com.trilead.ssh2;
+
+/**
+ * A <code>DHGexParameters</code> object can be used to specify parameters for
+ * the diffie-hellman group exchange.
+ * <p>
+ * Depending on which constructor is used, either the use of a
+ * <code>SSH_MSG_KEX_DH_GEX_REQUEST</code> or <code>SSH_MSG_KEX_DH_GEX_REQUEST_OLD</code>
+ * can be forced.
+ *
+ * @see Connection#setDHGexParameters(DHGexParameters)
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: DHGexParameters.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+
+public class DHGexParameters
+{
+ private final int min_group_len;
+ private final int pref_group_len;
+ private final int max_group_len;
+
+ private static final int MIN_ALLOWED = 1024;
+ private static final int MAX_ALLOWED = 8192;
+
+ /**
+ * Same as calling {@link #DHGexParameters(int, int, int) DHGexParameters(1024, 1024, 4096)}.
+ * This is also the default used by the Connection class.
+ *
+ */
+ public DHGexParameters()
+ {
+ this(1024, 1024, 4096);
+ }
+
+ /**
+ * This constructor can be used to force the sending of a
+ * <code>SSH_MSG_KEX_DH_GEX_REQUEST_OLD</code> request.
+ * Internally, the minimum and maximum group lengths will
+ * be set to zero.
+ *
+ * @param pref_group_len has to be &gt= 1024 and &lt;= 8192
+ */
+ public DHGexParameters(int pref_group_len)
+ {
+ if ((pref_group_len < MIN_ALLOWED) || (pref_group_len > MAX_ALLOWED))
+ throw new IllegalArgumentException("pref_group_len out of range!");
+
+ this.pref_group_len = pref_group_len;
+ this.min_group_len = 0;
+ this.max_group_len = 0;
+ }
+
+ /**
+ * This constructor can be used to force the sending of a
+ * <code>SSH_MSG_KEX_DH_GEX_REQUEST</code> request.
+ * <p>
+ * Note: older OpenSSH servers don't understand this request, in which
+ * case you should use the {@link #DHGexParameters(int)} constructor.
+ * <p>
+ * All values have to be &gt= 1024 and &lt;= 8192. Furthermore,
+ * min_group_len &lt;= pref_group_len &lt;= max_group_len.
+ *
+ * @param min_group_len
+ * @param pref_group_len
+ * @param max_group_len
+ */
+ public DHGexParameters(int min_group_len, int pref_group_len, int max_group_len)
+ {
+ if ((min_group_len < MIN_ALLOWED) || (min_group_len > MAX_ALLOWED))
+ throw new IllegalArgumentException("min_group_len out of range!");
+
+ if ((pref_group_len < MIN_ALLOWED) || (pref_group_len > MAX_ALLOWED))
+ throw new IllegalArgumentException("pref_group_len out of range!");
+
+ if ((max_group_len < MIN_ALLOWED) || (max_group_len > MAX_ALLOWED))
+ throw new IllegalArgumentException("max_group_len out of range!");
+
+ if ((pref_group_len < min_group_len) || (pref_group_len > max_group_len))
+ throw new IllegalArgumentException("pref_group_len is incompatible with min and max!");
+
+ if (max_group_len < min_group_len)
+ throw new IllegalArgumentException("max_group_len must not be smaller than min_group_len!");
+
+ this.min_group_len = min_group_len;
+ this.pref_group_len = pref_group_len;
+ this.max_group_len = max_group_len;
+ }
+
+ /**
+ * Get the maximum group length.
+ *
+ * @return the maximum group length, may be <code>zero</code> if
+ * SSH_MSG_KEX_DH_GEX_REQUEST_OLD should be requested
+ */
+ public int getMax_group_len()
+ {
+ return max_group_len;
+ }
+
+ /**
+ * Get the minimum group length.
+ *
+ * @return minimum group length, may be <code>zero</code> if
+ * SSH_MSG_KEX_DH_GEX_REQUEST_OLD should be requested
+ */
+ public int getMin_group_len()
+ {
+ return min_group_len;
+ }
+
+ /**
+ * Get the preferred group length.
+ *
+ * @return the preferred group length
+ */
+ public int getPref_group_len()
+ {
+ return pref_group_len;
+ }
+}
diff --git a/src/com/trilead/ssh2/DebugLogger.java b/src/com/trilead/ssh2/DebugLogger.java
index 731fa28..adf2c8e 100644
--- a/src/com/trilead/ssh2/DebugLogger.java
+++ b/src/com/trilead/ssh2/DebugLogger.java
@@ -1,23 +1,23 @@
-package com.trilead.ssh2;
-
-/**
- * An interface which needs to be implemented if you
- * want to capture debugging messages.
- *
- * @see Connection#enableDebugging(boolean, DebugLogger)
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: DebugLogger.java,v 1.1 2008/03/03 07:01:36 cplattne Exp $
- */
-public interface DebugLogger
-{
-
-/**
- * Log a debug message.
- *
- * @param level 0-99, 99 is a the most verbose level
- * @param className the class that generated the message
- * @param message the debug message
- */
- public void log(int level, String className, String message);
-}
+package com.trilead.ssh2;
+
+/**
+ * An interface which needs to be implemented if you
+ * want to capture debugging messages.
+ *
+ * @see Connection#enableDebugging(boolean, DebugLogger)
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: DebugLogger.java,v 1.1 2008/03/03 07:01:36 cplattne Exp $
+ */
+public interface DebugLogger
+{
+
+/**
+ * Log a debug message.
+ *
+ * @param level 0-99, 99 is a the most verbose level
+ * @param className the class that generated the message
+ * @param message the debug message
+ */
+ public void log(int level, String className, String message);
+}
diff --git a/src/com/trilead/ssh2/HTTPProxyData.java b/src/com/trilead/ssh2/HTTPProxyData.java
index d99c28e..2edffe6 100644
--- a/src/com/trilead/ssh2/HTTPProxyData.java
+++ b/src/com/trilead/ssh2/HTTPProxyData.java
@@ -1,83 +1,83 @@
-
-package com.trilead.ssh2;
-
-/**
- * A <code>HTTPProxyData</code> object is used to specify the needed connection data
- * to connect through a HTTP proxy.
- *
- * @see Connection#setProxyData(ProxyData)
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: HTTPProxyData.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-
-public class HTTPProxyData implements ProxyData
-{
- public final String proxyHost;
- public final int proxyPort;
- public final String proxyUser;
- public final String proxyPass;
- public final String[] requestHeaderLines;
-
- /**
- * Same as calling {@link #HTTPProxyData(String, int, String, String) HTTPProxyData(proxyHost, proxyPort, <code>null</code>, <code>null</code>)}
- *
- * @param proxyHost Proxy hostname.
- * @param proxyPort Proxy port.
- */
- public HTTPProxyData(String proxyHost, int proxyPort)
- {
- this(proxyHost, proxyPort, null, null);
- }
-
- /**
- * Same as calling {@link #HTTPProxyData(String, int, String, String, String[]) HTTPProxyData(proxyHost, proxyPort, <code>null</code>, <code>null</code>, <code>null</code>)}
- *
- * @param proxyHost Proxy hostname.
- * @param proxyPort Proxy port.
- * @param proxyUser Username for basic authentication (<code>null</code> if no authentication is needed).
- * @param proxyPass Password for basic authentication (<code>null</code> if no authentication is needed).
- */
- public HTTPProxyData(String proxyHost, int proxyPort, String proxyUser, String proxyPass)
- {
- this(proxyHost, proxyPort, proxyUser, proxyPass, null);
- }
-
- /**
- * Connection data for a HTTP proxy. It is possible to specify a username and password
- * if the proxy requires basic authentication. Also, additional request header lines can
- * be specified (e.g., "User-Agent: CERN-LineMode/2.15 libwww/2.17b3").
- * <p>
- * Please note: if you want to use basic authentication, then both <code>proxyUser</code>
- * and <code>proxyPass</code> must be non-null.
- * <p>
- * Here is an example:
- * <p>
- * <code>
- * new HTTPProxyData("192.168.1.1", "3128", "proxyuser", "secret", new String[] {"User-Agent: TrileadBasedClient/1.0", "X-My-Proxy-Option: something"});
- * </code>
- *
- * @param proxyHost Proxy hostname.
- * @param proxyPort Proxy port.
- * @param proxyUser Username for basic authentication (<code>null</code> if no authentication is needed).
- * @param proxyPass Password for basic authentication (<code>null</code> if no authentication is needed).
- * @param requestHeaderLines An array with additional request header lines (without end-of-line markers)
- * that have to be sent to the server. May be <code>null</code>.
- */
-
- public HTTPProxyData(String proxyHost, int proxyPort, String proxyUser, String proxyPass,
- String[] requestHeaderLines)
- {
- if (proxyHost == null)
- throw new IllegalArgumentException("proxyHost must be non-null");
-
- if (proxyPort < 0)
- throw new IllegalArgumentException("proxyPort must be non-negative");
-
- this.proxyHost = proxyHost;
- this.proxyPort = proxyPort;
- this.proxyUser = proxyUser;
- this.proxyPass = proxyPass;
- this.requestHeaderLines = requestHeaderLines;
- }
-}
+
+package com.trilead.ssh2;
+
+/**
+ * A <code>HTTPProxyData</code> object is used to specify the needed connection data
+ * to connect through a HTTP proxy.
+ *
+ * @see Connection#setProxyData(ProxyData)
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: HTTPProxyData.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+
+public class HTTPProxyData implements ProxyData
+{
+ public final String proxyHost;
+ public final int proxyPort;
+ public final String proxyUser;
+ public final String proxyPass;
+ public final String[] requestHeaderLines;
+
+ /**
+ * Same as calling {@link #HTTPProxyData(String, int, String, String) HTTPProxyData(proxyHost, proxyPort, <code>null</code>, <code>null</code>)}
+ *
+ * @param proxyHost Proxy hostname.
+ * @param proxyPort Proxy port.
+ */
+ public HTTPProxyData(String proxyHost, int proxyPort)
+ {
+ this(proxyHost, proxyPort, null, null);
+ }
+
+ /**
+ * Same as calling {@link #HTTPProxyData(String, int, String, String, String[]) HTTPProxyData(proxyHost, proxyPort, <code>null</code>, <code>null</code>, <code>null</code>)}
+ *
+ * @param proxyHost Proxy hostname.
+ * @param proxyPort Proxy port.
+ * @param proxyUser Username for basic authentication (<code>null</code> if no authentication is needed).
+ * @param proxyPass Password for basic authentication (<code>null</code> if no authentication is needed).
+ */
+ public HTTPProxyData(String proxyHost, int proxyPort, String proxyUser, String proxyPass)
+ {
+ this(proxyHost, proxyPort, proxyUser, proxyPass, null);
+ }
+
+ /**
+ * Connection data for a HTTP proxy. It is possible to specify a username and password
+ * if the proxy requires basic authentication. Also, additional request header lines can
+ * be specified (e.g., "User-Agent: CERN-LineMode/2.15 libwww/2.17b3").
+ * <p>
+ * Please note: if you want to use basic authentication, then both <code>proxyUser</code>
+ * and <code>proxyPass</code> must be non-null.
+ * <p>
+ * Here is an example:
+ * <p>
+ * <code>
+ * new HTTPProxyData("192.168.1.1", "3128", "proxyuser", "secret", new String[] {"User-Agent: TrileadBasedClient/1.0", "X-My-Proxy-Option: something"});
+ * </code>
+ *
+ * @param proxyHost Proxy hostname.
+ * @param proxyPort Proxy port.
+ * @param proxyUser Username for basic authentication (<code>null</code> if no authentication is needed).
+ * @param proxyPass Password for basic authentication (<code>null</code> if no authentication is needed).
+ * @param requestHeaderLines An array with additional request header lines (without end-of-line markers)
+ * that have to be sent to the server. May be <code>null</code>.
+ */
+
+ public HTTPProxyData(String proxyHost, int proxyPort, String proxyUser, String proxyPass,
+ String[] requestHeaderLines)
+ {
+ if (proxyHost == null)
+ throw new IllegalArgumentException("proxyHost must be non-null");
+
+ if (proxyPort < 0)
+ throw new IllegalArgumentException("proxyPort must be non-negative");
+
+ this.proxyHost = proxyHost;
+ this.proxyPort = proxyPort;
+ this.proxyUser = proxyUser;
+ this.proxyPass = proxyPass;
+ this.requestHeaderLines = requestHeaderLines;
+ }
+}
diff --git a/src/com/trilead/ssh2/HTTPProxyException.java b/src/com/trilead/ssh2/HTTPProxyException.java
index 3447be2..4687dd1 100644
--- a/src/com/trilead/ssh2/HTTPProxyException.java
+++ b/src/com/trilead/ssh2/HTTPProxyException.java
@@ -1,29 +1,29 @@
-
-package com.trilead.ssh2;
-
-import java.io.IOException;
-
-/**
- * May be thrown upon connect() if a HTTP proxy is being used.
- *
- * @see Connection#connect()
- * @see Connection#setProxyData(ProxyData)
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: HTTPProxyException.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-
-public class HTTPProxyException extends IOException
-{
- private static final long serialVersionUID = 2241537397104426186L;
-
- public final String httpResponse;
- public final int httpErrorCode;
-
- public HTTPProxyException(String httpResponse, int httpErrorCode)
- {
- super("HTTP Proxy Error (" + httpErrorCode + " " + httpResponse + ")");
- this.httpResponse = httpResponse;
- this.httpErrorCode = httpErrorCode;
- }
-}
+
+package com.trilead.ssh2;
+
+import java.io.IOException;
+
+/**
+ * May be thrown upon connect() if a HTTP proxy is being used.
+ *
+ * @see Connection#connect()
+ * @see Connection#setProxyData(ProxyData)
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: HTTPProxyException.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+
+public class HTTPProxyException extends IOException
+{
+ private static final long serialVersionUID = 2241537397104426186L;
+
+ public final String httpResponse;
+ public final int httpErrorCode;
+
+ public HTTPProxyException(String httpResponse, int httpErrorCode)
+ {
+ super("HTTP Proxy Error (" + httpErrorCode + " " + httpResponse + ")");
+ this.httpResponse = httpResponse;
+ this.httpErrorCode = httpErrorCode;
+ }
+}
diff --git a/src/com/trilead/ssh2/InteractiveCallback.java b/src/com/trilead/ssh2/InteractiveCallback.java
index b81bb9f..3b83417 100644
--- a/src/com/trilead/ssh2/InteractiveCallback.java
+++ b/src/com/trilead/ssh2/InteractiveCallback.java
@@ -1,55 +1,55 @@
-
-package com.trilead.ssh2;
-
-/**
- * An <code>InteractiveCallback</code> is used to respond to challenges sent
- * by the server if authentication mode "keyboard-interactive" is selected.
- *
- * @see Connection#authenticateWithKeyboardInteractive(String,
- * String[], InteractiveCallback)
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: InteractiveCallback.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-
-public interface InteractiveCallback
-{
- /**
- * This callback interface is used during a "keyboard-interactive"
- * authentication. Every time the server sends a set of challenges (however,
- * most often just one challenge at a time), this callback function will be
- * called to give your application a chance to talk to the user and to
- * determine the response(s).
- * <p>
- * Some copy-paste information from the standard: a command line interface
- * (CLI) client SHOULD print the name and instruction (if non-empty), adding
- * newlines. Then for each prompt in turn, the client SHOULD display the
- * prompt and read the user input. The name and instruction fields MAY be
- * empty strings, the client MUST be prepared to handle this correctly. The
- * prompt field(s) MUST NOT be empty strings.
- * <p>
- * Please refer to draft-ietf-secsh-auth-kbdinteract-XX.txt for the details.
- * <p>
- * Note: clients SHOULD use control character filtering as discussed in
- * RFC4251 to avoid attacks by including
- * terminal control characters in the fields to be displayed.
- *
- * @param name
- * the name String sent by the server.
- * @param instruction
- * the instruction String sent by the server.
- * @param numPrompts
- * number of prompts - may be zero (in this case, you should just
- * return a String array of length zero).
- * @param prompt
- * an array (length <code>numPrompts</code>) of Strings
- * @param echo
- * an array (length <code>numPrompts</code>) of booleans. For
- * each prompt, the corresponding echo field indicates whether or
- * not the user input should be echoed as characters are typed.
- * @return an array of reponses - the array size must match the parameter
- * <code>numPrompts</code>.
- */
- public String[] replyToChallenge(String name, String instruction, int numPrompts, String[] prompt, boolean[] echo)
- throws Exception;
-}
+
+package com.trilead.ssh2;
+
+/**
+ * An <code>InteractiveCallback</code> is used to respond to challenges sent
+ * by the server if authentication mode "keyboard-interactive" is selected.
+ *
+ * @see Connection#authenticateWithKeyboardInteractive(String,
+ * String[], InteractiveCallback)
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: InteractiveCallback.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+
+public interface InteractiveCallback
+{
+ /**
+ * This callback interface is used during a "keyboard-interactive"
+ * authentication. Every time the server sends a set of challenges (however,
+ * most often just one challenge at a time), this callback function will be
+ * called to give your application a chance to talk to the user and to
+ * determine the response(s).
+ * <p>
+ * Some copy-paste information from the standard: a command line interface
+ * (CLI) client SHOULD print the name and instruction (if non-empty), adding
+ * newlines. Then for each prompt in turn, the client SHOULD display the
+ * prompt and read the user input. The name and instruction fields MAY be
+ * empty strings, the client MUST be prepared to handle this correctly. The
+ * prompt field(s) MUST NOT be empty strings.
+ * <p>
+ * Please refer to draft-ietf-secsh-auth-kbdinteract-XX.txt for the details.
+ * <p>
+ * Note: clients SHOULD use control character filtering as discussed in
+ * RFC4251 to avoid attacks by including
+ * terminal control characters in the fields to be displayed.
+ *
+ * @param name
+ * the name String sent by the server.
+ * @param instruction
+ * the instruction String sent by the server.
+ * @param numPrompts
+ * number of prompts - may be zero (in this case, you should just
+ * return a String array of length zero).
+ * @param prompt
+ * an array (length <code>numPrompts</code>) of Strings
+ * @param echo
+ * an array (length <code>numPrompts</code>) of booleans. For
+ * each prompt, the corresponding echo field indicates whether or
+ * not the user input should be echoed as characters are typed.
+ * @return an array of reponses - the array size must match the parameter
+ * <code>numPrompts</code>.
+ */
+ public String[] replyToChallenge(String name, String instruction, int numPrompts, String[] prompt, boolean[] echo)
+ throws Exception;
+}
diff --git a/src/com/trilead/ssh2/KnownHosts.java b/src/com/trilead/ssh2/KnownHosts.java
index edca0a2..ec022ff 100644
--- a/src/com/trilead/ssh2/KnownHosts.java
+++ b/src/com/trilead/ssh2/KnownHosts.java
@@ -1,844 +1,844 @@
-
-package com.trilead.ssh2;
-
-import java.io.BufferedReader;
-import java.io.CharArrayReader;
-import java.io.CharArrayWriter;
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.io.UnsupportedEncodingException;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.security.SecureRandom;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.Vector;
-
-import com.trilead.ssh2.crypto.Base64;
-import com.trilead.ssh2.crypto.digest.Digest;
-import com.trilead.ssh2.crypto.digest.HMAC;
-import com.trilead.ssh2.crypto.digest.MD5;
-import com.trilead.ssh2.crypto.digest.SHA1;
-import com.trilead.ssh2.signature.DSAPublicKey;
-import com.trilead.ssh2.signature.DSASHA1Verify;
-import com.trilead.ssh2.signature.RSAPublicKey;
-import com.trilead.ssh2.signature.RSASHA1Verify;
-
-
-/**
- * The <code>KnownHosts</code> class is a handy tool to verify received server hostkeys
- * based on the information in <code>known_hosts</code> files (the ones used by OpenSSH).
- * <p>
- * It offers basically an in-memory database for known_hosts entries, as well as some
- * helper functions. Entries from a <code>known_hosts</code> file can be loaded at construction time.
- * It is also possible to add more keys later (e.g., one can parse different
- * <code>known_hosts<code> files).
- * <p>
- * It is a thread safe implementation, therefore, you need only to instantiate one
- * <code>KnownHosts</code> for your whole application.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: KnownHosts.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
- */
-
-public class KnownHosts
-{
- public static final int HOSTKEY_IS_OK = 0;
- public static final int HOSTKEY_IS_NEW = 1;
- public static final int HOSTKEY_HAS_CHANGED = 2;
-
- private class KnownHostsEntry
- {
- String[] patterns;
- Object key;
-
- KnownHostsEntry(String[] patterns, Object key)
- {
- this.patterns = patterns;
- this.key = key;
- }
- }
-
- private LinkedList publicKeys = new LinkedList();
-
- public KnownHosts()
- {
- }
-
- public KnownHosts(char[] knownHostsData) throws IOException
- {
- initialize(knownHostsData);
- }
-
- public KnownHosts(File knownHosts) throws IOException
- {
- initialize(knownHosts);
- }
-
- /**
- * Adds a single public key entry to the database. Note: this will NOT add the public key
- * to any physical file (e.g., "~/.ssh/known_hosts") - use <code>addHostkeyToFile()</code> for that purpose.
- * This method is designed to be used in a {@link ServerHostKeyVerifier}.
- *
- * @param hostnames a list of hostname patterns - at least one most be specified. Check out the
- * OpenSSH sshd man page for a description of the pattern matching algorithm.
- * @param serverHostKeyAlgorithm as passed to the {@link ServerHostKeyVerifier}.
- * @param serverHostKey as passed to the {@link ServerHostKeyVerifier}.
- * @throws IOException
- */
- public void addHostkey(String hostnames[], String serverHostKeyAlgorithm, byte[] serverHostKey) throws IOException
- {
- if (hostnames == null)
- throw new IllegalArgumentException("hostnames may not be null");
-
- if ("ssh-rsa".equals(serverHostKeyAlgorithm))
- {
- RSAPublicKey rpk = RSASHA1Verify.decodeSSHRSAPublicKey(serverHostKey);
-
- synchronized (publicKeys)
- {
- publicKeys.add(new KnownHostsEntry(hostnames, rpk));
- }
- }
- else if ("ssh-dss".equals(serverHostKeyAlgorithm))
- {
- DSAPublicKey dpk = DSASHA1Verify.decodeSSHDSAPublicKey(serverHostKey);
-
- synchronized (publicKeys)
- {
- publicKeys.add(new KnownHostsEntry(hostnames, dpk));
- }
- }
- else
- throw new IOException("Unknwon host key type (" + serverHostKeyAlgorithm + ")");
- }
-
- /**
- * Parses the given known_hosts data and adds entries to the database.
- *
- * @param knownHostsData
- * @throws IOException
- */
- public void addHostkeys(char[] knownHostsData) throws IOException
- {
- initialize(knownHostsData);
- }
-
- /**
- * Parses the given known_hosts file and adds entries to the database.
- *
- * @param knownHosts
- * @throws IOException
- */
- public void addHostkeys(File knownHosts) throws IOException
- {
- initialize(knownHosts);
- }
-
- /**
- * Generate the hashed representation of the given hostname. Useful for adding entries
- * with hashed hostnames to a known_hosts file. (see -H option of OpenSSH key-gen).
- *
- * @param hostname
- * @return the hashed representation, e.g., "|1|cDhrv7zwEUV3k71CEPHnhHZezhA=|Xo+2y6rUXo2OIWRAYhBOIijbJMA="
- */
- public static final String createHashedHostname(String hostname)
- {
- SHA1 sha1 = new SHA1();
-
- byte[] salt = new byte[sha1.getDigestLength()];
-
- new SecureRandom().nextBytes(salt);
-
- byte[] hash = hmacSha1Hash(salt, hostname);
-
- String base64_salt = new String(Base64.encode(salt));
- String base64_hash = new String(Base64.encode(hash));
-
- return new String("|1|" + base64_salt + "|" + base64_hash);
- }
-
- private static final byte[] hmacSha1Hash(byte[] salt, String hostname)
- {
- SHA1 sha1 = new SHA1();
-
- if (salt.length != sha1.getDigestLength())
- throw new IllegalArgumentException("Salt has wrong length (" + salt.length + ")");
-
- HMAC hmac = new HMAC(sha1, salt, salt.length);
-
- try
- {
- hmac.update(hostname.getBytes("ISO-8859-1"));
- }catch(UnsupportedEncodingException ignore)
- {
- /* Actually, ISO-8859-1 is supported by all correct
- * Java implementations. But... you never know. */
- hmac.update(hostname.getBytes());
- }
-
- byte[] dig = new byte[hmac.getDigestLength()];
-
- hmac.digest(dig);
-
- return dig;
- }
-
- private final boolean checkHashed(String entry, String hostname)
- {
- if (entry.startsWith("|1|") == false)
- return false;
-
- int delim_idx = entry.indexOf('|', 3);
-
- if (delim_idx == -1)
- return false;
-
- String salt_base64 = entry.substring(3, delim_idx);
- String hash_base64 = entry.substring(delim_idx + 1);
-
- byte[] salt = null;
- byte[] hash = null;
-
- try
- {
- salt = Base64.decode(salt_base64.toCharArray());
- hash = Base64.decode(hash_base64.toCharArray());
- }
- catch (IOException e)
- {
- return false;
- }
-
- SHA1 sha1 = new SHA1();
-
- if (salt.length != sha1.getDigestLength())
- return false;
-
- byte[] dig = hmacSha1Hash(salt, hostname);
-
- for (int i = 0; i < dig.length; i++)
- if (dig[i] != hash[i])
- return false;
-
- return true;
- }
-
- private int checkKey(String remoteHostname, Object remoteKey)
- {
- int result = HOSTKEY_IS_NEW;
-
- synchronized (publicKeys)
- {
- Iterator i = publicKeys.iterator();
-
- while (i.hasNext())
- {
- KnownHostsEntry ke = (KnownHostsEntry) i.next();
-
- if (hostnameMatches(ke.patterns, remoteHostname) == false)
- continue;
-
- boolean res = matchKeys(ke.key, remoteKey);
-
- if (res == true)
- return HOSTKEY_IS_OK;
-
- result = HOSTKEY_HAS_CHANGED;
- }
- }
- return result;
- }
-
- private Vector getAllKeys(String hostname)
- {
- Vector keys = new Vector();
-
- synchronized (publicKeys)
- {
- Iterator i = publicKeys.iterator();
-
- while (i.hasNext())
- {
- KnownHostsEntry ke = (KnownHostsEntry) i.next();
-
- if (hostnameMatches(ke.patterns, hostname) == false)
- continue;
-
- keys.addElement(ke.key);
- }
- }
-
- return keys;
- }
-
- /**
- * Try to find the preferred order of hostkey algorithms for the given hostname.
- * Based on the type of hostkey that is present in the internal database
- * (i.e., either <code>ssh-rsa</code> or <code>ssh-dss</code>)
- * an ordered list of hostkey algorithms is returned which can be passed
- * to <code>Connection.setServerHostKeyAlgorithms</code>.
- *
- * @param hostname
- * @return <code>null</code> if no key for the given hostname is present or
- * there are keys of multiple types present for the given hostname. Otherwise,
- * an array with hostkey algorithms is returned (i.e., an array of length 2).
- */
- public String[] getPreferredServerHostkeyAlgorithmOrder(String hostname)
- {
- String[] algos = recommendHostkeyAlgorithms(hostname);
-
- if (algos != null)
- return algos;
-
- InetAddress[] ipAdresses = null;
-
- try
- {
- ipAdresses = InetAddress.getAllByName(hostname);
- }
- catch (UnknownHostException e)
- {
- return null;
- }
-
- for (int i = 0; i < ipAdresses.length; i++)
- {
- algos = recommendHostkeyAlgorithms(ipAdresses[i].getHostAddress());
-
- if (algos != null)
- return algos;
- }
-
- return null;
- }
-
- private final boolean hostnameMatches(String[] hostpatterns, String hostname)
- {
- boolean isMatch = false;
- boolean negate = false;
-
- hostname = hostname.toLowerCase();
-
- for (int k = 0; k < hostpatterns.length; k++)
- {
- if (hostpatterns[k] == null)
- continue;
-
- String pattern = null;
-
- /* In contrast to OpenSSH we also allow negated hash entries (as well as hashed
- * entries in lines with multiple entries).
- */
-
- if ((hostpatterns[k].length() > 0) && (hostpatterns[k].charAt(0) == '!'))
- {
- pattern = hostpatterns[k].substring(1);
- negate = true;
- }
- else
- {
- pattern = hostpatterns[k];
- negate = false;
- }
-
- /* Optimize, no need to check this entry */
-
- if ((isMatch) && (negate == false))
- continue;
-
- /* Now compare */
-
- if (pattern.charAt(0) == '|')
- {
- if (checkHashed(pattern, hostname))
- {
- if (negate)
- return false;
- isMatch = true;
- }
- }
- else
- {
- pattern = pattern.toLowerCase();
-
- if ((pattern.indexOf('?') != -1) || (pattern.indexOf('*') != -1))
- {
- if (pseudoRegex(pattern.toCharArray(), 0, hostname.toCharArray(), 0))
- {
- if (negate)
- return false;
- isMatch = true;
- }
- }
- else if (pattern.compareTo(hostname) == 0)
- {
- if (negate)
- return false;
- isMatch = true;
- }
- }
- }
-
- return isMatch;
- }
-
- private void initialize(char[] knownHostsData) throws IOException
- {
- BufferedReader br = new BufferedReader(new CharArrayReader(knownHostsData));
-
- while (true)
- {
- String line = br.readLine();
-
- if (line == null)
- break;
-
- line = line.trim();
-
- if (line.startsWith("#"))
- continue;
-
- String[] arr = line.split(" ");
-
- if (arr.length >= 3)
- {
- if ((arr[1].compareTo("ssh-rsa") == 0) || (arr[1].compareTo("ssh-dss") == 0))
- {
- String[] hostnames = arr[0].split(",");
-
- byte[] msg = Base64.decode(arr[2].toCharArray());
-
- addHostkey(hostnames, arr[1], msg);
- }
- }
- }
- }
-
- private void initialize(File knownHosts) throws IOException
- {
- char[] buff = new char[512];
-
- CharArrayWriter cw = new CharArrayWriter();
-
- knownHosts.createNewFile();
-
- FileReader fr = new FileReader(knownHosts);
-
- while (true)
- {
- int len = fr.read(buff);
- if (len < 0)
- break;
- cw.write(buff, 0, len);
- }
-
- fr.close();
-
- initialize(cw.toCharArray());
- }
-
- private final boolean matchKeys(Object key1, Object key2)
- {
- if ((key1 instanceof RSAPublicKey) && (key2 instanceof RSAPublicKey))
- {
- RSAPublicKey savedRSAKey = (RSAPublicKey) key1;
- RSAPublicKey remoteRSAKey = (RSAPublicKey) key2;
-
- if (savedRSAKey.getE().equals(remoteRSAKey.getE()) == false)
- return false;
-
- if (savedRSAKey.getN().equals(remoteRSAKey.getN()) == false)
- return false;
-
- return true;
- }
-
- if ((key1 instanceof DSAPublicKey) && (key2 instanceof DSAPublicKey))
- {
- DSAPublicKey savedDSAKey = (DSAPublicKey) key1;
- DSAPublicKey remoteDSAKey = (DSAPublicKey) key2;
-
- if (savedDSAKey.getG().equals(remoteDSAKey.getG()) == false)
- return false;
-
- if (savedDSAKey.getP().equals(remoteDSAKey.getP()) == false)
- return false;
-
- if (savedDSAKey.getQ().equals(remoteDSAKey.getQ()) == false)
- return false;
-
- if (savedDSAKey.getY().equals(remoteDSAKey.getY()) == false)
- return false;
-
- return true;
- }
-
- return false;
- }
-
- private final boolean pseudoRegex(char[] pattern, int i, char[] match, int j)
- {
- /* This matching logic is equivalent to the one present in OpenSSH 4.1 */
-
- while (true)
- {
- /* Are we at the end of the pattern? */
-
- if (pattern.length == i)
- return (match.length == j);
-
- if (pattern[i] == '*')
- {
- i++;
-
- if (pattern.length == i)
- return true;
-
- if ((pattern[i] != '*') && (pattern[i] != '?'))
- {
- while (true)
- {
- if ((pattern[i] == match[j]) && pseudoRegex(pattern, i + 1, match, j + 1))
- return true;
- j++;
- if (match.length == j)
- return false;
- }
- }
-
- while (true)
- {
- if (pseudoRegex(pattern, i, match, j))
- return true;
- j++;
- if (match.length == j)
- return false;
- }
- }
-
- if (match.length == j)
- return false;
-
- if ((pattern[i] != '?') && (pattern[i] != match[j]))
- return false;
-
- i++;
- j++;
- }
- }
-
- private String[] recommendHostkeyAlgorithms(String hostname)
- {
- String preferredAlgo = null;
-
- Vector keys = getAllKeys(hostname);
-
- for (int i = 0; i < keys.size(); i++)
- {
- String thisAlgo = null;
-
- if (keys.elementAt(i) instanceof RSAPublicKey)
- thisAlgo = "ssh-rsa";
- else if (keys.elementAt(i) instanceof DSAPublicKey)
- thisAlgo = "ssh-dss";
- else
- continue;
-
- if (preferredAlgo != null)
- {
- /* If we find different key types, then return null */
-
- if (preferredAlgo.compareTo(thisAlgo) != 0)
- return null;
-
- /* OK, we found the same algo again, optimize */
-
- continue;
- }
- }
-
- /* If we did not find anything that we know of, return null */
-
- if (preferredAlgo == null)
- return null;
-
- /* Now put the preferred algo to the start of the array.
- * You may ask yourself why we do it that way - basically, we could just
- * return only the preferred algorithm: since we have a saved key of that
- * type (sent earlier from the remote host), then that should work out.
- * However, imagine that the server is (for whatever reasons) not offering
- * that type of hostkey anymore (e.g., "ssh-rsa" was disabled and
- * now "ssh-dss" is being used). If we then do not let the server send us
- * a fresh key of the new type, then we shoot ourself into the foot:
- * the connection cannot be established and hence the user cannot decide
- * if he/she wants to accept the new key.
- */
-
- if (preferredAlgo.equals("ssh-rsa"))
- return new String[] { "ssh-rsa", "ssh-dss" };
-
- return new String[] { "ssh-dss", "ssh-rsa" };
- }
-
- /**
- * Checks the internal hostkey database for the given hostkey.
- * If no matching key can be found, then the hostname is resolved to an IP address
- * and the search is repeated using that IP address.
- *
- * @param hostname the server's hostname, will be matched with all hostname patterns
- * @param serverHostKeyAlgorithm type of hostkey, either <code>ssh-rsa</code> or <code>ssh-dss</code>
- * @param serverHostKey the key blob
- * @return <ul>
- * <li><code>HOSTKEY_IS_OK</code>: the given hostkey matches an entry for the given hostname</li>
- * <li><code>HOSTKEY_IS_NEW</code>: no entries found for this hostname and this type of hostkey</li>
- * <li><code>HOSTKEY_HAS_CHANGED</code>: hostname is known, but with another key of the same type
- * (man-in-the-middle attack?)</li>
- * </ul>
- * @throws IOException if the supplied key blob cannot be parsed or does not match the given hostkey type.
- */
- public int verifyHostkey(String hostname, String serverHostKeyAlgorithm, byte[] serverHostKey) throws IOException
- {
- Object remoteKey = null;
-
- if ("ssh-rsa".equals(serverHostKeyAlgorithm))
- {
- remoteKey = RSASHA1Verify.decodeSSHRSAPublicKey(serverHostKey);
- }
- else if ("ssh-dss".equals(serverHostKeyAlgorithm))
- {
- remoteKey = DSASHA1Verify.decodeSSHDSAPublicKey(serverHostKey);
- }
- else
- throw new IllegalArgumentException("Unknown hostkey type " + serverHostKeyAlgorithm);
-
- int result = checkKey(hostname, remoteKey);
-
- if (result == HOSTKEY_IS_OK)
- return result;
-
- InetAddress[] ipAdresses = null;
-
- try
- {
- ipAdresses = InetAddress.getAllByName(hostname);
- }
- catch (UnknownHostException e)
- {
- return result;
- }
-
- for (int i = 0; i < ipAdresses.length; i++)
- {
- int newresult = checkKey(ipAdresses[i].getHostAddress(), remoteKey);
-
- if (newresult == HOSTKEY_IS_OK)
- return newresult;
-
- if (newresult == HOSTKEY_HAS_CHANGED)
- result = HOSTKEY_HAS_CHANGED;
- }
-
- return result;
- }
-
- /**
- * Adds a single public key entry to the a known_hosts file.
- * This method is designed to be used in a {@link ServerHostKeyVerifier}.
- *
- * @param knownHosts the file where the publickey entry will be appended.
- * @param hostnames a list of hostname patterns - at least one most be specified. Check out the
- * OpenSSH sshd man page for a description of the pattern matching algorithm.
- * @param serverHostKeyAlgorithm as passed to the {@link ServerHostKeyVerifier}.
- * @param serverHostKey as passed to the {@link ServerHostKeyVerifier}.
- * @throws IOException
- */
- public final static void addHostkeyToFile(File knownHosts, String[] hostnames, String serverHostKeyAlgorithm,
- byte[] serverHostKey) throws IOException
- {
- if ((hostnames == null) || (hostnames.length == 0))
- throw new IllegalArgumentException("Need at least one hostname specification");
-
- if ((serverHostKeyAlgorithm == null) || (serverHostKey == null))
- throw new IllegalArgumentException();
-
- CharArrayWriter writer = new CharArrayWriter();
-
- for (int i = 0; i < hostnames.length; i++)
- {
- if (i != 0)
- writer.write(',');
- writer.write(hostnames[i]);
- }
-
- writer.write(' ');
- writer.write(serverHostKeyAlgorithm);
- writer.write(' ');
- writer.write(Base64.encode(serverHostKey));
- writer.write("\n");
-
- char[] entry = writer.toCharArray();
-
- RandomAccessFile raf = new RandomAccessFile(knownHosts, "rw");
-
- long len = raf.length();
-
- if (len > 0)
- {
- raf.seek(len - 1);
- int last = raf.read();
- if (last != '\n')
- raf.write('\n');
- }
-
- raf.write(new String(entry).getBytes("ISO-8859-1"));
- raf.close();
- }
-
- /**
- * Generates a "raw" fingerprint of a hostkey.
- *
- * @param type either "md5" or "sha1"
- * @param keyType either "ssh-rsa" or "ssh-dss"
- * @param hostkey the hostkey
- * @return the raw fingerprint
- */
- static final private byte[] rawFingerPrint(String type, String keyType, byte[] hostkey)
- {
- Digest dig = null;
-
- if ("md5".equals(type))
- {
- dig = new MD5();
- }
- else if ("sha1".equals(type))
- {
- dig = new SHA1();
- }
- else
- throw new IllegalArgumentException("Unknown hash type " + type);
-
- if ("ssh-rsa".equals(keyType))
- {
- }
- else if ("ssh-dss".equals(keyType))
- {
- }
- else
- throw new IllegalArgumentException("Unknown key type " + keyType);
-
- if (hostkey == null)
- throw new IllegalArgumentException("hostkey is null");
-
- dig.update(hostkey);
- byte[] res = new byte[dig.getDigestLength()];
- dig.digest(res);
- return res;
- }
-
- /**
- * Convert a raw fingerprint to hex representation (XX:YY:ZZ...).
- * @param fingerprint raw fingerprint
- * @return the hex representation
- */
- static final private String rawToHexFingerprint(byte[] fingerprint)
- {
- final char[] alpha = "0123456789abcdef".toCharArray();
-
- StringBuffer sb = new StringBuffer();
-
- for (int i = 0; i < fingerprint.length; i++)
- {
- if (i != 0)
- sb.append(':');
- int b = fingerprint[i] & 0xff;
- sb.append(alpha[b >> 4]);
- sb.append(alpha[b & 15]);
- }
-
- return sb.toString();
- }
-
- /**
- * Convert a raw fingerprint to bubblebabble representation.
- * @param raw raw fingerprint
- * @return the bubblebabble representation
- */
- static final private String rawToBubblebabbleFingerprint(byte[] raw)
- {
- final char[] v = "aeiouy".toCharArray();
- final char[] c = "bcdfghklmnprstvzx".toCharArray();
-
- StringBuffer sb = new StringBuffer();
-
- int seed = 1;
-
- int rounds = (raw.length / 2) + 1;
-
- sb.append('x');
-
- for (int i = 0; i < rounds; i++)
- {
- if (((i + 1) < rounds) || ((raw.length) % 2 != 0))
- {
- sb.append(v[(((raw[2 * i] >> 6) & 3) + seed) % 6]);
- sb.append(c[(raw[2 * i] >> 2) & 15]);
- sb.append(v[((raw[2 * i] & 3) + (seed / 6)) % 6]);
-
- if ((i + 1) < rounds)
- {
- sb.append(c[(((raw[(2 * i) + 1])) >> 4) & 15]);
- sb.append('-');
- sb.append(c[(((raw[(2 * i) + 1]))) & 15]);
- // As long as seed >= 0, seed will be >= 0 afterwards
- seed = ((seed * 5) + (((raw[2 * i] & 0xff) * 7) + (raw[(2 * i) + 1] & 0xff))) % 36;
- }
- }
- else
- {
- sb.append(v[seed % 6]); // seed >= 0, therefore index positive
- sb.append('x');
- sb.append(v[seed / 6]);
- }
- }
-
- sb.append('x');
-
- return sb.toString();
- }
-
- /**
- * Convert a ssh2 key-blob into a human readable hex fingerprint.
- * Generated fingerprints are identical to those generated by OpenSSH.
- * <p>
- * Example fingerprint: d0:cb:76:19:99:5a:03:fc:73:10:70:93:f2:44:63:47.
-
- * @param keytype either "ssh-rsa" or "ssh-dss"
- * @param publickey key blob
- * @return Hex fingerprint
- */
- public final static String createHexFingerprint(String keytype, byte[] publickey)
- {
- byte[] raw = rawFingerPrint("md5", keytype, publickey);
- return rawToHexFingerprint(raw);
- }
-
- /**
- * Convert a ssh2 key-blob into a human readable bubblebabble fingerprint.
- * The used bubblebabble algorithm (taken from OpenSSH) generates fingerprints
- * that are easier to remember for humans.
- * <p>
- * Example fingerprint: xofoc-bubuz-cazin-zufyl-pivuk-biduk-tacib-pybur-gonar-hotat-lyxux.
- *
- * @param keytype either "ssh-rsa" or "ssh-dss"
- * @param publickey key data
- * @return Bubblebabble fingerprint
- */
- public final static String createBubblebabbleFingerprint(String keytype, byte[] publickey)
- {
- byte[] raw = rawFingerPrint("sha1", keytype, publickey);
- return rawToBubblebabbleFingerprint(raw);
- }
-}
+
+package com.trilead.ssh2;
+
+import java.io.BufferedReader;
+import java.io.CharArrayReader;
+import java.io.CharArrayWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.io.UnsupportedEncodingException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.security.InvalidKeyException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Locale;
+import java.util.Vector;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+import com.trilead.ssh2.crypto.Base64;
+import com.trilead.ssh2.signature.DSASHA1Verify;
+import com.trilead.ssh2.signature.ECDSASHA2Verify;
+import com.trilead.ssh2.signature.RSASHA1Verify;
+
+
+/**
+ * The <code>KnownHosts</code> class is a handy tool to verify received server hostkeys
+ * based on the information in <code>known_hosts</code> files (the ones used by OpenSSH).
+ * <p>
+ * It offers basically an in-memory database for known_hosts entries, as well as some
+ * helper functions. Entries from a <code>known_hosts</code> file can be loaded at construction time.
+ * It is also possible to add more keys later (e.g., one can parse different
+ * <code>known_hosts<code> files).
+ * <p>
+ * It is a thread safe implementation, therefore, you need only to instantiate one
+ * <code>KnownHosts</code> for your whole application.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: KnownHosts.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
+ */
+
+public class KnownHosts
+{
+ public static final int HOSTKEY_IS_OK = 0;
+ public static final int HOSTKEY_IS_NEW = 1;
+ public static final int HOSTKEY_HAS_CHANGED = 2;
+
+ private class KnownHostsEntry
+ {
+ String[] patterns;
+ PublicKey key;
+
+ KnownHostsEntry(String[] patterns, PublicKey key)
+ {
+ this.patterns = patterns;
+ this.key = key;
+ }
+ }
+
+ private LinkedList<KnownHostsEntry> publicKeys = new LinkedList<KnownHostsEntry>();
+
+ public KnownHosts()
+ {
+ }
+
+ public KnownHosts(char[] knownHostsData) throws IOException
+ {
+ initialize(knownHostsData);
+ }
+
+ public KnownHosts(File knownHosts) throws IOException
+ {
+ initialize(knownHosts);
+ }
+
+ /**
+ * Adds a single public key entry to the database. Note: this will NOT add the public key
+ * to any physical file (e.g., "~/.ssh/known_hosts") - use <code>addHostkeyToFile()</code> for that purpose.
+ * This method is designed to be used in a {@link ServerHostKeyVerifier}.
+ *
+ * @param hostnames a list of hostname patterns - at least one most be specified. Check out the
+ * OpenSSH sshd man page for a description of the pattern matching algorithm.
+ * @param serverHostKeyAlgorithm as passed to the {@link ServerHostKeyVerifier}.
+ * @param serverHostKey as passed to the {@link ServerHostKeyVerifier}.
+ * @throws IOException
+ */
+ public void addHostkey(String hostnames[], String serverHostKeyAlgorithm, byte[] serverHostKey) throws IOException
+ {
+ if (hostnames == null)
+ throw new IllegalArgumentException("hostnames may not be null");
+
+ if ("ssh-rsa".equals(serverHostKeyAlgorithm))
+ {
+ RSAPublicKey rpk = RSASHA1Verify.decodeSSHRSAPublicKey(serverHostKey);
+
+ synchronized (publicKeys)
+ {
+ publicKeys.add(new KnownHostsEntry(hostnames, rpk));
+ }
+ }
+ else if ("ssh-dss".equals(serverHostKeyAlgorithm))
+ {
+ DSAPublicKey dpk = DSASHA1Verify.decodeSSHDSAPublicKey(serverHostKey);
+
+ synchronized (publicKeys)
+ {
+ publicKeys.add(new KnownHostsEntry(hostnames, dpk));
+ }
+ }
+ else if (serverHostKeyAlgorithm.startsWith(ECDSASHA2Verify.ECDSA_SHA2_PREFIX))
+ {
+ ECPublicKey epk = ECDSASHA2Verify.decodeSSHECDSAPublicKey(serverHostKey);
+
+ synchronized (publicKeys) {
+ publicKeys.add(new KnownHostsEntry(hostnames, epk));
+ }
+ }
+ else
+ throw new IOException("Unknwon host key type (" + serverHostKeyAlgorithm + ")");
+ }
+
+ /**
+ * Parses the given known_hosts data and adds entries to the database.
+ *
+ * @param knownHostsData
+ * @throws IOException
+ */
+ public void addHostkeys(char[] knownHostsData) throws IOException
+ {
+ initialize(knownHostsData);
+ }
+
+ /**
+ * Parses the given known_hosts file and adds entries to the database.
+ *
+ * @param knownHosts
+ * @throws IOException
+ */
+ public void addHostkeys(File knownHosts) throws IOException
+ {
+ initialize(knownHosts);
+ }
+
+ /**
+ * Generate the hashed representation of the given hostname. Useful for adding entries
+ * with hashed hostnames to a known_hosts file. (see -H option of OpenSSH key-gen).
+ *
+ * @param hostname
+ * @return the hashed representation, e.g., "|1|cDhrv7zwEUV3k71CEPHnhHZezhA=|Xo+2y6rUXo2OIWRAYhBOIijbJMA="
+ */
+ public static final String createHashedHostname(String hostname)
+ {
+ MessageDigest sha1;
+ try {
+ sha1 = MessageDigest.getInstance("SHA1");
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException("VM doesn't support SHA1", e);
+ }
+
+ byte[] salt = new byte[sha1.getDigestLength()];
+
+ new SecureRandom().nextBytes(salt);
+
+ byte[] hash = hmacSha1Hash(salt, hostname);
+
+ String base64_salt = new String(Base64.encode(salt));
+ String base64_hash = new String(Base64.encode(hash));
+
+ return new String("|1|" + base64_salt + "|" + base64_hash);
+ }
+
+ private static final byte[] hmacSha1Hash(byte[] salt, String hostname)
+ {
+ Mac hmac;
+ try {
+ hmac = Mac.getInstance("HmacSHA1");
+ if (salt.length != hmac.getMacLength())
+ throw new IllegalArgumentException("Salt has wrong length (" + salt.length + ")");
+ hmac.init(new SecretKeySpec(salt, "HmacSHA1"));
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException("Unable to HMAC-SHA1", e);
+ } catch (InvalidKeyException e) {
+ throw new RuntimeException("Unable to create SecretKey", e);
+ }
+
+ try
+ {
+ hmac.update(hostname.getBytes("ISO-8859-1"));
+ }catch(UnsupportedEncodingException ignore)
+ {
+ /* Actually, ISO-8859-1 is supported by all correct
+ * Java implementations. But... you never know. */
+ hmac.update(hostname.getBytes());
+ }
+
+ return hmac.doFinal();
+ }
+
+ private final boolean checkHashed(String entry, String hostname)
+ {
+ if (entry.startsWith("|1|") == false)
+ return false;
+
+ int delim_idx = entry.indexOf('|', 3);
+
+ if (delim_idx == -1)
+ return false;
+
+ String salt_base64 = entry.substring(3, delim_idx);
+ String hash_base64 = entry.substring(delim_idx + 1);
+
+ byte[] salt = null;
+ byte[] hash = null;
+
+ try
+ {
+ salt = Base64.decode(salt_base64.toCharArray());
+ hash = Base64.decode(hash_base64.toCharArray());
+ }
+ catch (IOException e)
+ {
+ return false;
+ }
+
+ try {
+ MessageDigest sha1 = MessageDigest.getInstance("SHA1");
+ if (salt.length != sha1.getDigestLength())
+ return false;
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException("VM does not support SHA1", e);
+ }
+
+ byte[] dig = hmacSha1Hash(salt, hostname);
+
+ for (int i = 0; i < dig.length; i++)
+ if (dig[i] != hash[i])
+ return false;
+
+ return true;
+ }
+
+ private int checkKey(String remoteHostname, PublicKey remoteKey)
+ {
+ int result = HOSTKEY_IS_NEW;
+
+ synchronized (publicKeys)
+ {
+ Iterator<KnownHostsEntry> i = publicKeys.iterator();
+
+ while (i.hasNext())
+ {
+ KnownHostsEntry ke = i.next();
+
+ if (hostnameMatches(ke.patterns, remoteHostname) == false)
+ continue;
+
+ boolean res = matchKeys(ke.key, remoteKey);
+
+ if (res == true)
+ return HOSTKEY_IS_OK;
+
+ result = HOSTKEY_HAS_CHANGED;
+ }
+ }
+ return result;
+ }
+
+ private Vector<PublicKey> getAllKeys(String hostname)
+ {
+ Vector<PublicKey> keys = new Vector<PublicKey>();
+
+ synchronized (publicKeys)
+ {
+ Iterator<KnownHostsEntry> i = publicKeys.iterator();
+
+ while (i.hasNext())
+ {
+ KnownHostsEntry ke = i.next();
+
+ if (hostnameMatches(ke.patterns, hostname) == false)
+ continue;
+
+ keys.addElement(ke.key);
+ }
+ }
+
+ return keys;
+ }
+
+ /**
+ * Try to find the preferred order of hostkey algorithms for the given hostname.
+ * Based on the type of hostkey that is present in the internal database
+ * (i.e., either <code>ssh-rsa</code> or <code>ssh-dss</code>)
+ * an ordered list of hostkey algorithms is returned which can be passed
+ * to <code>Connection.setServerHostKeyAlgorithms</code>.
+ *
+ * @param hostname
+ * @return <code>null</code> if no key for the given hostname is present or
+ * there are keys of multiple types present for the given hostname. Otherwise,
+ * an array with hostkey algorithms is returned (i.e., an array of length 2).
+ */
+ public String[] getPreferredServerHostkeyAlgorithmOrder(String hostname)
+ {
+ String[] algos = recommendHostkeyAlgorithms(hostname);
+
+ if (algos != null)
+ return algos;
+
+ InetAddress[] ipAdresses = null;
+
+ try
+ {
+ ipAdresses = InetAddress.getAllByName(hostname);
+ }
+ catch (UnknownHostException e)
+ {
+ return null;
+ }
+
+ for (int i = 0; i < ipAdresses.length; i++)
+ {
+ algos = recommendHostkeyAlgorithms(ipAdresses[i].getHostAddress());
+
+ if (algos != null)
+ return algos;
+ }
+
+ return null;
+ }
+
+ private final boolean hostnameMatches(String[] hostpatterns, String hostname)
+ {
+ boolean isMatch = false;
+ boolean negate = false;
+
+ hostname = hostname.toLowerCase(Locale.US);
+
+ for (int k = 0; k < hostpatterns.length; k++)
+ {
+ if (hostpatterns[k] == null)
+ continue;
+
+ String pattern = null;
+
+ /* In contrast to OpenSSH we also allow negated hash entries (as well as hashed
+ * entries in lines with multiple entries).
+ */
+
+ if ((hostpatterns[k].length() > 0) && (hostpatterns[k].charAt(0) == '!'))
+ {
+ pattern = hostpatterns[k].substring(1);
+ negate = true;
+ }
+ else
+ {
+ pattern = hostpatterns[k];
+ negate = false;
+ }
+
+ /* Optimize, no need to check this entry */
+
+ if ((isMatch) && (negate == false))
+ continue;
+
+ /* Now compare */
+
+ if (pattern.charAt(0) == '|')
+ {
+ if (checkHashed(pattern, hostname))
+ {
+ if (negate)
+ return false;
+ isMatch = true;
+ }
+ }
+ else
+ {
+ pattern = pattern.toLowerCase(Locale.US);
+
+ if ((pattern.indexOf('?') != -1) || (pattern.indexOf('*') != -1))
+ {
+ if (pseudoRegex(pattern.toCharArray(), 0, hostname.toCharArray(), 0))
+ {
+ if (negate)
+ return false;
+ isMatch = true;
+ }
+ }
+ else if (pattern.compareTo(hostname) == 0)
+ {
+ if (negate)
+ return false;
+ isMatch = true;
+ }
+ }
+ }
+
+ return isMatch;
+ }
+
+ private void initialize(char[] knownHostsData) throws IOException
+ {
+ BufferedReader br = new BufferedReader(new CharArrayReader(knownHostsData));
+
+ while (true)
+ {
+ String line = br.readLine();
+
+ if (line == null)
+ break;
+
+ line = line.trim();
+
+ if (line.startsWith("#"))
+ continue;
+
+ String[] arr = line.split(" ");
+
+ if (arr.length >= 3)
+ {
+ if ((arr[1].compareTo("ssh-rsa") == 0) || (arr[1].compareTo("ssh-dss") == 0))
+ {
+ String[] hostnames = arr[0].split(",");
+
+ byte[] msg = Base64.decode(arr[2].toCharArray());
+
+ addHostkey(hostnames, arr[1], msg);
+ }
+ }
+ }
+ }
+
+ private void initialize(File knownHosts) throws IOException
+ {
+ char[] buff = new char[512];
+
+ CharArrayWriter cw = new CharArrayWriter();
+
+ knownHosts.createNewFile();
+
+ FileReader fr = new FileReader(knownHosts);
+
+ while (true)
+ {
+ int len = fr.read(buff);
+ if (len < 0)
+ break;
+ cw.write(buff, 0, len);
+ }
+
+ fr.close();
+
+ initialize(cw.toCharArray());
+ }
+
+ private final boolean matchKeys(PublicKey key1, PublicKey key2)
+ {
+ return key1.equals(key2);
+ }
+
+ private final boolean pseudoRegex(char[] pattern, int i, char[] match, int j)
+ {
+ /* This matching logic is equivalent to the one present in OpenSSH 4.1 */
+
+ while (true)
+ {
+ /* Are we at the end of the pattern? */
+
+ if (pattern.length == i)
+ return (match.length == j);
+
+ if (pattern[i] == '*')
+ {
+ i++;
+
+ if (pattern.length == i)
+ return true;
+
+ if ((pattern[i] != '*') && (pattern[i] != '?'))
+ {
+ while (true)
+ {
+ if ((pattern[i] == match[j]) && pseudoRegex(pattern, i + 1, match, j + 1))
+ return true;
+ j++;
+ if (match.length == j)
+ return false;
+ }
+ }
+
+ while (true)
+ {
+ if (pseudoRegex(pattern, i, match, j))
+ return true;
+ j++;
+ if (match.length == j)
+ return false;
+ }
+ }
+
+ if (match.length == j)
+ return false;
+
+ if ((pattern[i] != '?') && (pattern[i] != match[j]))
+ return false;
+
+ i++;
+ j++;
+ }
+ }
+
+ private String[] recommendHostkeyAlgorithms(String hostname)
+ {
+ String preferredAlgo = null;
+
+ Vector<PublicKey> keys = getAllKeys(hostname);
+
+ for (int i = 0; i < keys.size(); i++)
+ {
+ String thisAlgo = null;
+
+ if (keys.elementAt(i) instanceof RSAPublicKey)
+ thisAlgo = "ssh-rsa";
+ else if (keys.elementAt(i) instanceof DSAPublicKey)
+ thisAlgo = "ssh-dss";
+ else
+ continue;
+
+ if (preferredAlgo != null)
+ {
+ /* If we find different key types, then return null */
+
+ if (preferredAlgo.compareTo(thisAlgo) != 0)
+ return null;
+
+ /* OK, we found the same algo again, optimize */
+
+ continue;
+ }
+ }
+
+ /* If we did not find anything that we know of, return null */
+
+ if (preferredAlgo == null)
+ return null;
+
+ /* Now put the preferred algo to the start of the array.
+ * You may ask yourself why we do it that way - basically, we could just
+ * return only the preferred algorithm: since we have a saved key of that
+ * type (sent earlier from the remote host), then that should work out.
+ * However, imagine that the server is (for whatever reasons) not offering
+ * that type of hostkey anymore (e.g., "ssh-rsa" was disabled and
+ * now "ssh-dss" is being used). If we then do not let the server send us
+ * a fresh key of the new type, then we shoot ourself into the foot:
+ * the connection cannot be established and hence the user cannot decide
+ * if he/she wants to accept the new key.
+ */
+
+ if (preferredAlgo.equals("ssh-rsa"))
+ return new String[] { "ssh-rsa", "ssh-dss" };
+
+ return new String[] { "ssh-dss", "ssh-rsa" };
+ }
+
+ /**
+ * Checks the internal hostkey database for the given hostkey.
+ * If no matching key can be found, then the hostname is resolved to an IP address
+ * and the search is repeated using that IP address.
+ *
+ * @param hostname the server's hostname, will be matched with all hostname patterns
+ * @param serverHostKeyAlgorithm type of hostkey, either <code>ssh-rsa</code> or <code>ssh-dss</code>
+ * @param serverHostKey the key blob
+ * @return <ul>
+ * <li><code>HOSTKEY_IS_OK</code>: the given hostkey matches an entry for the given hostname</li>
+ * <li><code>HOSTKEY_IS_NEW</code>: no entries found for this hostname and this type of hostkey</li>
+ * <li><code>HOSTKEY_HAS_CHANGED</code>: hostname is known, but with another key of the same type
+ * (man-in-the-middle attack?)</li>
+ * </ul>
+ * @throws IOException if the supplied key blob cannot be parsed or does not match the given hostkey type.
+ */
+ public int verifyHostkey(String hostname, String serverHostKeyAlgorithm, byte[] serverHostKey) throws IOException
+ {
+ PublicKey remoteKey = null;
+
+ if ("ssh-rsa".equals(serverHostKeyAlgorithm))
+ {
+ remoteKey = RSASHA1Verify.decodeSSHRSAPublicKey(serverHostKey);
+ }
+ else if ("ssh-dss".equals(serverHostKeyAlgorithm))
+ {
+ remoteKey = DSASHA1Verify.decodeSSHDSAPublicKey(serverHostKey);
+ }
+ else if (serverHostKeyAlgorithm.startsWith("ecdsa-sha2-"))
+ {
+ remoteKey = ECDSASHA2Verify.decodeSSHECDSAPublicKey(serverHostKey);
+ }
+ else
+ throw new IllegalArgumentException("Unknown hostkey type " + serverHostKeyAlgorithm);
+
+ int result = checkKey(hostname, remoteKey);
+
+ if (result == HOSTKEY_IS_OK)
+ return result;
+
+ InetAddress[] ipAdresses = null;
+
+ try
+ {
+ ipAdresses = InetAddress.getAllByName(hostname);
+ }
+ catch (UnknownHostException e)
+ {
+ return result;
+ }
+
+ for (int i = 0; i < ipAdresses.length; i++)
+ {
+ int newresult = checkKey(ipAdresses[i].getHostAddress(), remoteKey);
+
+ if (newresult == HOSTKEY_IS_OK)
+ return newresult;
+
+ if (newresult == HOSTKEY_HAS_CHANGED)
+ result = HOSTKEY_HAS_CHANGED;
+ }
+
+ return result;
+ }
+
+ /**
+ * Adds a single public key entry to the a known_hosts file.
+ * This method is designed to be used in a {@link ServerHostKeyVerifier}.
+ *
+ * @param knownHosts the file where the publickey entry will be appended.
+ * @param hostnames a list of hostname patterns - at least one most be specified. Check out the
+ * OpenSSH sshd man page for a description of the pattern matching algorithm.
+ * @param serverHostKeyAlgorithm as passed to the {@link ServerHostKeyVerifier}.
+ * @param serverHostKey as passed to the {@link ServerHostKeyVerifier}.
+ * @throws IOException
+ */
+ public final static void addHostkeyToFile(File knownHosts, String[] hostnames, String serverHostKeyAlgorithm,
+ byte[] serverHostKey) throws IOException
+ {
+ if ((hostnames == null) || (hostnames.length == 0))
+ throw new IllegalArgumentException("Need at least one hostname specification");
+
+ if ((serverHostKeyAlgorithm == null) || (serverHostKey == null))
+ throw new IllegalArgumentException();
+
+ CharArrayWriter writer = new CharArrayWriter();
+
+ for (int i = 0; i < hostnames.length; i++)
+ {
+ if (i != 0)
+ writer.write(',');
+ writer.write(hostnames[i]);
+ }
+
+ writer.write(' ');
+ writer.write(serverHostKeyAlgorithm);
+ writer.write(' ');
+ writer.write(Base64.encode(serverHostKey));
+ writer.write("\n");
+
+ char[] entry = writer.toCharArray();
+
+ RandomAccessFile raf = new RandomAccessFile(knownHosts, "rw");
+
+ long len = raf.length();
+
+ if (len > 0)
+ {
+ raf.seek(len - 1);
+ int last = raf.read();
+ if (last != '\n')
+ raf.write('\n');
+ }
+
+ raf.write(new String(entry).getBytes("ISO-8859-1"));
+ raf.close();
+ }
+
+ /**
+ * Generates a "raw" fingerprint of a hostkey.
+ *
+ * @param type either "md5" or "sha1"
+ * @param keyType either "ssh-rsa" or "ssh-dss"
+ * @param hostkey the hostkey
+ * @return the raw fingerprint
+ */
+ static final private byte[] rawFingerPrint(String type, String keyType, byte[] hostkey)
+ {
+ MessageDigest dig = null;
+
+ try {
+ if ("md5".equals(type))
+ {
+ dig = MessageDigest.getInstance("MD5");
+ }
+ else if ("sha1".equals(type))
+ {
+ dig = MessageDigest.getInstance("SHA1");
+ }
+ else
+ {
+ throw new IllegalArgumentException("Unknown hash type " + type);
+ }
+ } catch (NoSuchAlgorithmException e) {
+ throw new IllegalArgumentException("Unknown hash type " + type);
+ }
+
+ if (keyType.startsWith("ecdsa-sha2-"))
+ {
+ }
+ else if ("ssh-rsa".equals(keyType))
+ {
+ }
+ else if ("ssh-dss".equals(keyType))
+ {
+ }
+ else
+ throw new IllegalArgumentException("Unknown key type " + keyType);
+
+ if (hostkey == null)
+ throw new IllegalArgumentException("hostkey is null");
+
+ dig.update(hostkey);
+ return dig.digest();
+ }
+
+ /**
+ * Convert a raw fingerprint to hex representation (XX:YY:ZZ...).
+ * @param fingerprint raw fingerprint
+ * @return the hex representation
+ */
+ static final private String rawToHexFingerprint(byte[] fingerprint)
+ {
+ final char[] alpha = "0123456789abcdef".toCharArray();
+
+ StringBuffer sb = new StringBuffer();
+
+ for (int i = 0; i < fingerprint.length; i++)
+ {
+ if (i != 0)
+ sb.append(':');
+ int b = fingerprint[i] & 0xff;
+ sb.append(alpha[b >> 4]);
+ sb.append(alpha[b & 15]);
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Convert a raw fingerprint to bubblebabble representation.
+ * @param raw raw fingerprint
+ * @return the bubblebabble representation
+ */
+ static final private String rawToBubblebabbleFingerprint(byte[] raw)
+ {
+ final char[] v = "aeiouy".toCharArray();
+ final char[] c = "bcdfghklmnprstvzx".toCharArray();
+
+ StringBuffer sb = new StringBuffer();
+
+ int seed = 1;
+
+ int rounds = (raw.length / 2) + 1;
+
+ sb.append('x');
+
+ for (int i = 0; i < rounds; i++)
+ {
+ if (((i + 1) < rounds) || ((raw.length) % 2 != 0))
+ {
+ sb.append(v[(((raw[2 * i] >> 6) & 3) + seed) % 6]);
+ sb.append(c[(raw[2 * i] >> 2) & 15]);
+ sb.append(v[((raw[2 * i] & 3) + (seed / 6)) % 6]);
+
+ if ((i + 1) < rounds)
+ {
+ sb.append(c[(((raw[(2 * i) + 1])) >> 4) & 15]);
+ sb.append('-');
+ sb.append(c[(((raw[(2 * i) + 1]))) & 15]);
+ // As long as seed >= 0, seed will be >= 0 afterwards
+ seed = ((seed * 5) + (((raw[2 * i] & 0xff) * 7) + (raw[(2 * i) + 1] & 0xff))) % 36;
+ }
+ }
+ else
+ {
+ sb.append(v[seed % 6]); // seed >= 0, therefore index positive
+ sb.append('x');
+ sb.append(v[seed / 6]);
+ }
+ }
+
+ sb.append('x');
+
+ return sb.toString();
+ }
+
+ /**
+ * Convert a ssh2 key-blob into a human readable hex fingerprint.
+ * Generated fingerprints are identical to those generated by OpenSSH.
+ * <p>
+ * Example fingerprint: d0:cb:76:19:99:5a:03:fc:73:10:70:93:f2:44:63:47.
+
+ * @param keytype either "ssh-rsa" or "ssh-dss"
+ * @param publickey key blob
+ * @return Hex fingerprint
+ */
+ public final static String createHexFingerprint(String keytype, byte[] publickey)
+ {
+ byte[] raw = rawFingerPrint("md5", keytype, publickey);
+ return rawToHexFingerprint(raw);
+ }
+
+ /**
+ * Convert a ssh2 key-blob into a human readable bubblebabble fingerprint.
+ * The used bubblebabble algorithm (taken from OpenSSH) generates fingerprints
+ * that are easier to remember for humans.
+ * <p>
+ * Example fingerprint: xofoc-bubuz-cazin-zufyl-pivuk-biduk-tacib-pybur-gonar-hotat-lyxux.
+ *
+ * @param keytype either "ssh-rsa" or "ssh-dss"
+ * @param publickey key data
+ * @return Bubblebabble fingerprint
+ */
+ public final static String createBubblebabbleFingerprint(String keytype, byte[] publickey)
+ {
+ byte[] raw = rawFingerPrint("sha1", keytype, publickey);
+ return rawToBubblebabbleFingerprint(raw);
+ }
+}
diff --git a/src/com/trilead/ssh2/LocalPortForwarder.java b/src/com/trilead/ssh2/LocalPortForwarder.java
index c3183cb..96fa082 100644
--- a/src/com/trilead/ssh2/LocalPortForwarder.java
+++ b/src/com/trilead/ssh2/LocalPortForwarder.java
@@ -1,63 +1,63 @@
-
-package com.trilead.ssh2;
-
-import java.io.IOException;
-import java.net.InetSocketAddress;
-
-import com.trilead.ssh2.channel.ChannelManager;
-import com.trilead.ssh2.channel.LocalAcceptThread;
-
-
-/**
- * A <code>LocalPortForwarder</code> forwards TCP/IP connections to a local
- * port via the secure tunnel to another host (which may or may not be identical
- * to the remote SSH-2 server). Checkout {@link Connection#createLocalPortForwarder(int, String, int)}
- * on how to create one.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: LocalPortForwarder.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public class LocalPortForwarder
-{
- ChannelManager cm;
-
- String host_to_connect;
-
- int port_to_connect;
-
- LocalAcceptThread lat;
-
- LocalPortForwarder(ChannelManager cm, int local_port, String host_to_connect, int port_to_connect)
- throws IOException
- {
- this.cm = cm;
- this.host_to_connect = host_to_connect;
- this.port_to_connect = port_to_connect;
-
- lat = new LocalAcceptThread(cm, local_port, host_to_connect, port_to_connect);
- lat.setDaemon(true);
- lat.start();
- }
-
- LocalPortForwarder(ChannelManager cm, InetSocketAddress addr, String host_to_connect, int port_to_connect)
- throws IOException
- {
- this.cm = cm;
- this.host_to_connect = host_to_connect;
- this.port_to_connect = port_to_connect;
-
- lat = new LocalAcceptThread(cm, addr, host_to_connect, port_to_connect);
- lat.setDaemon(true);
- lat.start();
- }
-
- /**
- * Stop TCP/IP forwarding of newly arriving connections.
- *
- * @throws IOException
- */
- public void close() throws IOException
- {
- lat.stopWorking();
- }
-}
+
+package com.trilead.ssh2;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+import com.trilead.ssh2.channel.ChannelManager;
+import com.trilead.ssh2.channel.LocalAcceptThread;
+
+
+/**
+ * A <code>LocalPortForwarder</code> forwards TCP/IP connections to a local
+ * port via the secure tunnel to another host (which may or may not be identical
+ * to the remote SSH-2 server). Checkout {@link Connection#createLocalPortForwarder(int, String, int)}
+ * on how to create one.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: LocalPortForwarder.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+public class LocalPortForwarder
+{
+ ChannelManager cm;
+
+ String host_to_connect;
+
+ int port_to_connect;
+
+ LocalAcceptThread lat;
+
+ LocalPortForwarder(ChannelManager cm, int local_port, String host_to_connect, int port_to_connect)
+ throws IOException
+ {
+ this.cm = cm;
+ this.host_to_connect = host_to_connect;
+ this.port_to_connect = port_to_connect;
+
+ lat = new LocalAcceptThread(cm, local_port, host_to_connect, port_to_connect);
+ lat.setDaemon(true);
+ lat.start();
+ }
+
+ LocalPortForwarder(ChannelManager cm, InetSocketAddress addr, String host_to_connect, int port_to_connect)
+ throws IOException
+ {
+ this.cm = cm;
+ this.host_to_connect = host_to_connect;
+ this.port_to_connect = port_to_connect;
+
+ lat = new LocalAcceptThread(cm, addr, host_to_connect, port_to_connect);
+ lat.setDaemon(true);
+ lat.start();
+ }
+
+ /**
+ * Stop TCP/IP forwarding of newly arriving connections.
+ *
+ * @throws IOException
+ */
+ public void close() throws IOException
+ {
+ lat.stopWorking();
+ }
+}
diff --git a/src/com/trilead/ssh2/LocalStreamForwarder.java b/src/com/trilead/ssh2/LocalStreamForwarder.java
index a841976..7899367 100644
--- a/src/com/trilead/ssh2/LocalStreamForwarder.java
+++ b/src/com/trilead/ssh2/LocalStreamForwarder.java
@@ -1,78 +1,78 @@
-
-package com.trilead.ssh2;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-import com.trilead.ssh2.channel.Channel;
-import com.trilead.ssh2.channel.ChannelManager;
-import com.trilead.ssh2.channel.LocalAcceptThread;
-
-
-/**
- * A <code>LocalStreamForwarder</code> forwards an Input- and Outputstream
- * pair via the secure tunnel to another host (which may or may not be identical
- * to the remote SSH-2 server).
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: LocalStreamForwarder.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public class LocalStreamForwarder
-{
- ChannelManager cm;
-
- String host_to_connect;
- int port_to_connect;
- LocalAcceptThread lat;
-
- Channel cn;
-
- LocalStreamForwarder(ChannelManager cm, String host_to_connect, int port_to_connect) throws IOException
- {
- this.cm = cm;
- this.host_to_connect = host_to_connect;
- this.port_to_connect = port_to_connect;
-
- cn = cm.openDirectTCPIPChannel(host_to_connect, port_to_connect, "127.0.0.1", 0);
- }
-
- /**
- * @return An <code>InputStream</code> object.
- * @throws IOException
- */
- public InputStream getInputStream() throws IOException
- {
- return cn.getStdoutStream();
- }
-
- /**
- * Get the OutputStream. Please be aware that the implementation MAY use an
- * internal buffer. To make sure that the buffered data is sent over the
- * tunnel, you have to call the <code>flush</code> method of the
- * <code>OutputStream</code>. To signal EOF, please use the
- * <code>close</code> method of the <code>OutputStream</code>.
- *
- * @return An <code>OutputStream</code> object.
- * @throws IOException
- */
- public OutputStream getOutputStream() throws IOException
- {
- return cn.getStdinStream();
- }
-
- /**
- * Close the underlying SSH forwarding channel and free up resources.
- * You can also use this method to force the shutdown of the underlying
- * forwarding channel. Pending output (OutputStream not flushed) will NOT
- * be sent. Pending input (InputStream) can still be read. If the shutdown
- * operation is already in progress (initiated from either side), then this
- * call is a no-op.
- *
- * @throws IOException
- */
- public void close() throws IOException
- {
- cm.closeChannel(cn, "Closed due to user request.", true);
- }
-}
+
+package com.trilead.ssh2;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import com.trilead.ssh2.channel.Channel;
+import com.trilead.ssh2.channel.ChannelManager;
+import com.trilead.ssh2.channel.LocalAcceptThread;
+
+
+/**
+ * A <code>LocalStreamForwarder</code> forwards an Input- and Outputstream
+ * pair via the secure tunnel to another host (which may or may not be identical
+ * to the remote SSH-2 server).
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: LocalStreamForwarder.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+public class LocalStreamForwarder
+{
+ ChannelManager cm;
+
+ String host_to_connect;
+ int port_to_connect;
+ LocalAcceptThread lat;
+
+ Channel cn;
+
+ LocalStreamForwarder(ChannelManager cm, String host_to_connect, int port_to_connect) throws IOException
+ {
+ this.cm = cm;
+ this.host_to_connect = host_to_connect;
+ this.port_to_connect = port_to_connect;
+
+ cn = cm.openDirectTCPIPChannel(host_to_connect, port_to_connect, "127.0.0.1", 0);
+ }
+
+ /**
+ * @return An <code>InputStream</code> object.
+ * @throws IOException
+ */
+ public InputStream getInputStream() throws IOException
+ {
+ return cn.getStdoutStream();
+ }
+
+ /**
+ * Get the OutputStream. Please be aware that the implementation MAY use an
+ * internal buffer. To make sure that the buffered data is sent over the
+ * tunnel, you have to call the <code>flush</code> method of the
+ * <code>OutputStream</code>. To signal EOF, please use the
+ * <code>close</code> method of the <code>OutputStream</code>.
+ *
+ * @return An <code>OutputStream</code> object.
+ * @throws IOException
+ */
+ public OutputStream getOutputStream() throws IOException
+ {
+ return cn.getStdinStream();
+ }
+
+ /**
+ * Close the underlying SSH forwarding channel and free up resources.
+ * You can also use this method to force the shutdown of the underlying
+ * forwarding channel. Pending output (OutputStream not flushed) will NOT
+ * be sent. Pending input (InputStream) can still be read. If the shutdown
+ * operation is already in progress (initiated from either side), then this
+ * call is a no-op.
+ *
+ * @throws IOException
+ */
+ public void close() throws IOException
+ {
+ cm.closeChannel(cn, "Closed due to user request.", true);
+ }
+}
diff --git a/src/com/trilead/ssh2/ProxyData.java b/src/com/trilead/ssh2/ProxyData.java
index 3298a9d..059a6e3 100644
--- a/src/com/trilead/ssh2/ProxyData.java
+++ b/src/com/trilead/ssh2/ProxyData.java
@@ -1,15 +1,15 @@
-
-package com.trilead.ssh2;
-
-/**
- * An abstract marker interface implemented by all proxy data implementations.
- *
- * @see HTTPProxyData
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: ProxyData.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-
-public abstract interface ProxyData
-{
-}
+
+package com.trilead.ssh2;
+
+/**
+ * An abstract marker interface implemented by all proxy data implementations.
+ *
+ * @see HTTPProxyData
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: ProxyData.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+
+public abstract interface ProxyData
+{
+}
diff --git a/src/com/trilead/ssh2/SCPClient.java b/src/com/trilead/ssh2/SCPClient.java
index 8ea248a..b692750 100644
--- a/src/com/trilead/ssh2/SCPClient.java
+++ b/src/com/trilead/ssh2/SCPClient.java
@@ -1,729 +1,729 @@
-
-package com.trilead.ssh2;
-
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-/**
- * A very basic <code>SCPClient</code> that can be used to copy files from/to
- * the SSH-2 server. On the server side, the "scp" program must be in the PATH.
- * <p>
- * This scp client is thread safe - you can download (and upload) different sets
- * of files concurrently without any troubles. The <code>SCPClient</code> is
- * actually mapping every request to a distinct {@link Session}.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: SCPClient.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
- */
-
-public class SCPClient
-{
- Connection conn;
-
- class LenNamePair
- {
- long length;
- String filename;
- }
-
- public SCPClient(Connection conn)
- {
- if (conn == null)
- throw new IllegalArgumentException("Cannot accept null argument!");
- this.conn = conn;
- }
-
- private void readResponse(InputStream is) throws IOException
- {
- int c = is.read();
-
- if (c == 0)
- return;
-
- if (c == -1)
- throw new IOException("Remote scp terminated unexpectedly.");
-
- if ((c != 1) && (c != 2))
- throw new IOException("Remote scp sent illegal error code.");
-
- if (c == 2)
- throw new IOException("Remote scp terminated with error.");
-
- String err = receiveLine(is);
- throw new IOException("Remote scp terminated with error (" + err + ").");
- }
-
- private String receiveLine(InputStream is) throws IOException
- {
- StringBuffer sb = new StringBuffer(30);
-
- while (true)
- {
- /*
- * This is a random limit - if your path names are longer, then
- * adjust it
- */
-
- if (sb.length() > 8192)
- throw new IOException("Remote scp sent a too long line");
-
- int c = is.read();
-
- if (c < 0)
- throw new IOException("Remote scp terminated unexpectedly.");
-
- if (c == '\n')
- break;
-
- sb.append((char) c);
-
- }
- return sb.toString();
- }
-
- private LenNamePair parseCLine(String line) throws IOException
- {
- /* Minimum line: "xxxx y z" ---> 8 chars */
-
- long len;
-
- if (line.length() < 8)
- throw new IOException("Malformed C line sent by remote SCP binary, line too short.");
-
- if ((line.charAt(4) != ' ') || (line.charAt(5) == ' '))
- throw new IOException("Malformed C line sent by remote SCP binary.");
-
- int length_name_sep = line.indexOf(' ', 5);
-
- if (length_name_sep == -1)
- throw new IOException("Malformed C line sent by remote SCP binary.");
-
- String length_substring = line.substring(5, length_name_sep);
- String name_substring = line.substring(length_name_sep + 1);
-
- if ((length_substring.length() <= 0) || (name_substring.length() <= 0))
- throw new IOException("Malformed C line sent by remote SCP binary.");
-
- if ((6 + length_substring.length() + name_substring.length()) != line.length())
- throw new IOException("Malformed C line sent by remote SCP binary.");
-
- try
- {
- len = Long.parseLong(length_substring);
- }
- catch (NumberFormatException e)
- {
- throw new IOException("Malformed C line sent by remote SCP binary, cannot parse file length.");
- }
-
- if (len < 0)
- throw new IOException("Malformed C line sent by remote SCP binary, illegal file length.");
-
- LenNamePair lnp = new LenNamePair();
- lnp.length = len;
- lnp.filename = name_substring;
-
- return lnp;
- }
-
- private void sendBytes(Session sess, byte[] data, String fileName, String mode) throws IOException
- {
- OutputStream os = sess.getStdin();
- InputStream is = new BufferedInputStream(sess.getStdout(), 512);
-
- readResponse(is);
-
- String cline = "C" + mode + " " + data.length + " " + fileName + "\n";
-
- os.write(cline.getBytes("ISO-8859-1"));
- os.flush();
-
- readResponse(is);
-
- os.write(data, 0, data.length);
- os.write(0);
- os.flush();
-
- readResponse(is);
-
- os.write("E\n".getBytes("ISO-8859-1"));
- os.flush();
- }
-
- private void sendFiles(Session sess, String[] files, String[] remoteFiles, String mode) throws IOException
- {
- byte[] buffer = new byte[8192];
-
- OutputStream os = new BufferedOutputStream(sess.getStdin(), 40000);
- InputStream is = new BufferedInputStream(sess.getStdout(), 512);
-
- readResponse(is);
-
- for (int i = 0; i < files.length; i++)
- {
- File f = new File(files[i]);
- long remain = f.length();
-
- String remoteName;
-
- if ((remoteFiles != null) && (remoteFiles.length > i) && (remoteFiles[i] != null))
- remoteName = remoteFiles[i];
- else
- remoteName = f.getName();
-
- String cline = "C" + mode + " " + remain + " " + remoteName + "\n";
-
- os.write(cline.getBytes("ISO-8859-1"));
- os.flush();
-
- readResponse(is);
-
- FileInputStream fis = null;
-
- try
- {
- fis = new FileInputStream(f);
-
- while (remain > 0)
- {
- int trans;
- if (remain > buffer.length)
- trans = buffer.length;
- else
- trans = (int) remain;
-
- if (fis.read(buffer, 0, trans) != trans)
- throw new IOException("Cannot read enough from local file " + files[i]);
-
- os.write(buffer, 0, trans);
-
- remain -= trans;
- }
- }
- finally
- {
- if (fis != null)
- fis.close();
- }
-
- os.write(0);
- os.flush();
-
- readResponse(is);
- }
-
- os.write("E\n".getBytes("ISO-8859-1"));
- os.flush();
- }
-
- private void receiveFiles(Session sess, OutputStream[] targets) throws IOException
- {
- byte[] buffer = new byte[8192];
-
- OutputStream os = new BufferedOutputStream(sess.getStdin(), 512);
- InputStream is = new BufferedInputStream(sess.getStdout(), 40000);
-
- os.write(0x0);
- os.flush();
-
- for (int i = 0; i < targets.length; i++)
- {
- LenNamePair lnp = null;
-
- while (true)
- {
- int c = is.read();
- if (c < 0)
- throw new IOException("Remote scp terminated unexpectedly.");
-
- String line = receiveLine(is);
-
- if (c == 'T')
- {
- /* Ignore modification times */
-
- continue;
- }
-
- if ((c == 1) || (c == 2))
- throw new IOException("Remote SCP error: " + line);
-
- if (c == 'C')
- {
- lnp = parseCLine(line);
- break;
-
- }
- throw new IOException("Remote SCP error: " + ((char) c) + line);
- }
-
- os.write(0x0);
- os.flush();
-
- long remain = lnp.length;
-
- while (remain > 0)
- {
- int trans;
- if (remain > buffer.length)
- trans = buffer.length;
- else
- trans = (int) remain;
-
- int this_time_received = is.read(buffer, 0, trans);
-
- if (this_time_received < 0)
- {
- throw new IOException("Remote scp terminated connection unexpectedly");
- }
-
- targets[i].write(buffer, 0, this_time_received);
-
- remain -= this_time_received;
- }
-
- readResponse(is);
-
- os.write(0x0);
- os.flush();
- }
- }
-
- private void receiveFiles(Session sess, String[] files, String target) throws IOException
- {
- byte[] buffer = new byte[8192];
-
- OutputStream os = new BufferedOutputStream(sess.getStdin(), 512);
- InputStream is = new BufferedInputStream(sess.getStdout(), 40000);
-
- os.write(0x0);
- os.flush();
-
- for (int i = 0; i < files.length; i++)
- {
- LenNamePair lnp = null;
-
- while (true)
- {
- int c = is.read();
- if (c < 0)
- throw new IOException("Remote scp terminated unexpectedly.");
-
- String line = receiveLine(is);
-
- if (c == 'T')
- {
- /* Ignore modification times */
-
- continue;
- }
-
- if ((c == 1) || (c == 2))
- throw new IOException("Remote SCP error: " + line);
-
- if (c == 'C')
- {
- lnp = parseCLine(line);
- break;
-
- }
- throw new IOException("Remote SCP error: " + ((char) c) + line);
- }
-
- os.write(0x0);
- os.flush();
-
- File f = new File(target + File.separatorChar + lnp.filename);
- FileOutputStream fop = null;
-
- try
- {
- fop = new FileOutputStream(f);
-
- long remain = lnp.length;
-
- while (remain > 0)
- {
- int trans;
- if (remain > buffer.length)
- trans = buffer.length;
- else
- trans = (int) remain;
-
- int this_time_received = is.read(buffer, 0, trans);
-
- if (this_time_received < 0)
- {
- throw new IOException("Remote scp terminated connection unexpectedly");
- }
-
- fop.write(buffer, 0, this_time_received);
-
- remain -= this_time_received;
- }
- }
- finally
- {
- if (fop != null)
- fop.close();
- }
-
- readResponse(is);
-
- os.write(0x0);
- os.flush();
- }
- }
-
- /**
- * Copy a local file to a remote directory, uses mode 0600 when creating the
- * file on the remote side.
- *
- * @param localFile
- * Path and name of local file.
- * @param remoteTargetDirectory
- * Remote target directory. Use an empty string to specify the
- * default directory.
- *
- * @throws IOException
- */
- public void put(String localFile, String remoteTargetDirectory) throws IOException
- {
- put(new String[] { localFile }, remoteTargetDirectory, "0600");
- }
-
- /**
- * Copy a set of local files to a remote directory, uses mode 0600 when
- * creating files on the remote side.
- *
- * @param localFiles
- * Paths and names of local file names.
- * @param remoteTargetDirectory
- * Remote target directory. Use an empty string to specify the
- * default directory.
- *
- * @throws IOException
- */
-
- public void put(String[] localFiles, String remoteTargetDirectory) throws IOException
- {
- put(localFiles, remoteTargetDirectory, "0600");
- }
-
- /**
- * Copy a local file to a remote directory, uses the specified mode when
- * creating the file on the remote side.
- *
- * @param localFile
- * Path and name of local file.
- * @param remoteTargetDirectory
- * Remote target directory. Use an empty string to specify the
- * default directory.
- * @param mode
- * a four digit string (e.g., 0644, see "man chmod", "man open")
- * @throws IOException
- */
- public void put(String localFile, String remoteTargetDirectory, String mode) throws IOException
- {
- put(new String[] { localFile }, remoteTargetDirectory, mode);
- }
-
- /**
- * Copy a local file to a remote directory, uses the specified mode and
- * remote filename when creating the file on the remote side.
- *
- * @param localFile
- * Path and name of local file.
- * @param remoteFileName
- * The name of the file which will be created in the remote
- * target directory.
- * @param remoteTargetDirectory
- * Remote target directory. Use an empty string to specify the
- * default directory.
- * @param mode
- * a four digit string (e.g., 0644, see "man chmod", "man open")
- * @throws IOException
- */
- public void put(String localFile, String remoteFileName, String remoteTargetDirectory, String mode)
- throws IOException
- {
- put(new String[] { localFile }, new String[] { remoteFileName }, remoteTargetDirectory, mode);
- }
-
- /**
- * Create a remote file and copy the contents of the passed byte array into
- * it. Uses mode 0600 for creating the remote file.
- *
- * @param data
- * the data to be copied into the remote file.
- * @param remoteFileName
- * The name of the file which will be created in the remote
- * target directory.
- * @param remoteTargetDirectory
- * Remote target directory. Use an empty string to specify the
- * default directory.
- * @throws IOException
- */
-
- public void put(byte[] data, String remoteFileName, String remoteTargetDirectory) throws IOException
- {
- put(data, remoteFileName, remoteTargetDirectory, "0600");
- }
-
- /**
- * Create a remote file and copy the contents of the passed byte array into
- * it. The method use the specified mode when creating the file on the
- * remote side.
- *
- * @param data
- * the data to be copied into the remote file.
- * @param remoteFileName
- * The name of the file which will be created in the remote
- * target directory.
- * @param remoteTargetDirectory
- * Remote target directory. Use an empty string to specify the
- * default directory.
- * @param mode
- * a four digit string (e.g., 0644, see "man chmod", "man open")
- * @throws IOException
- */
- public void put(byte[] data, String remoteFileName, String remoteTargetDirectory, String mode) throws IOException
- {
- Session sess = null;
-
- if ((remoteFileName == null) || (remoteTargetDirectory == null) || (mode == null))
- throw new IllegalArgumentException("Null argument.");
-
- if (mode.length() != 4)
- throw new IllegalArgumentException("Invalid mode.");
-
- for (int i = 0; i < mode.length(); i++)
- if (Character.isDigit(mode.charAt(i)) == false)
- throw new IllegalArgumentException("Invalid mode.");
-
- remoteTargetDirectory = remoteTargetDirectory.trim();
- remoteTargetDirectory = (remoteTargetDirectory.length() > 0) ? remoteTargetDirectory : ".";
-
- String cmd = "scp -t -d " + remoteTargetDirectory;
-
- try
- {
- sess = conn.openSession();
- sess.execCommand(cmd);
- sendBytes(sess, data, remoteFileName, mode);
- }
- catch (IOException e)
- {
- throw (IOException) new IOException("Error during SCP transfer.").initCause(e);
- }
- finally
- {
- if (sess != null)
- sess.close();
- }
- }
-
- /**
- * Copy a set of local files to a remote directory, uses the specified mode
- * when creating the files on the remote side.
- *
- * @param localFiles
- * Paths and names of the local files.
- * @param remoteTargetDirectory
- * Remote target directory. Use an empty string to specify the
- * default directory.
- * @param mode
- * a four digit string (e.g., 0644, see "man chmod", "man open")
- * @throws IOException
- */
- public void put(String[] localFiles, String remoteTargetDirectory, String mode) throws IOException
- {
- put(localFiles, null, remoteTargetDirectory, mode);
- }
-
- public void put(String[] localFiles, String[] remoteFiles, String remoteTargetDirectory, String mode)
- throws IOException
- {
- Session sess = null;
-
- /*
- * remoteFiles may be null, indicating that the local filenames shall be
- * used
- */
-
- if ((localFiles == null) || (remoteTargetDirectory == null) || (mode == null))
- throw new IllegalArgumentException("Null argument.");
-
- if (mode.length() != 4)
- throw new IllegalArgumentException("Invalid mode.");
-
- for (int i = 0; i < mode.length(); i++)
- if (Character.isDigit(mode.charAt(i)) == false)
- throw new IllegalArgumentException("Invalid mode.");
-
- if (localFiles.length == 0)
- return;
-
- remoteTargetDirectory = remoteTargetDirectory.trim();
- remoteTargetDirectory = (remoteTargetDirectory.length() > 0) ? remoteTargetDirectory : ".";
-
- String cmd = "scp -t -d " + remoteTargetDirectory;
-
- for (int i = 0; i < localFiles.length; i++)
- {
- if (localFiles[i] == null)
- throw new IllegalArgumentException("Cannot accept null filename.");
- }
-
- try
- {
- sess = conn.openSession();
- sess.execCommand(cmd);
- sendFiles(sess, localFiles, remoteFiles, mode);
- }
- catch (IOException e)
- {
- throw (IOException) new IOException("Error during SCP transfer.").initCause(e);
- }
- finally
- {
- if (sess != null)
- sess.close();
- }
- }
-
- /**
- * Download a file from the remote server to a local directory.
- *
- * @param remoteFile
- * Path and name of the remote file.
- * @param localTargetDirectory
- * Local directory to put the downloaded file.
- *
- * @throws IOException
- */
- public void get(String remoteFile, String localTargetDirectory) throws IOException
- {
- get(new String[] { remoteFile }, localTargetDirectory);
- }
-
- /**
- * Download a file from the remote server and pipe its contents into an
- * <code>OutputStream</code>. Please note that, to enable flexible usage
- * of this method, the <code>OutputStream</code> will not be closed nor
- * flushed.
- *
- * @param remoteFile
- * Path and name of the remote file.
- * @param target
- * OutputStream where the contents of the file will be sent to.
- * @throws IOException
- */
- public void get(String remoteFile, OutputStream target) throws IOException
- {
- get(new String[] { remoteFile }, new OutputStream[] { target });
- }
-
- private void get(String remoteFiles[], OutputStream[] targets) throws IOException
- {
- Session sess = null;
-
- if ((remoteFiles == null) || (targets == null))
- throw new IllegalArgumentException("Null argument.");
-
- if (remoteFiles.length != targets.length)
- throw new IllegalArgumentException("Length of arguments does not match.");
-
- if (remoteFiles.length == 0)
- return;
-
- String cmd = "scp -f";
-
- for (int i = 0; i < remoteFiles.length; i++)
- {
- if (remoteFiles[i] == null)
- throw new IllegalArgumentException("Cannot accept null filename.");
-
- String tmp = remoteFiles[i].trim();
-
- if (tmp.length() == 0)
- throw new IllegalArgumentException("Cannot accept empty filename.");
-
- cmd += (" " + tmp);
- }
-
- try
- {
- sess = conn.openSession();
- sess.execCommand(cmd);
- receiveFiles(sess, targets);
- }
- catch (IOException e)
- {
- throw (IOException) new IOException("Error during SCP transfer.").initCause(e);
- }
- finally
- {
- if (sess != null)
- sess.close();
- }
- }
-
- /**
- * Download a set of files from the remote server to a local directory.
- *
- * @param remoteFiles
- * Paths and names of the remote files.
- * @param localTargetDirectory
- * Local directory to put the downloaded files.
- *
- * @throws IOException
- */
- public void get(String remoteFiles[], String localTargetDirectory) throws IOException
- {
- Session sess = null;
-
- if ((remoteFiles == null) || (localTargetDirectory == null))
- throw new IllegalArgumentException("Null argument.");
-
- if (remoteFiles.length == 0)
- return;
-
- String cmd = "scp -f";
-
- for (int i = 0; i < remoteFiles.length; i++)
- {
- if (remoteFiles[i] == null)
- throw new IllegalArgumentException("Cannot accept null filename.");
-
- String tmp = remoteFiles[i].trim();
-
- if (tmp.length() == 0)
- throw new IllegalArgumentException("Cannot accept empty filename.");
-
- cmd += (" " + tmp);
- }
-
- try
- {
- sess = conn.openSession();
- sess.execCommand(cmd);
- receiveFiles(sess, remoteFiles, localTargetDirectory);
- }
- catch (IOException e)
- {
- throw (IOException) new IOException("Error during SCP transfer.").initCause(e);
- }
- finally
- {
- if (sess != null)
- sess.close();
- }
- }
-}
+
+package com.trilead.ssh2;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * A very basic <code>SCPClient</code> that can be used to copy files from/to
+ * the SSH-2 server. On the server side, the "scp" program must be in the PATH.
+ * <p>
+ * This scp client is thread safe - you can download (and upload) different sets
+ * of files concurrently without any troubles. The <code>SCPClient</code> is
+ * actually mapping every request to a distinct {@link Session}.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: SCPClient.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
+ */
+
+public class SCPClient
+{
+ Connection conn;
+
+ class LenNamePair
+ {
+ long length;
+ String filename;
+ }
+
+ public SCPClient(Connection conn)
+ {
+ if (conn == null)
+ throw new IllegalArgumentException("Cannot accept null argument!");
+ this.conn = conn;
+ }
+
+ private void readResponse(InputStream is) throws IOException
+ {
+ int c = is.read();
+
+ if (c == 0)
+ return;
+
+ if (c == -1)
+ throw new IOException("Remote scp terminated unexpectedly.");
+
+ if ((c != 1) && (c != 2))
+ throw new IOException("Remote scp sent illegal error code.");
+
+ if (c == 2)
+ throw new IOException("Remote scp terminated with error.");
+
+ String err = receiveLine(is);
+ throw new IOException("Remote scp terminated with error (" + err + ").");
+ }
+
+ private String receiveLine(InputStream is) throws IOException
+ {
+ StringBuffer sb = new StringBuffer(30);
+
+ while (true)
+ {
+ /*
+ * This is a random limit - if your path names are longer, then
+ * adjust it
+ */
+
+ if (sb.length() > 8192)
+ throw new IOException("Remote scp sent a too long line");
+
+ int c = is.read();
+
+ if (c < 0)
+ throw new IOException("Remote scp terminated unexpectedly.");
+
+ if (c == '\n')
+ break;
+
+ sb.append((char) c);
+
+ }
+ return sb.toString();
+ }
+
+ private LenNamePair parseCLine(String line) throws IOException
+ {
+ /* Minimum line: "xxxx y z" ---> 8 chars */
+
+ long len;
+
+ if (line.length() < 8)
+ throw new IOException("Malformed C line sent by remote SCP binary, line too short.");
+
+ if ((line.charAt(4) != ' ') || (line.charAt(5) == ' '))
+ throw new IOException("Malformed C line sent by remote SCP binary.");
+
+ int length_name_sep = line.indexOf(' ', 5);
+
+ if (length_name_sep == -1)
+ throw new IOException("Malformed C line sent by remote SCP binary.");
+
+ String length_substring = line.substring(5, length_name_sep);
+ String name_substring = line.substring(length_name_sep + 1);
+
+ if ((length_substring.length() <= 0) || (name_substring.length() <= 0))
+ throw new IOException("Malformed C line sent by remote SCP binary.");
+
+ if ((6 + length_substring.length() + name_substring.length()) != line.length())
+ throw new IOException("Malformed C line sent by remote SCP binary.");
+
+ try
+ {
+ len = Long.parseLong(length_substring);
+ }
+ catch (NumberFormatException e)
+ {
+ throw new IOException("Malformed C line sent by remote SCP binary, cannot parse file length.");
+ }
+
+ if (len < 0)
+ throw new IOException("Malformed C line sent by remote SCP binary, illegal file length.");
+
+ LenNamePair lnp = new LenNamePair();
+ lnp.length = len;
+ lnp.filename = name_substring;
+
+ return lnp;
+ }
+
+ private void sendBytes(Session sess, byte[] data, String fileName, String mode) throws IOException
+ {
+ OutputStream os = sess.getStdin();
+ InputStream is = new BufferedInputStream(sess.getStdout(), 512);
+
+ readResponse(is);
+
+ String cline = "C" + mode + " " + data.length + " " + fileName + "\n";
+
+ os.write(cline.getBytes("ISO-8859-1"));
+ os.flush();
+
+ readResponse(is);
+
+ os.write(data, 0, data.length);
+ os.write(0);
+ os.flush();
+
+ readResponse(is);
+
+ os.write("E\n".getBytes("ISO-8859-1"));
+ os.flush();
+ }
+
+ private void sendFiles(Session sess, String[] files, String[] remoteFiles, String mode) throws IOException
+ {
+ byte[] buffer = new byte[8192];
+
+ OutputStream os = new BufferedOutputStream(sess.getStdin(), 40000);
+ InputStream is = new BufferedInputStream(sess.getStdout(), 512);
+
+ readResponse(is);
+
+ for (int i = 0; i < files.length; i++)
+ {
+ File f = new File(files[i]);
+ long remain = f.length();
+
+ String remoteName;
+
+ if ((remoteFiles != null) && (remoteFiles.length > i) && (remoteFiles[i] != null))
+ remoteName = remoteFiles[i];
+ else
+ remoteName = f.getName();
+
+ String cline = "C" + mode + " " + remain + " " + remoteName + "\n";
+
+ os.write(cline.getBytes("ISO-8859-1"));
+ os.flush();
+
+ readResponse(is);
+
+ FileInputStream fis = null;
+
+ try
+ {
+ fis = new FileInputStream(f);
+
+ while (remain > 0)
+ {
+ int trans;
+ if (remain > buffer.length)
+ trans = buffer.length;
+ else
+ trans = (int) remain;
+
+ if (fis.read(buffer, 0, trans) != trans)
+ throw new IOException("Cannot read enough from local file " + files[i]);
+
+ os.write(buffer, 0, trans);
+
+ remain -= trans;
+ }
+ }
+ finally
+ {
+ if (fis != null)
+ fis.close();
+ }
+
+ os.write(0);
+ os.flush();
+
+ readResponse(is);
+ }
+
+ os.write("E\n".getBytes("ISO-8859-1"));
+ os.flush();
+ }
+
+ private void receiveFiles(Session sess, OutputStream[] targets) throws IOException
+ {
+ byte[] buffer = new byte[8192];
+
+ OutputStream os = new BufferedOutputStream(sess.getStdin(), 512);
+ InputStream is = new BufferedInputStream(sess.getStdout(), 40000);
+
+ os.write(0x0);
+ os.flush();
+
+ for (int i = 0; i < targets.length; i++)
+ {
+ LenNamePair lnp = null;
+
+ while (true)
+ {
+ int c = is.read();
+ if (c < 0)
+ throw new IOException("Remote scp terminated unexpectedly.");
+
+ String line = receiveLine(is);
+
+ if (c == 'T')
+ {
+ /* Ignore modification times */
+
+ continue;
+ }
+
+ if ((c == 1) || (c == 2))
+ throw new IOException("Remote SCP error: " + line);
+
+ if (c == 'C')
+ {
+ lnp = parseCLine(line);
+ break;
+
+ }
+ throw new IOException("Remote SCP error: " + ((char) c) + line);
+ }
+
+ os.write(0x0);
+ os.flush();
+
+ long remain = lnp.length;
+
+ while (remain > 0)
+ {
+ int trans;
+ if (remain > buffer.length)
+ trans = buffer.length;
+ else
+ trans = (int) remain;
+
+ int this_time_received = is.read(buffer, 0, trans);
+
+ if (this_time_received < 0)
+ {
+ throw new IOException("Remote scp terminated connection unexpectedly");
+ }
+
+ targets[i].write(buffer, 0, this_time_received);
+
+ remain -= this_time_received;
+ }
+
+ readResponse(is);
+
+ os.write(0x0);
+ os.flush();
+ }
+ }
+
+ private void receiveFiles(Session sess, String[] files, String target) throws IOException
+ {
+ byte[] buffer = new byte[8192];
+
+ OutputStream os = new BufferedOutputStream(sess.getStdin(), 512);
+ InputStream is = new BufferedInputStream(sess.getStdout(), 40000);
+
+ os.write(0x0);
+ os.flush();
+
+ for (int i = 0; i < files.length; i++)
+ {
+ LenNamePair lnp = null;
+
+ while (true)
+ {
+ int c = is.read();
+ if (c < 0)
+ throw new IOException("Remote scp terminated unexpectedly.");
+
+ String line = receiveLine(is);
+
+ if (c == 'T')
+ {
+ /* Ignore modification times */
+
+ continue;
+ }
+
+ if ((c == 1) || (c == 2))
+ throw new IOException("Remote SCP error: " + line);
+
+ if (c == 'C')
+ {
+ lnp = parseCLine(line);
+ break;
+
+ }
+ throw new IOException("Remote SCP error: " + ((char) c) + line);
+ }
+
+ os.write(0x0);
+ os.flush();
+
+ File f = new File(target + File.separatorChar + lnp.filename);
+ FileOutputStream fop = null;
+
+ try
+ {
+ fop = new FileOutputStream(f);
+
+ long remain = lnp.length;
+
+ while (remain > 0)
+ {
+ int trans;
+ if (remain > buffer.length)
+ trans = buffer.length;
+ else
+ trans = (int) remain;
+
+ int this_time_received = is.read(buffer, 0, trans);
+
+ if (this_time_received < 0)
+ {
+ throw new IOException("Remote scp terminated connection unexpectedly");
+ }
+
+ fop.write(buffer, 0, this_time_received);
+
+ remain -= this_time_received;
+ }
+ }
+ finally
+ {
+ if (fop != null)
+ fop.close();
+ }
+
+ readResponse(is);
+
+ os.write(0x0);
+ os.flush();
+ }
+ }
+
+ /**
+ * Copy a local file to a remote directory, uses mode 0600 when creating the
+ * file on the remote side.
+ *
+ * @param localFile
+ * Path and name of local file.
+ * @param remoteTargetDirectory
+ * Remote target directory. Use an empty string to specify the
+ * default directory.
+ *
+ * @throws IOException
+ */
+ public void put(String localFile, String remoteTargetDirectory) throws IOException
+ {
+ put(new String[] { localFile }, remoteTargetDirectory, "0600");
+ }
+
+ /**
+ * Copy a set of local files to a remote directory, uses mode 0600 when
+ * creating files on the remote side.
+ *
+ * @param localFiles
+ * Paths and names of local file names.
+ * @param remoteTargetDirectory
+ * Remote target directory. Use an empty string to specify the
+ * default directory.
+ *
+ * @throws IOException
+ */
+
+ public void put(String[] localFiles, String remoteTargetDirectory) throws IOException
+ {
+ put(localFiles, remoteTargetDirectory, "0600");
+ }
+
+ /**
+ * Copy a local file to a remote directory, uses the specified mode when
+ * creating the file on the remote side.
+ *
+ * @param localFile
+ * Path and name of local file.
+ * @param remoteTargetDirectory
+ * Remote target directory. Use an empty string to specify the
+ * default directory.
+ * @param mode
+ * a four digit string (e.g., 0644, see "man chmod", "man open")
+ * @throws IOException
+ */
+ public void put(String localFile, String remoteTargetDirectory, String mode) throws IOException
+ {
+ put(new String[] { localFile }, remoteTargetDirectory, mode);
+ }
+
+ /**
+ * Copy a local file to a remote directory, uses the specified mode and
+ * remote filename when creating the file on the remote side.
+ *
+ * @param localFile
+ * Path and name of local file.
+ * @param remoteFileName
+ * The name of the file which will be created in the remote
+ * target directory.
+ * @param remoteTargetDirectory
+ * Remote target directory. Use an empty string to specify the
+ * default directory.
+ * @param mode
+ * a four digit string (e.g., 0644, see "man chmod", "man open")
+ * @throws IOException
+ */
+ public void put(String localFile, String remoteFileName, String remoteTargetDirectory, String mode)
+ throws IOException
+ {
+ put(new String[] { localFile }, new String[] { remoteFileName }, remoteTargetDirectory, mode);
+ }
+
+ /**
+ * Create a remote file and copy the contents of the passed byte array into
+ * it. Uses mode 0600 for creating the remote file.
+ *
+ * @param data
+ * the data to be copied into the remote file.
+ * @param remoteFileName
+ * The name of the file which will be created in the remote
+ * target directory.
+ * @param remoteTargetDirectory
+ * Remote target directory. Use an empty string to specify the
+ * default directory.
+ * @throws IOException
+ */
+
+ public void put(byte[] data, String remoteFileName, String remoteTargetDirectory) throws IOException
+ {
+ put(data, remoteFileName, remoteTargetDirectory, "0600");
+ }
+
+ /**
+ * Create a remote file and copy the contents of the passed byte array into
+ * it. The method use the specified mode when creating the file on the
+ * remote side.
+ *
+ * @param data
+ * the data to be copied into the remote file.
+ * @param remoteFileName
+ * The name of the file which will be created in the remote
+ * target directory.
+ * @param remoteTargetDirectory
+ * Remote target directory. Use an empty string to specify the
+ * default directory.
+ * @param mode
+ * a four digit string (e.g., 0644, see "man chmod", "man open")
+ * @throws IOException
+ */
+ public void put(byte[] data, String remoteFileName, String remoteTargetDirectory, String mode) throws IOException
+ {
+ Session sess = null;
+
+ if ((remoteFileName == null) || (remoteTargetDirectory == null) || (mode == null))
+ throw new IllegalArgumentException("Null argument.");
+
+ if (mode.length() != 4)
+ throw new IllegalArgumentException("Invalid mode.");
+
+ for (int i = 0; i < mode.length(); i++)
+ if (Character.isDigit(mode.charAt(i)) == false)
+ throw new IllegalArgumentException("Invalid mode.");
+
+ remoteTargetDirectory = remoteTargetDirectory.trim();
+ remoteTargetDirectory = (remoteTargetDirectory.length() > 0) ? remoteTargetDirectory : ".";
+
+ String cmd = "scp -t -d " + remoteTargetDirectory;
+
+ try
+ {
+ sess = conn.openSession();
+ sess.execCommand(cmd);
+ sendBytes(sess, data, remoteFileName, mode);
+ }
+ catch (IOException e)
+ {
+ throw (IOException) new IOException("Error during SCP transfer.").initCause(e);
+ }
+ finally
+ {
+ if (sess != null)
+ sess.close();
+ }
+ }
+
+ /**
+ * Copy a set of local files to a remote directory, uses the specified mode
+ * when creating the files on the remote side.
+ *
+ * @param localFiles
+ * Paths and names of the local files.
+ * @param remoteTargetDirectory
+ * Remote target directory. Use an empty string to specify the
+ * default directory.
+ * @param mode
+ * a four digit string (e.g., 0644, see "man chmod", "man open")
+ * @throws IOException
+ */
+ public void put(String[] localFiles, String remoteTargetDirectory, String mode) throws IOException
+ {
+ put(localFiles, null, remoteTargetDirectory, mode);
+ }
+
+ public void put(String[] localFiles, String[] remoteFiles, String remoteTargetDirectory, String mode)
+ throws IOException
+ {
+ Session sess = null;
+
+ /*
+ * remoteFiles may be null, indicating that the local filenames shall be
+ * used
+ */
+
+ if ((localFiles == null) || (remoteTargetDirectory == null) || (mode == null))
+ throw new IllegalArgumentException("Null argument.");
+
+ if (mode.length() != 4)
+ throw new IllegalArgumentException("Invalid mode.");
+
+ for (int i = 0; i < mode.length(); i++)
+ if (Character.isDigit(mode.charAt(i)) == false)
+ throw new IllegalArgumentException("Invalid mode.");
+
+ if (localFiles.length == 0)
+ return;
+
+ remoteTargetDirectory = remoteTargetDirectory.trim();
+ remoteTargetDirectory = (remoteTargetDirectory.length() > 0) ? remoteTargetDirectory : ".";
+
+ String cmd = "scp -t -d " + remoteTargetDirectory;
+
+ for (int i = 0; i < localFiles.length; i++)
+ {
+ if (localFiles[i] == null)
+ throw new IllegalArgumentException("Cannot accept null filename.");
+ }
+
+ try
+ {
+ sess = conn.openSession();
+ sess.execCommand(cmd);
+ sendFiles(sess, localFiles, remoteFiles, mode);
+ }
+ catch (IOException e)
+ {
+ throw (IOException) new IOException("Error during SCP transfer.").initCause(e);
+ }
+ finally
+ {
+ if (sess != null)
+ sess.close();
+ }
+ }
+
+ /**
+ * Download a file from the remote server to a local directory.
+ *
+ * @param remoteFile
+ * Path and name of the remote file.
+ * @param localTargetDirectory
+ * Local directory to put the downloaded file.
+ *
+ * @throws IOException
+ */
+ public void get(String remoteFile, String localTargetDirectory) throws IOException
+ {
+ get(new String[] { remoteFile }, localTargetDirectory);
+ }
+
+ /**
+ * Download a file from the remote server and pipe its contents into an
+ * <code>OutputStream</code>. Please note that, to enable flexible usage
+ * of this method, the <code>OutputStream</code> will not be closed nor
+ * flushed.
+ *
+ * @param remoteFile
+ * Path and name of the remote file.
+ * @param target
+ * OutputStream where the contents of the file will be sent to.
+ * @throws IOException
+ */
+ public void get(String remoteFile, OutputStream target) throws IOException
+ {
+ get(new String[] { remoteFile }, new OutputStream[] { target });
+ }
+
+ private void get(String remoteFiles[], OutputStream[] targets) throws IOException
+ {
+ Session sess = null;
+
+ if ((remoteFiles == null) || (targets == null))
+ throw new IllegalArgumentException("Null argument.");
+
+ if (remoteFiles.length != targets.length)
+ throw new IllegalArgumentException("Length of arguments does not match.");
+
+ if (remoteFiles.length == 0)
+ return;
+
+ String cmd = "scp -f";
+
+ for (int i = 0; i < remoteFiles.length; i++)
+ {
+ if (remoteFiles[i] == null)
+ throw new IllegalArgumentException("Cannot accept null filename.");
+
+ String tmp = remoteFiles[i].trim();
+
+ if (tmp.length() == 0)
+ throw new IllegalArgumentException("Cannot accept empty filename.");
+
+ cmd += (" " + tmp);
+ }
+
+ try
+ {
+ sess = conn.openSession();
+ sess.execCommand(cmd);
+ receiveFiles(sess, targets);
+ }
+ catch (IOException e)
+ {
+ throw (IOException) new IOException("Error during SCP transfer.").initCause(e);
+ }
+ finally
+ {
+ if (sess != null)
+ sess.close();
+ }
+ }
+
+ /**
+ * Download a set of files from the remote server to a local directory.
+ *
+ * @param remoteFiles
+ * Paths and names of the remote files.
+ * @param localTargetDirectory
+ * Local directory to put the downloaded files.
+ *
+ * @throws IOException
+ */
+ public void get(String remoteFiles[], String localTargetDirectory) throws IOException
+ {
+ Session sess = null;
+
+ if ((remoteFiles == null) || (localTargetDirectory == null))
+ throw new IllegalArgumentException("Null argument.");
+
+ if (remoteFiles.length == 0)
+ return;
+
+ String cmd = "scp -f";
+
+ for (int i = 0; i < remoteFiles.length; i++)
+ {
+ if (remoteFiles[i] == null)
+ throw new IllegalArgumentException("Cannot accept null filename.");
+
+ String tmp = remoteFiles[i].trim();
+
+ if (tmp.length() == 0)
+ throw new IllegalArgumentException("Cannot accept empty filename.");
+
+ cmd += (" " + tmp);
+ }
+
+ try
+ {
+ sess = conn.openSession();
+ sess.execCommand(cmd);
+ receiveFiles(sess, remoteFiles, localTargetDirectory);
+ }
+ catch (IOException e)
+ {
+ throw (IOException) new IOException("Error during SCP transfer.").initCause(e);
+ }
+ finally
+ {
+ if (sess != null)
+ sess.close();
+ }
+ }
+}
diff --git a/src/com/trilead/ssh2/SFTPException.java b/src/com/trilead/ssh2/SFTPException.java
index f3b6d3e..d97723f 100644
--- a/src/com/trilead/ssh2/SFTPException.java
+++ b/src/com/trilead/ssh2/SFTPException.java
@@ -1,91 +1,91 @@
-
-package com.trilead.ssh2;
-
-import java.io.IOException;
-
-import com.trilead.ssh2.sftp.ErrorCodes;
-
-
-/**
- * Used in combination with the SFTPv3Client. This exception wraps
- * error messages sent by the SFTP server.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: SFTPException.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-
-public class SFTPException extends IOException
-{
- private static final long serialVersionUID = 578654644222421811L;
-
- private final String sftpErrorMessage;
- private final int sftpErrorCode;
-
- private static String constructMessage(String s, int errorCode)
- {
- String[] detail = ErrorCodes.getDescription(errorCode);
-
- if (detail == null)
- return s + " (UNKNOW SFTP ERROR CODE)";
-
- return s + " (" + detail[0] + ": " + detail[1] + ")";
- }
-
- SFTPException(String msg, int errorCode)
- {
- super(constructMessage(msg, errorCode));
- sftpErrorMessage = msg;
- sftpErrorCode = errorCode;
- }
-
- /**
- * Get the error message sent by the server. Often, this
- * message does not help a lot (e.g., "failure").
- *
- * @return the plain string as sent by the server.
- */
- public String getServerErrorMessage()
- {
- return sftpErrorMessage;
- }
-
- /**
- * Get the error code sent by the server.
- *
- * @return an error code as defined in the SFTP specs.
- */
- public int getServerErrorCode()
- {
- return sftpErrorCode;
- }
-
- /**
- * Get the symbolic name of the error code as given in the SFTP specs.
- *
- * @return e.g., "SSH_FX_INVALID_FILENAME".
- */
- public String getServerErrorCodeSymbol()
- {
- String[] detail = ErrorCodes.getDescription(sftpErrorCode);
-
- if (detail == null)
- return "UNKNOW SFTP ERROR CODE " + sftpErrorCode;
-
- return detail[0];
- }
-
- /**
- * Get the description of the error code as given in the SFTP specs.
- *
- * @return e.g., "The filename is not valid."
- */
- public String getServerErrorCodeVerbose()
- {
- String[] detail = ErrorCodes.getDescription(sftpErrorCode);
-
- if (detail == null)
- return "The error code " + sftpErrorCode + " is unknown.";
-
- return detail[1];
- }
-}
+
+package com.trilead.ssh2;
+
+import java.io.IOException;
+
+import com.trilead.ssh2.sftp.ErrorCodes;
+
+
+/**
+ * Used in combination with the SFTPv3Client. This exception wraps
+ * error messages sent by the SFTP server.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: SFTPException.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+
+public class SFTPException extends IOException
+{
+ private static final long serialVersionUID = 578654644222421811L;
+
+ private final String sftpErrorMessage;
+ private final int sftpErrorCode;
+
+ private static String constructMessage(String s, int errorCode)
+ {
+ String[] detail = ErrorCodes.getDescription(errorCode);
+
+ if (detail == null)
+ return s + " (UNKNOW SFTP ERROR CODE)";
+
+ return s + " (" + detail[0] + ": " + detail[1] + ")";
+ }
+
+ SFTPException(String msg, int errorCode)
+ {
+ super(constructMessage(msg, errorCode));
+ sftpErrorMessage = msg;
+ sftpErrorCode = errorCode;
+ }
+
+ /**
+ * Get the error message sent by the server. Often, this
+ * message does not help a lot (e.g., "failure").
+ *
+ * @return the plain string as sent by the server.
+ */
+ public String getServerErrorMessage()
+ {
+ return sftpErrorMessage;
+ }
+
+ /**
+ * Get the error code sent by the server.
+ *
+ * @return an error code as defined in the SFTP specs.
+ */
+ public int getServerErrorCode()
+ {
+ return sftpErrorCode;
+ }
+
+ /**
+ * Get the symbolic name of the error code as given in the SFTP specs.
+ *
+ * @return e.g., "SSH_FX_INVALID_FILENAME".
+ */
+ public String getServerErrorCodeSymbol()
+ {
+ String[] detail = ErrorCodes.getDescription(sftpErrorCode);
+
+ if (detail == null)
+ return "UNKNOW SFTP ERROR CODE " + sftpErrorCode;
+
+ return detail[0];
+ }
+
+ /**
+ * Get the description of the error code as given in the SFTP specs.
+ *
+ * @return e.g., "The filename is not valid."
+ */
+ public String getServerErrorCodeVerbose()
+ {
+ String[] detail = ErrorCodes.getDescription(sftpErrorCode);
+
+ if (detail == null)
+ return "The error code " + sftpErrorCode + " is unknown.";
+
+ return detail[1];
+ }
+}
diff --git a/src/com/trilead/ssh2/SFTPv3Client.java b/src/com/trilead/ssh2/SFTPv3Client.java
index be2fa1c..06796e9 100644
--- a/src/com/trilead/ssh2/SFTPv3Client.java
+++ b/src/com/trilead/ssh2/SFTPv3Client.java
@@ -1,1388 +1,1388 @@
-
-package com.trilead.ssh2;
-
-import java.io.BufferedOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PrintStream;
-import java.nio.charset.Charset;
-import java.util.HashMap;
-import java.util.Vector;
-
-import com.trilead.ssh2.packets.TypesReader;
-import com.trilead.ssh2.packets.TypesWriter;
-import com.trilead.ssh2.sftp.AttribFlags;
-import com.trilead.ssh2.sftp.ErrorCodes;
-import com.trilead.ssh2.sftp.Packet;
-
-
-/**
- * A <code>SFTPv3Client</code> represents a SFTP (protocol version 3)
- * client connection tunnelled over a SSH-2 connection. This is a very simple
- * (synchronous) implementation.
- * <p>
- * Basically, most methods in this class map directly to one of
- * the packet types described in draft-ietf-secsh-filexfer-02.txt.
- * <p>
- * Note: this is experimental code.
- * <p>
- * Error handling: the methods of this class throw IOExceptions. However, unless
- * there is catastrophic failure, exceptions of the type {@link SFTPv3Client} will
- * be thrown (a subclass of IOException). Therefore, you can implement more verbose
- * behavior by checking if a thrown exception if of this type. If yes, then you
- * can cast the exception and access detailed information about the failure.
- * <p>
- * Notes about file names, directory names and paths, copy-pasted
- * from the specs:
- * <ul>
- * <li>SFTP v3 represents file names as strings. File names are
- * assumed to use the slash ('/') character as a directory separator.</li>
- * <li>File names starting with a slash are "absolute", and are relative to
- * the root of the file system. Names starting with any other character
- * are relative to the user's default directory (home directory).</li>
- * <li>Servers SHOULD interpret a path name component ".." as referring to
- * the parent directory, and "." as referring to the current directory.
- * If the server implementation limits access to certain parts of the
- * file system, it must be extra careful in parsing file names when
- * enforcing such restrictions. There have been numerous reported
- * security bugs where a ".." in a path name has allowed access outside
- * the intended area.</li>
- * <li>An empty path name is valid, and it refers to the user's default
- * directory (usually the user's home directory).</li>
- * </ul>
- * <p>
- * If you are still not tired then please go on and read the comment for
- * {@link #setCharset(String)}.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: SFTPv3Client.java,v 1.3 2008/04/01 12:38:09 cplattne Exp $
- */
-public class SFTPv3Client
-{
- final Connection conn;
- final Session sess;
- final PrintStream debug;
-
- boolean flag_closed = false;
-
- InputStream is;
- OutputStream os;
-
- int protocol_version = 0;
- HashMap server_extensions = new HashMap();
-
- int next_request_id = 1000;
-
- String charsetName = null;
-
- /**
- * Create a SFTP v3 client.
- *
- * @param conn The underlying SSH-2 connection to be used.
- * @param debug
- * @throws IOException
- *
- * @deprecated this constructor (debug version) will disappear in the future,
- * use {@link #SFTPv3Client(Connection)} instead.
- */
- public SFTPv3Client(Connection conn, PrintStream debug) throws IOException
- {
- if (conn == null)
- throw new IllegalArgumentException("Cannot accept null argument!");
-
- this.conn = conn;
- this.debug = debug;
-
- if (debug != null)
- debug.println("Opening session and starting SFTP subsystem.");
-
- sess = conn.openSession();
- sess.startSubSystem("sftp");
-
- is = sess.getStdout();
- os = new BufferedOutputStream(sess.getStdin(), 2048);
-
- if ((is == null) || (os == null))
- throw new IOException("There is a problem with the streams of the underlying channel.");
-
- init();
- }
-
- /**
- * Create a SFTP v3 client.
- *
- * @param conn The underlying SSH-2 connection to be used.
- * @throws IOException
- */
- public SFTPv3Client(Connection conn) throws IOException
- {
- this(conn, null);
- }
-
- /**
- * Set the charset used to convert between Java Unicode Strings and byte encodings
- * used by the server for paths and file names. Unfortunately, the SFTP v3 draft
- * says NOTHING about such conversions (well, with the exception of error messages
- * which have to be in UTF-8). Newer drafts specify to use UTF-8 for file names
- * (if I remember correctly). However, a quick test using OpenSSH serving a EXT-3
- * filesystem has shown that UTF-8 seems to be a bad choice for SFTP v3 (tested with
- * filenames containing german umlauts). "windows-1252" seems to work better for Europe.
- * Luckily, "windows-1252" is the platform default in my case =).
- * <p>
- * If you don't set anything, then the platform default will be used (this is the default
- * behavior).
- *
- * @see #getCharset()
- *
- * @param charset the name of the charset to be used or <code>null</code> to use the platform's
- * default encoding.
- * @throws IOException
- */
- public void setCharset(String charset) throws IOException
- {
- if (charset == null)
- {
- charsetName = charset;
- return;
- }
-
- try
- {
- Charset.forName(charset);
- }
- catch (Exception e)
- {
- throw (IOException) new IOException("This charset is not supported").initCause(e);
- }
- charsetName = charset;
- }
-
- /**
- * The currently used charset for filename encoding/decoding.
- *
- * @see #setCharset(String)
- *
- * @return The name of the charset (<code>null</code> if the platform's default charset is being used)
- */
- public String getCharset()
- {
- return charsetName;
- }
-
- private final void checkHandleValidAndOpen(SFTPv3FileHandle handle) throws IOException
- {
- if (handle.client != this)
- throw new IOException("The file handle was created with another SFTPv3FileHandle instance.");
-
- if (handle.isClosed == true)
- throw new IOException("The file handle is closed.");
- }
-
- private final void sendMessage(int type, int requestId, byte[] msg, int off, int len) throws IOException
- {
- int msglen = len + 1;
-
- if (type != Packet.SSH_FXP_INIT)
- msglen += 4;
-
- os.write(msglen >> 24);
- os.write(msglen >> 16);
- os.write(msglen >> 8);
- os.write(msglen);
- os.write(type);
-
- if (type != Packet.SSH_FXP_INIT)
- {
- os.write(requestId >> 24);
- os.write(requestId >> 16);
- os.write(requestId >> 8);
- os.write(requestId);
- }
-
- os.write(msg, off, len);
- os.flush();
- }
-
- private final void sendMessage(int type, int requestId, byte[] msg) throws IOException
- {
- sendMessage(type, requestId, msg, 0, msg.length);
- }
-
- private final void readBytes(byte[] buff, int pos, int len) throws IOException
- {
- while (len > 0)
- {
- int count = is.read(buff, pos, len);
- if (count < 0)
- throw new IOException("Unexpected end of sftp stream.");
- if ((count == 0) || (count > len))
- throw new IOException("Underlying stream implementation is bogus!");
- len -= count;
- pos += count;
- }
- }
-
- /**
- * Read a message and guarantee that the <b>contents</b> is not larger than
- * <code>maxlen</code> bytes.
- * <p>
- * Note: receiveMessage(34000) actually means that the message may be up to 34004
- * bytes (the length attribute preceeding the contents is 4 bytes).
- *
- * @param maxlen
- * @return the message contents
- * @throws IOException
- */
- private final byte[] receiveMessage(int maxlen) throws IOException
- {
- byte[] msglen = new byte[4];
-
- readBytes(msglen, 0, 4);
-
- int len = (((msglen[0] & 0xff) << 24) | ((msglen[1] & 0xff) << 16) | ((msglen[2] & 0xff) << 8) | (msglen[3] & 0xff));
-
- if ((len > maxlen) || (len <= 0))
- throw new IOException("Illegal sftp packet len: " + len);
-
- byte[] msg = new byte[len];
-
- readBytes(msg, 0, len);
-
- return msg;
- }
-
- private final int generateNextRequestID()
- {
- synchronized (this)
- {
- return next_request_id++;
- }
- }
-
- private final void closeHandle(byte[] handle) throws IOException
- {
- int req_id = generateNextRequestID();
-
- TypesWriter tw = new TypesWriter();
- tw.writeString(handle, 0, handle.length);
-
- sendMessage(Packet.SSH_FXP_CLOSE, req_id, tw.getBytes());
-
- expectStatusOKMessage(req_id);
- }
-
- private SFTPv3FileAttributes readAttrs(TypesReader tr) throws IOException
- {
- /*
- * uint32 flags
- * uint64 size present only if flag SSH_FILEXFER_ATTR_SIZE
- * uint32 uid present only if flag SSH_FILEXFER_ATTR_V3_UIDGID
- * uint32 gid present only if flag SSH_FILEXFER_ATTR_V3_UIDGID
- * uint32 permissions present only if flag SSH_FILEXFER_ATTR_PERMISSIONS
- * uint32 atime present only if flag SSH_FILEXFER_ATTR_V3_ACMODTIME
- * uint32 mtime present only if flag SSH_FILEXFER_ATTR_V3_ACMODTIME
- * uint32 extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED
- * string extended_type
- * string extended_data
- * ... more extended data (extended_type - extended_data pairs),
- * so that number of pairs equals extended_count
- */
-
- SFTPv3FileAttributes fa = new SFTPv3FileAttributes();
-
- int flags = tr.readUINT32();
-
- if ((flags & AttribFlags.SSH_FILEXFER_ATTR_SIZE) != 0)
- {
- if (debug != null)
- debug.println("SSH_FILEXFER_ATTR_SIZE");
- fa.size = new Long(tr.readUINT64());
- }
-
- if ((flags & AttribFlags.SSH_FILEXFER_ATTR_V3_UIDGID) != 0)
- {
- if (debug != null)
- debug.println("SSH_FILEXFER_ATTR_V3_UIDGID");
- fa.uid = new Integer(tr.readUINT32());
- fa.gid = new Integer(tr.readUINT32());
- }
-
- if ((flags & AttribFlags.SSH_FILEXFER_ATTR_PERMISSIONS) != 0)
- {
- if (debug != null)
- debug.println("SSH_FILEXFER_ATTR_PERMISSIONS");
- fa.permissions = new Integer(tr.readUINT32());
- }
-
- if ((flags & AttribFlags.SSH_FILEXFER_ATTR_V3_ACMODTIME) != 0)
- {
- if (debug != null)
- debug.println("SSH_FILEXFER_ATTR_V3_ACMODTIME");
- fa.atime = new Long(((long)tr.readUINT32()) & 0xffffffffl);
- fa.mtime = new Long(((long)tr.readUINT32()) & 0xffffffffl);
-
- }
-
- if ((flags & AttribFlags.SSH_FILEXFER_ATTR_EXTENDED) != 0)
- {
- int count = tr.readUINT32();
-
- if (debug != null)
- debug.println("SSH_FILEXFER_ATTR_EXTENDED (" + count + ")");
-
- /* Read it anyway to detect corrupt packets */
-
- while (count > 0)
- {
- tr.readByteString();
- tr.readByteString();
- count--;
- }
- }
-
- return fa;
- }
-
- /**
- * Retrieve the file attributes of an open file.
- *
- * @param handle a SFTPv3FileHandle handle.
- * @return a SFTPv3FileAttributes object.
- * @throws IOException
- */
- public SFTPv3FileAttributes fstat(SFTPv3FileHandle handle) throws IOException
- {
- checkHandleValidAndOpen(handle);
-
- int req_id = generateNextRequestID();
-
- TypesWriter tw = new TypesWriter();
- tw.writeString(handle.fileHandle, 0, handle.fileHandle.length);
-
- if (debug != null)
- {
- debug.println("Sending SSH_FXP_FSTAT...");
- debug.flush();
- }
-
- sendMessage(Packet.SSH_FXP_FSTAT, req_id, tw.getBytes());
-
- byte[] resp = receiveMessage(34000);
-
- if (debug != null)
- {
- debug.println("Got REPLY.");
- debug.flush();
- }
-
- TypesReader tr = new TypesReader(resp);
-
- int t = tr.readByte();
-
- int rep_id = tr.readUINT32();
- if (rep_id != req_id)
- throw new IOException("The server sent an invalid id field.");
-
- if (t == Packet.SSH_FXP_ATTRS)
- {
- return readAttrs(tr);
- }
-
- if (t != Packet.SSH_FXP_STATUS)
- throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
-
- int errorCode = tr.readUINT32();
-
- throw new SFTPException(tr.readString(), errorCode);
- }
-
- private SFTPv3FileAttributes statBoth(String path, int statMethod) throws IOException
- {
- int req_id = generateNextRequestID();
-
- TypesWriter tw = new TypesWriter();
- tw.writeString(path, charsetName);
-
- if (debug != null)
- {
- debug.println("Sending SSH_FXP_STAT/SSH_FXP_LSTAT...");
- debug.flush();
- }
-
- sendMessage(statMethod, req_id, tw.getBytes());
-
- byte[] resp = receiveMessage(34000);
-
- if (debug != null)
- {
- debug.println("Got REPLY.");
- debug.flush();
- }
-
- TypesReader tr = new TypesReader(resp);
-
- int t = tr.readByte();
-
- int rep_id = tr.readUINT32();
- if (rep_id != req_id)
- throw new IOException("The server sent an invalid id field.");
-
- if (t == Packet.SSH_FXP_ATTRS)
- {
- return readAttrs(tr);
- }
-
- if (t != Packet.SSH_FXP_STATUS)
- throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
-
- int errorCode = tr.readUINT32();
-
- throw new SFTPException(tr.readString(), errorCode);
- }
-
- /**
- * Retrieve the file attributes of a file. This method
- * follows symbolic links on the server.
- *
- * @see #lstat(String)
- *
- * @param path See the {@link SFTPv3Client comment} for the class for more details.
- * @return a SFTPv3FileAttributes object.
- * @throws IOException
- */
- public SFTPv3FileAttributes stat(String path) throws IOException
- {
- return statBoth(path, Packet.SSH_FXP_STAT);
- }
-
- /**
- * Retrieve the file attributes of a file. This method
- * does NOT follow symbolic links on the server.
- *
- * @see #stat(String)
- *
- * @param path See the {@link SFTPv3Client comment} for the class for more details.
- * @return a SFTPv3FileAttributes object.
- * @throws IOException
- */
- public SFTPv3FileAttributes lstat(String path) throws IOException
- {
- return statBoth(path, Packet.SSH_FXP_LSTAT);
- }
-
- /**
- * Read the target of a symbolic link.
- *
- * @param path See the {@link SFTPv3Client comment} for the class for more details.
- * @return The target of the link.
- * @throws IOException
- */
- public String readLink(String path) throws IOException
- {
- int req_id = generateNextRequestID();
-
- TypesWriter tw = new TypesWriter();
- tw.writeString(path, charsetName);
-
- if (debug != null)
- {
- debug.println("Sending SSH_FXP_READLINK...");
- debug.flush();
- }
-
- sendMessage(Packet.SSH_FXP_READLINK, req_id, tw.getBytes());
-
- byte[] resp = receiveMessage(34000);
-
- if (debug != null)
- {
- debug.println("Got REPLY.");
- debug.flush();
- }
-
- TypesReader tr = new TypesReader(resp);
-
- int t = tr.readByte();
-
- int rep_id = tr.readUINT32();
- if (rep_id != req_id)
- throw new IOException("The server sent an invalid id field.");
-
- if (t == Packet.SSH_FXP_NAME)
- {
- int count = tr.readUINT32();
-
- if (count != 1)
- throw new IOException("The server sent an invalid SSH_FXP_NAME packet.");
-
- return tr.readString(charsetName);
- }
-
- if (t != Packet.SSH_FXP_STATUS)
- throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
-
- int errorCode = tr.readUINT32();
-
- throw new SFTPException(tr.readString(), errorCode);
- }
-
- private void expectStatusOKMessage(int id) throws IOException
- {
- byte[] resp = receiveMessage(34000);
-
- if (debug != null)
- {
- debug.println("Got REPLY.");
- debug.flush();
- }
-
- TypesReader tr = new TypesReader(resp);
-
- int t = tr.readByte();
-
- int rep_id = tr.readUINT32();
- if (rep_id != id)
- throw new IOException("The server sent an invalid id field.");
-
- if (t != Packet.SSH_FXP_STATUS)
- throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
-
- int errorCode = tr.readUINT32();
-
- if (errorCode == ErrorCodes.SSH_FX_OK)
- return;
-
- throw new SFTPException(tr.readString(), errorCode);
- }
-
- /**
- * Modify the attributes of a file. Used for operations such as changing
- * the ownership, permissions or access times, as well as for truncating a file.
- *
- * @param path See the {@link SFTPv3Client comment} for the class for more details.
- * @param attr A SFTPv3FileAttributes object. Specifies the modifications to be
- * made to the attributes of the file. Empty fields will be ignored.
- * @throws IOException
- */
- public void setstat(String path, SFTPv3FileAttributes attr) throws IOException
- {
- int req_id = generateNextRequestID();
-
- TypesWriter tw = new TypesWriter();
- tw.writeString(path, charsetName);
- tw.writeBytes(createAttrs(attr));
-
- if (debug != null)
- {
- debug.println("Sending SSH_FXP_SETSTAT...");
- debug.flush();
- }
-
- sendMessage(Packet.SSH_FXP_SETSTAT, req_id, tw.getBytes());
-
- expectStatusOKMessage(req_id);
- }
-
- /**
- * Modify the attributes of a file. Used for operations such as changing
- * the ownership, permissions or access times, as well as for truncating a file.
- *
- * @param handle a SFTPv3FileHandle handle
- * @param attr A SFTPv3FileAttributes object. Specifies the modifications to be
- * made to the attributes of the file. Empty fields will be ignored.
- * @throws IOException
- */
- public void fsetstat(SFTPv3FileHandle handle, SFTPv3FileAttributes attr) throws IOException
- {
- checkHandleValidAndOpen(handle);
-
- int req_id = generateNextRequestID();
-
- TypesWriter tw = new TypesWriter();
- tw.writeString(handle.fileHandle, 0, handle.fileHandle.length);
- tw.writeBytes(createAttrs(attr));
-
- if (debug != null)
- {
- debug.println("Sending SSH_FXP_FSETSTAT...");
- debug.flush();
- }
-
- sendMessage(Packet.SSH_FXP_FSETSTAT, req_id, tw.getBytes());
-
- expectStatusOKMessage(req_id);
- }
-
- /**
- * Create a symbolic link on the server. Creates a link "src" that points
- * to "target".
- *
- * @param src See the {@link SFTPv3Client comment} for the class for more details.
- * @param target See the {@link SFTPv3Client comment} for the class for more details.
- * @throws IOException
- */
- public void createSymlink(String src, String target) throws IOException
- {
- int req_id = generateNextRequestID();
-
- /* Either I am too stupid to understand the SFTP draft
- * or the OpenSSH guys changed the semantics of src and target.
- */
-
- TypesWriter tw = new TypesWriter();
- tw.writeString(target, charsetName);
- tw.writeString(src, charsetName);
-
- if (debug != null)
- {
- debug.println("Sending SSH_FXP_SYMLINK...");
- debug.flush();
- }
-
- sendMessage(Packet.SSH_FXP_SYMLINK, req_id, tw.getBytes());
-
- expectStatusOKMessage(req_id);
- }
-
- /**
- * Have the server canonicalize any given path name to an absolute path.
- * This is useful for converting path names containing ".." components or
- * relative pathnames without a leading slash into absolute paths.
- *
- * @param path See the {@link SFTPv3Client comment} for the class for more details.
- * @return An absolute path.
- * @throws IOException
- */
- public String canonicalPath(String path) throws IOException
- {
- int req_id = generateNextRequestID();
-
- TypesWriter tw = new TypesWriter();
- tw.writeString(path, charsetName);
-
- if (debug != null)
- {
- debug.println("Sending SSH_FXP_REALPATH...");
- debug.flush();
- }
-
- sendMessage(Packet.SSH_FXP_REALPATH, req_id, tw.getBytes());
-
- byte[] resp = receiveMessage(34000);
-
- if (debug != null)
- {
- debug.println("Got REPLY.");
- debug.flush();
- }
-
- TypesReader tr = new TypesReader(resp);
-
- int t = tr.readByte();
-
- int rep_id = tr.readUINT32();
- if (rep_id != req_id)
- throw new IOException("The server sent an invalid id field.");
-
- if (t == Packet.SSH_FXP_NAME)
- {
- int count = tr.readUINT32();
-
- if (count != 1)
- throw new IOException("The server sent an invalid SSH_FXP_NAME packet.");
-
- return tr.readString(charsetName);
- }
-
- if (t != Packet.SSH_FXP_STATUS)
- throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
-
- int errorCode = tr.readUINT32();
-
- throw new SFTPException(tr.readString(), errorCode);
- }
-
- private final Vector scanDirectory(byte[] handle) throws IOException
- {
- Vector files = new Vector();
-
- while (true)
- {
- int req_id = generateNextRequestID();
-
- TypesWriter tw = new TypesWriter();
- tw.writeString(handle, 0, handle.length);
-
- if (debug != null)
- {
- debug.println("Sending SSH_FXP_READDIR...");
- debug.flush();
- }
-
- sendMessage(Packet.SSH_FXP_READDIR, req_id, tw.getBytes());
-
- /* Some servers send here a packet with size > 34000 */
- /* To whom it may concern: please learn to read the specs. */
-
- byte[] resp = receiveMessage(65536);
-
- if (debug != null)
- {
- debug.println("Got REPLY.");
- debug.flush();
- }
-
- TypesReader tr = new TypesReader(resp);
-
- int t = tr.readByte();
-
- int rep_id = tr.readUINT32();
- if (rep_id != req_id)
- throw new IOException("The server sent an invalid id field.");
-
- if (t == Packet.SSH_FXP_NAME)
- {
- int count = tr.readUINT32();
-
- if (debug != null)
- debug.println("Parsing " + count + " name entries...");
-
- while (count > 0)
- {
- SFTPv3DirectoryEntry dirEnt = new SFTPv3DirectoryEntry();
-
- dirEnt.filename = tr.readString(charsetName);
- dirEnt.longEntry = tr.readString(charsetName);
-
- dirEnt.attributes = readAttrs(tr);
- files.addElement(dirEnt);
-
- if (debug != null)
- debug.println("File: '" + dirEnt.filename + "'");
- count--;
- }
- continue;
- }
-
- if (t != Packet.SSH_FXP_STATUS)
- throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
-
- int errorCode = tr.readUINT32();
-
- if (errorCode == ErrorCodes.SSH_FX_EOF)
- return files;
-
- throw new SFTPException(tr.readString(), errorCode);
- }
- }
-
- private final byte[] openDirectory(String path) throws IOException
- {
- int req_id = generateNextRequestID();
-
- TypesWriter tw = new TypesWriter();
- tw.writeString(path, charsetName);
-
- if (debug != null)
- {
- debug.println("Sending SSH_FXP_OPENDIR...");
- debug.flush();
- }
-
- sendMessage(Packet.SSH_FXP_OPENDIR, req_id, tw.getBytes());
-
- byte[] resp = receiveMessage(34000);
-
- TypesReader tr = new TypesReader(resp);
-
- int t = tr.readByte();
-
- int rep_id = tr.readUINT32();
- if (rep_id != req_id)
- throw new IOException("The server sent an invalid id field.");
-
- if (t == Packet.SSH_FXP_HANDLE)
- {
- if (debug != null)
- {
- debug.println("Got SSH_FXP_HANDLE.");
- debug.flush();
- }
-
- byte[] handle = tr.readByteString();
- return handle;
- }
-
- if (t != Packet.SSH_FXP_STATUS)
- throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
-
- int errorCode = tr.readUINT32();
- String errorMessage = tr.readString();
-
- throw new SFTPException(errorMessage, errorCode);
- }
-
- private final String expandString(byte[] b, int off, int len)
- {
- StringBuffer sb = new StringBuffer();
-
- for (int i = 0; i < len; i++)
- {
- int c = b[off + i] & 0xff;
-
- if ((c >= 32) && (c <= 126))
- {
- sb.append((char) c);
- }
- else
- {
- sb.append("{0x" + Integer.toHexString(c) + "}");
- }
- }
-
- return sb.toString();
- }
-
- private void init() throws IOException
- {
- /* Send SSH_FXP_INIT (version 3) */
-
- final int client_version = 3;
-
- if (debug != null)
- debug.println("Sending SSH_FXP_INIT (" + client_version + ")...");
-
- TypesWriter tw = new TypesWriter();
- tw.writeUINT32(client_version);
- sendMessage(Packet.SSH_FXP_INIT, 0, tw.getBytes());
-
- /* Receive SSH_FXP_VERSION */
-
- if (debug != null)
- debug.println("Waiting for SSH_FXP_VERSION...");
-
- TypesReader tr = new TypesReader(receiveMessage(34000)); /* Should be enough for any reasonable server */
-
- int type = tr.readByte();
-
- if (type != Packet.SSH_FXP_VERSION)
- {
- throw new IOException("The server did not send a SSH_FXP_VERSION packet (got " + type + ")");
- }
-
- protocol_version = tr.readUINT32();
-
- if (debug != null)
- debug.println("SSH_FXP_VERSION: protocol_version = " + protocol_version);
-
- if (protocol_version != 3)
- throw new IOException("Server version " + protocol_version + " is currently not supported");
-
- /* Read and save extensions (if any) for later use */
-
- while (tr.remain() != 0)
- {
- String name = tr.readString();
- byte[] value = tr.readByteString();
- server_extensions.put(name, value);
-
- if (debug != null)
- debug.println("SSH_FXP_VERSION: extension: " + name + " = '" + expandString(value, 0, value.length)
- + "'");
- }
- }
-
- /**
- * Returns the negotiated SFTP protocol version between the client and the server.
- *
- * @return SFTP protocol version, i.e., "3".
- *
- */
- public int getProtocolVersion()
- {
- return protocol_version;
- }
-
- /**
- * Close this SFTP session. NEVER forget to call this method to free up
- * resources - even if you got an exception from one of the other methods.
- * Sometimes these other methods may throw an exception, saying that the
- * underlying channel is closed (this can happen, e.g., if the other server
- * sent a close message.) However, as long as you have not called the
- * <code>close()</code> method, you are likely wasting resources.
- *
- */
- public void close()
- {
- sess.close();
- }
-
- /**
- * List the contents of a directory.
- *
- * @param dirName See the {@link SFTPv3Client comment} for the class for more details.
- * @return A Vector containing {@link SFTPv3DirectoryEntry} objects.
- * @throws IOException
- */
- public Vector ls(String dirName) throws IOException
- {
- byte[] handle = openDirectory(dirName);
- Vector result = scanDirectory(handle);
- closeHandle(handle);
- return result;
- }
-
- /**
- * Create a new directory.
- *
- * @param dirName See the {@link SFTPv3Client comment} for the class for more details.
- * @param posixPermissions the permissions for this directory, e.g., "0700" (remember that
- * this is octal noation). The server will likely apply a umask.
- *
- * @throws IOException
- */
- public void mkdir(String dirName, int posixPermissions) throws IOException
- {
- int req_id = generateNextRequestID();
-
- TypesWriter tw = new TypesWriter();
- tw.writeString(dirName, charsetName);
- tw.writeUINT32(AttribFlags.SSH_FILEXFER_ATTR_PERMISSIONS);
- tw.writeUINT32(posixPermissions);
-
- sendMessage(Packet.SSH_FXP_MKDIR, req_id, tw.getBytes());
-
- expectStatusOKMessage(req_id);
- }
-
- /**
- * Remove a file.
- *
- * @param fileName See the {@link SFTPv3Client comment} for the class for more details.
- * @throws IOException
- */
- public void rm(String fileName) throws IOException
- {
- int req_id = generateNextRequestID();
-
- TypesWriter tw = new TypesWriter();
- tw.writeString(fileName, charsetName);
-
- sendMessage(Packet.SSH_FXP_REMOVE, req_id, tw.getBytes());
-
- expectStatusOKMessage(req_id);
- }
-
- /**
- * Remove an empty directory.
- *
- * @param dirName See the {@link SFTPv3Client comment} for the class for more details.
- * @throws IOException
- */
- public void rmdir(String dirName) throws IOException
- {
- int req_id = generateNextRequestID();
-
- TypesWriter tw = new TypesWriter();
- tw.writeString(dirName, charsetName);
-
- sendMessage(Packet.SSH_FXP_RMDIR, req_id, tw.getBytes());
-
- expectStatusOKMessage(req_id);
- }
-
- /**
- * Move a file or directory.
- *
- * @param oldPath See the {@link SFTPv3Client comment} for the class for more details.
- * @param newPath See the {@link SFTPv3Client comment} for the class for more details.
- * @throws IOException
- */
- public void mv(String oldPath, String newPath) throws IOException
- {
- int req_id = generateNextRequestID();
-
- TypesWriter tw = new TypesWriter();
- tw.writeString(oldPath, charsetName);
- tw.writeString(newPath, charsetName);
-
- sendMessage(Packet.SSH_FXP_RENAME, req_id, tw.getBytes());
-
- expectStatusOKMessage(req_id);
- }
-
- /**
- * Open a file for reading.
- *
- * @param fileName See the {@link SFTPv3Client comment} for the class for more details.
- * @return a SFTPv3FileHandle handle
- * @throws IOException
- */
- public SFTPv3FileHandle openFileRO(String fileName) throws IOException
- {
- return openFile(fileName, 0x00000001, null); // SSH_FXF_READ
- }
-
- /**
- * Open a file for reading and writing.
- *
- * @param fileName See the {@link SFTPv3Client comment} for the class for more details.
- * @return a SFTPv3FileHandle handle
- * @throws IOException
- */
- public SFTPv3FileHandle openFileRW(String fileName) throws IOException
- {
- return openFile(fileName, 0x00000003, null); // SSH_FXF_READ | SSH_FXF_WRITE
- }
-
- // Append is broken (already in the specification, because there is no way to
- // send a write operation (what offset to use??))
- // public SFTPv3FileHandle openFileRWAppend(String fileName) throws IOException
- // {
- // return openFile(fileName, 0x00000007, null); // SSH_FXF_READ | SSH_FXF_WRITE | SSH_FXF_APPEND
- // }
-
- /**
- * Create a file and open it for reading and writing.
- * Same as {@link #createFile(String, SFTPv3FileAttributes) createFile(fileName, null)}.
- *
- * @param fileName See the {@link SFTPv3Client comment} for the class for more details.
- * @return a SFTPv3FileHandle handle
- * @throws IOException
- */
- public SFTPv3FileHandle createFile(String fileName) throws IOException
- {
- return createFile(fileName, null);
- }
-
- /**
- * Create a file and open it for reading and writing.
- * You can specify the default attributes of the file (the server may or may
- * not respect your wishes).
- *
- * @param fileName See the {@link SFTPv3Client comment} for the class for more details.
- * @param attr may be <code>null</code> to use server defaults. Probably only
- * the <code>uid</code>, <code>gid</code> and <code>permissions</code>
- * (remember the server may apply a umask) entries of the {@link SFTPv3FileHandle}
- * structure make sense. You need only to set those fields where you want
- * to override the server's defaults.
- * @return a SFTPv3FileHandle handle
- * @throws IOException
- */
- public SFTPv3FileHandle createFile(String fileName, SFTPv3FileAttributes attr) throws IOException
- {
- return openFile(fileName, 0x00000008 | 0x00000003, attr); // SSH_FXF_CREAT | SSH_FXF_READ | SSH_FXF_WRITE
- }
-
- /**
- * Create a file (truncate it if it already exists) and open it for reading and writing.
- * Same as {@link #createFileTruncate(String, SFTPv3FileAttributes) createFileTruncate(fileName, null)}.
- *
- * @param fileName See the {@link SFTPv3Client comment} for the class for more details.
- * @return a SFTPv3FileHandle handle
- * @throws IOException
- */
- public SFTPv3FileHandle createFileTruncate(String fileName) throws IOException
- {
- return createFileTruncate(fileName, null);
- }
-
- /**
- * reate a file (truncate it if it already exists) and open it for reading and writing.
- * You can specify the default attributes of the file (the server may or may
- * not respect your wishes).
- *
- * @param fileName See the {@link SFTPv3Client comment} for the class for more details.
- * @param attr may be <code>null</code> to use server defaults. Probably only
- * the <code>uid</code>, <code>gid</code> and <code>permissions</code>
- * (remember the server may apply a umask) entries of the {@link SFTPv3FileHandle}
- * structure make sense. You need only to set those fields where you want
- * to override the server's defaults.
- * @return a SFTPv3FileHandle handle
- * @throws IOException
- */
- public SFTPv3FileHandle createFileTruncate(String fileName, SFTPv3FileAttributes attr) throws IOException
- {
- return openFile(fileName, 0x00000018 | 0x00000003, attr); // SSH_FXF_CREAT | SSH_FXF_TRUNC | SSH_FXF_READ | SSH_FXF_WRITE
- }
-
- private byte[] createAttrs(SFTPv3FileAttributes attr)
- {
- TypesWriter tw = new TypesWriter();
-
- int attrFlags = 0;
-
- if (attr == null)
- {
- tw.writeUINT32(0);
- }
- else
- {
- if (attr.size != null)
- attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_SIZE;
-
- if ((attr.uid != null) && (attr.gid != null))
- attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_V3_UIDGID;
-
- if (attr.permissions != null)
- attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_PERMISSIONS;
-
- if ((attr.atime != null) && (attr.mtime != null))
- attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_V3_ACMODTIME;
-
- tw.writeUINT32(attrFlags);
-
- if (attr.size != null)
- tw.writeUINT64(attr.size.longValue());
-
- if ((attr.uid != null) && (attr.gid != null))
- {
- tw.writeUINT32(attr.uid.intValue());
- tw.writeUINT32(attr.gid.intValue());
- }
-
- if (attr.permissions != null)
- tw.writeUINT32(attr.permissions.intValue());
-
- if ((attr.atime != null) && (attr.mtime != null))
- {
- tw.writeUINT32(attr.atime.intValue());
- tw.writeUINT32(attr.mtime.intValue());
- }
- }
-
- return tw.getBytes();
- }
-
- private SFTPv3FileHandle openFile(String fileName, int flags, SFTPv3FileAttributes attr) throws IOException
- {
- int req_id = generateNextRequestID();
-
- TypesWriter tw = new TypesWriter();
- tw.writeString(fileName, charsetName);
- tw.writeUINT32(flags);
- tw.writeBytes(createAttrs(attr));
-
- if (debug != null)
- {
- debug.println("Sending SSH_FXP_OPEN...");
- debug.flush();
- }
-
- sendMessage(Packet.SSH_FXP_OPEN, req_id, tw.getBytes());
-
- byte[] resp = receiveMessage(34000);
-
- TypesReader tr = new TypesReader(resp);
-
- int t = tr.readByte();
-
- int rep_id = tr.readUINT32();
- if (rep_id != req_id)
- throw new IOException("The server sent an invalid id field.");
-
- if (t == Packet.SSH_FXP_HANDLE)
- {
- if (debug != null)
- {
- debug.println("Got SSH_FXP_HANDLE.");
- debug.flush();
- }
-
- return new SFTPv3FileHandle(this, tr.readByteString());
- }
-
- if (t != Packet.SSH_FXP_STATUS)
- throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
-
- int errorCode = tr.readUINT32();
- String errorMessage = tr.readString();
-
- throw new SFTPException(errorMessage, errorCode);
- }
-
- /**
- * Read bytes from a file. No more than 32768 bytes may be read at once.
- * Be aware that the semantics of read() are different than for Java streams.
- * <p>
- * <ul>
- * <li>The server will read as many bytes as it can from the file (up to <code>len</code>),
- * and return them.</li>
- * <li>If EOF is encountered before reading any data, <code>-1</code> is returned.
- * <li>If an error occurs, an exception is thrown</li>.
- * <li>For normal disk files, it is guaranteed that the server will return the specified
- * number of bytes, or up to end of file. For, e.g., device files this may return
- * fewer bytes than requested.</li>
- * </ul>
- *
- * @param handle a SFTPv3FileHandle handle
- * @param fileOffset offset (in bytes) in the file
- * @param dst the destination byte array
- * @param dstoff offset in the destination byte array
- * @param len how many bytes to read, 0 &lt; len &lt;= 32768 bytes
- * @return the number of bytes that could be read, may be less than requested if
- * the end of the file is reached, -1 is returned in case of <code>EOF</code>
- * @throws IOException
- */
- public int read(SFTPv3FileHandle handle, long fileOffset, byte[] dst, int dstoff, int len) throws IOException
- {
- checkHandleValidAndOpen(handle);
-
- if ((len > 32768) || (len <= 0))
- throw new IllegalArgumentException("invalid len argument");
-
- int req_id = generateNextRequestID();
-
- TypesWriter tw = new TypesWriter();
- tw.writeString(handle.fileHandle, 0, handle.fileHandle.length);
- tw.writeUINT64(fileOffset);
- tw.writeUINT32(len);
-
- if (debug != null)
- {
- debug.println("Sending SSH_FXP_READ...");
- debug.flush();
- }
-
- sendMessage(Packet.SSH_FXP_READ, req_id, tw.getBytes());
-
- byte[] resp = receiveMessage(34000);
-
- TypesReader tr = new TypesReader(resp);
-
- int t = tr.readByte();
-
- int rep_id = tr.readUINT32();
- if (rep_id != req_id)
- throw new IOException("The server sent an invalid id field.");
-
- if (t == Packet.SSH_FXP_DATA)
- {
- if (debug != null)
- {
- debug.println("Got SSH_FXP_DATA...");
- debug.flush();
- }
-
- int readLen = tr.readUINT32();
-
- if ((readLen < 0) || (readLen > len))
- throw new IOException("The server sent an invalid length field.");
-
- tr.readBytes(dst, dstoff, readLen);
-
- return readLen;
- }
-
- if (t != Packet.SSH_FXP_STATUS)
- throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
-
- int errorCode = tr.readUINT32();
-
- if (errorCode == ErrorCodes.SSH_FX_EOF)
- {
- if (debug != null)
- {
- debug.println("Got SSH_FX_EOF.");
- debug.flush();
- }
-
- return -1;
- }
-
- String errorMessage = tr.readString();
-
- throw new SFTPException(errorMessage, errorCode);
- }
-
- /**
- * Write bytes to a file. If <code>len</code> &gt; 32768, then the write operation will
- * be split into multiple writes.
- *
- * @param handle a SFTPv3FileHandle handle.
- * @param fileOffset offset (in bytes) in the file.
- * @param src the source byte array.
- * @param srcoff offset in the source byte array.
- * @param len how many bytes to write.
- * @throws IOException
- */
- public void write(SFTPv3FileHandle handle, long fileOffset, byte[] src, int srcoff, int len) throws IOException
- {
- checkHandleValidAndOpen(handle);
-
- while (len > 0)
- {
- int writeRequestLen = len;
-
- if (writeRequestLen > 32768)
- writeRequestLen = 32768;
-
- int req_id = generateNextRequestID();
-
- TypesWriter tw = new TypesWriter();
- tw.writeString(handle.fileHandle, 0, handle.fileHandle.length);
- tw.writeUINT64(fileOffset);
- tw.writeString(src, srcoff, writeRequestLen);
-
- if (debug != null)
- {
- debug.println("Sending SSH_FXP_WRITE...");
- debug.flush();
- }
-
- sendMessage(Packet.SSH_FXP_WRITE, req_id, tw.getBytes());
-
- fileOffset += writeRequestLen;
-
- srcoff += writeRequestLen;
- len -= writeRequestLen;
-
- byte[] resp = receiveMessage(34000);
-
- TypesReader tr = new TypesReader(resp);
-
- int t = tr.readByte();
-
- int rep_id = tr.readUINT32();
- if (rep_id != req_id)
- throw new IOException("The server sent an invalid id field.");
-
- if (t != Packet.SSH_FXP_STATUS)
- throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
-
- int errorCode = tr.readUINT32();
-
- if (errorCode == ErrorCodes.SSH_FX_OK)
- continue;
-
- String errorMessage = tr.readString();
-
- throw new SFTPException(errorMessage, errorCode);
- }
- }
-
- /**
- * Close a file.
- *
- * @param handle a SFTPv3FileHandle handle
- * @throws IOException
- */
- public void closeFile(SFTPv3FileHandle handle) throws IOException
- {
- if (handle == null)
- throw new IllegalArgumentException("the handle argument may not be null");
-
- try
- {
- if (handle.isClosed == false)
- {
- closeHandle(handle.fileHandle);
- }
- }
- finally
- {
- handle.isClosed = true;
- }
- }
-}
+
+package com.trilead.ssh2;
+
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.nio.charset.Charset;
+import java.util.HashMap;
+import java.util.Vector;
+
+import com.trilead.ssh2.packets.TypesReader;
+import com.trilead.ssh2.packets.TypesWriter;
+import com.trilead.ssh2.sftp.AttribFlags;
+import com.trilead.ssh2.sftp.ErrorCodes;
+import com.trilead.ssh2.sftp.Packet;
+
+
+/**
+ * A <code>SFTPv3Client</code> represents a SFTP (protocol version 3)
+ * client connection tunnelled over a SSH-2 connection. This is a very simple
+ * (synchronous) implementation.
+ * <p>
+ * Basically, most methods in this class map directly to one of
+ * the packet types described in draft-ietf-secsh-filexfer-02.txt.
+ * <p>
+ * Note: this is experimental code.
+ * <p>
+ * Error handling: the methods of this class throw IOExceptions. However, unless
+ * there is catastrophic failure, exceptions of the type {@link SFTPv3Client} will
+ * be thrown (a subclass of IOException). Therefore, you can implement more verbose
+ * behavior by checking if a thrown exception if of this type. If yes, then you
+ * can cast the exception and access detailed information about the failure.
+ * <p>
+ * Notes about file names, directory names and paths, copy-pasted
+ * from the specs:
+ * <ul>
+ * <li>SFTP v3 represents file names as strings. File names are
+ * assumed to use the slash ('/') character as a directory separator.</li>
+ * <li>File names starting with a slash are "absolute", and are relative to
+ * the root of the file system. Names starting with any other character
+ * are relative to the user's default directory (home directory).</li>
+ * <li>Servers SHOULD interpret a path name component ".." as referring to
+ * the parent directory, and "." as referring to the current directory.
+ * If the server implementation limits access to certain parts of the
+ * file system, it must be extra careful in parsing file names when
+ * enforcing such restrictions. There have been numerous reported
+ * security bugs where a ".." in a path name has allowed access outside
+ * the intended area.</li>
+ * <li>An empty path name is valid, and it refers to the user's default
+ * directory (usually the user's home directory).</li>
+ * </ul>
+ * <p>
+ * If you are still not tired then please go on and read the comment for
+ * {@link #setCharset(String)}.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: SFTPv3Client.java,v 1.3 2008/04/01 12:38:09 cplattne Exp $
+ */
+public class SFTPv3Client
+{
+ final Connection conn;
+ final Session sess;
+ final PrintStream debug;
+
+ boolean flag_closed = false;
+
+ InputStream is;
+ OutputStream os;
+
+ int protocol_version = 0;
+ HashMap server_extensions = new HashMap();
+
+ int next_request_id = 1000;
+
+ String charsetName = null;
+
+ /**
+ * Create a SFTP v3 client.
+ *
+ * @param conn The underlying SSH-2 connection to be used.
+ * @param debug
+ * @throws IOException
+ *
+ * @deprecated this constructor (debug version) will disappear in the future,
+ * use {@link #SFTPv3Client(Connection)} instead.
+ */
+ public SFTPv3Client(Connection conn, PrintStream debug) throws IOException
+ {
+ if (conn == null)
+ throw new IllegalArgumentException("Cannot accept null argument!");
+
+ this.conn = conn;
+ this.debug = debug;
+
+ if (debug != null)
+ debug.println("Opening session and starting SFTP subsystem.");
+
+ sess = conn.openSession();
+ sess.startSubSystem("sftp");
+
+ is = sess.getStdout();
+ os = new BufferedOutputStream(sess.getStdin(), 2048);
+
+ if ((is == null) || (os == null))
+ throw new IOException("There is a problem with the streams of the underlying channel.");
+
+ init();
+ }
+
+ /**
+ * Create a SFTP v3 client.
+ *
+ * @param conn The underlying SSH-2 connection to be used.
+ * @throws IOException
+ */
+ public SFTPv3Client(Connection conn) throws IOException
+ {
+ this(conn, null);
+ }
+
+ /**
+ * Set the charset used to convert between Java Unicode Strings and byte encodings
+ * used by the server for paths and file names. Unfortunately, the SFTP v3 draft
+ * says NOTHING about such conversions (well, with the exception of error messages
+ * which have to be in UTF-8). Newer drafts specify to use UTF-8 for file names
+ * (if I remember correctly). However, a quick test using OpenSSH serving a EXT-3
+ * filesystem has shown that UTF-8 seems to be a bad choice for SFTP v3 (tested with
+ * filenames containing german umlauts). "windows-1252" seems to work better for Europe.
+ * Luckily, "windows-1252" is the platform default in my case =).
+ * <p>
+ * If you don't set anything, then the platform default will be used (this is the default
+ * behavior).
+ *
+ * @see #getCharset()
+ *
+ * @param charset the name of the charset to be used or <code>null</code> to use the platform's
+ * default encoding.
+ * @throws IOException
+ */
+ public void setCharset(String charset) throws IOException
+ {
+ if (charset == null)
+ {
+ charsetName = charset;
+ return;
+ }
+
+ try
+ {
+ Charset.forName(charset);
+ }
+ catch (Exception e)
+ {
+ throw (IOException) new IOException("This charset is not supported").initCause(e);
+ }
+ charsetName = charset;
+ }
+
+ /**
+ * The currently used charset for filename encoding/decoding.
+ *
+ * @see #setCharset(String)
+ *
+ * @return The name of the charset (<code>null</code> if the platform's default charset is being used)
+ */
+ public String getCharset()
+ {
+ return charsetName;
+ }
+
+ private final void checkHandleValidAndOpen(SFTPv3FileHandle handle) throws IOException
+ {
+ if (handle.client != this)
+ throw new IOException("The file handle was created with another SFTPv3FileHandle instance.");
+
+ if (handle.isClosed == true)
+ throw new IOException("The file handle is closed.");
+ }
+
+ private final void sendMessage(int type, int requestId, byte[] msg, int off, int len) throws IOException
+ {
+ int msglen = len + 1;
+
+ if (type != Packet.SSH_FXP_INIT)
+ msglen += 4;
+
+ os.write(msglen >> 24);
+ os.write(msglen >> 16);
+ os.write(msglen >> 8);
+ os.write(msglen);
+ os.write(type);
+
+ if (type != Packet.SSH_FXP_INIT)
+ {
+ os.write(requestId >> 24);
+ os.write(requestId >> 16);
+ os.write(requestId >> 8);
+ os.write(requestId);
+ }
+
+ os.write(msg, off, len);
+ os.flush();
+ }
+
+ private final void sendMessage(int type, int requestId, byte[] msg) throws IOException
+ {
+ sendMessage(type, requestId, msg, 0, msg.length);
+ }
+
+ private final void readBytes(byte[] buff, int pos, int len) throws IOException
+ {
+ while (len > 0)
+ {
+ int count = is.read(buff, pos, len);
+ if (count < 0)
+ throw new IOException("Unexpected end of sftp stream.");
+ if ((count == 0) || (count > len))
+ throw new IOException("Underlying stream implementation is bogus!");
+ len -= count;
+ pos += count;
+ }
+ }
+
+ /**
+ * Read a message and guarantee that the <b>contents</b> is not larger than
+ * <code>maxlen</code> bytes.
+ * <p>
+ * Note: receiveMessage(34000) actually means that the message may be up to 34004
+ * bytes (the length attribute preceeding the contents is 4 bytes).
+ *
+ * @param maxlen
+ * @return the message contents
+ * @throws IOException
+ */
+ private final byte[] receiveMessage(int maxlen) throws IOException
+ {
+ byte[] msglen = new byte[4];
+
+ readBytes(msglen, 0, 4);
+
+ int len = (((msglen[0] & 0xff) << 24) | ((msglen[1] & 0xff) << 16) | ((msglen[2] & 0xff) << 8) | (msglen[3] & 0xff));
+
+ if ((len > maxlen) || (len <= 0))
+ throw new IOException("Illegal sftp packet len: " + len);
+
+ byte[] msg = new byte[len];
+
+ readBytes(msg, 0, len);
+
+ return msg;
+ }
+
+ private final int generateNextRequestID()
+ {
+ synchronized (this)
+ {
+ return next_request_id++;
+ }
+ }
+
+ private final void closeHandle(byte[] handle) throws IOException
+ {
+ int req_id = generateNextRequestID();
+
+ TypesWriter tw = new TypesWriter();
+ tw.writeString(handle, 0, handle.length);
+
+ sendMessage(Packet.SSH_FXP_CLOSE, req_id, tw.getBytes());
+
+ expectStatusOKMessage(req_id);
+ }
+
+ private SFTPv3FileAttributes readAttrs(TypesReader tr) throws IOException
+ {
+ /*
+ * uint32 flags
+ * uint64 size present only if flag SSH_FILEXFER_ATTR_SIZE
+ * uint32 uid present only if flag SSH_FILEXFER_ATTR_V3_UIDGID
+ * uint32 gid present only if flag SSH_FILEXFER_ATTR_V3_UIDGID
+ * uint32 permissions present only if flag SSH_FILEXFER_ATTR_PERMISSIONS
+ * uint32 atime present only if flag SSH_FILEXFER_ATTR_V3_ACMODTIME
+ * uint32 mtime present only if flag SSH_FILEXFER_ATTR_V3_ACMODTIME
+ * uint32 extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED
+ * string extended_type
+ * string extended_data
+ * ... more extended data (extended_type - extended_data pairs),
+ * so that number of pairs equals extended_count
+ */
+
+ SFTPv3FileAttributes fa = new SFTPv3FileAttributes();
+
+ int flags = tr.readUINT32();
+
+ if ((flags & AttribFlags.SSH_FILEXFER_ATTR_SIZE) != 0)
+ {
+ if (debug != null)
+ debug.println("SSH_FILEXFER_ATTR_SIZE");
+ fa.size = new Long(tr.readUINT64());
+ }
+
+ if ((flags & AttribFlags.SSH_FILEXFER_ATTR_V3_UIDGID) != 0)
+ {
+ if (debug != null)
+ debug.println("SSH_FILEXFER_ATTR_V3_UIDGID");
+ fa.uid = new Integer(tr.readUINT32());
+ fa.gid = new Integer(tr.readUINT32());
+ }
+
+ if ((flags & AttribFlags.SSH_FILEXFER_ATTR_PERMISSIONS) != 0)
+ {
+ if (debug != null)
+ debug.println("SSH_FILEXFER_ATTR_PERMISSIONS");
+ fa.permissions = new Integer(tr.readUINT32());
+ }
+
+ if ((flags & AttribFlags.SSH_FILEXFER_ATTR_V3_ACMODTIME) != 0)
+ {
+ if (debug != null)
+ debug.println("SSH_FILEXFER_ATTR_V3_ACMODTIME");
+ fa.atime = new Long(((long)tr.readUINT32()) & 0xffffffffl);
+ fa.mtime = new Long(((long)tr.readUINT32()) & 0xffffffffl);
+
+ }
+
+ if ((flags & AttribFlags.SSH_FILEXFER_ATTR_EXTENDED) != 0)
+ {
+ int count = tr.readUINT32();
+
+ if (debug != null)
+ debug.println("SSH_FILEXFER_ATTR_EXTENDED (" + count + ")");
+
+ /* Read it anyway to detect corrupt packets */
+
+ while (count > 0)
+ {
+ tr.readByteString();
+ tr.readByteString();
+ count--;
+ }
+ }
+
+ return fa;
+ }
+
+ /**
+ * Retrieve the file attributes of an open file.
+ *
+ * @param handle a SFTPv3FileHandle handle.
+ * @return a SFTPv3FileAttributes object.
+ * @throws IOException
+ */
+ public SFTPv3FileAttributes fstat(SFTPv3FileHandle handle) throws IOException
+ {
+ checkHandleValidAndOpen(handle);
+
+ int req_id = generateNextRequestID();
+
+ TypesWriter tw = new TypesWriter();
+ tw.writeString(handle.fileHandle, 0, handle.fileHandle.length);
+
+ if (debug != null)
+ {
+ debug.println("Sending SSH_FXP_FSTAT...");
+ debug.flush();
+ }
+
+ sendMessage(Packet.SSH_FXP_FSTAT, req_id, tw.getBytes());
+
+ byte[] resp = receiveMessage(34000);
+
+ if (debug != null)
+ {
+ debug.println("Got REPLY.");
+ debug.flush();
+ }
+
+ TypesReader tr = new TypesReader(resp);
+
+ int t = tr.readByte();
+
+ int rep_id = tr.readUINT32();
+ if (rep_id != req_id)
+ throw new IOException("The server sent an invalid id field.");
+
+ if (t == Packet.SSH_FXP_ATTRS)
+ {
+ return readAttrs(tr);
+ }
+
+ if (t != Packet.SSH_FXP_STATUS)
+ throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
+
+ int errorCode = tr.readUINT32();
+
+ throw new SFTPException(tr.readString(), errorCode);
+ }
+
+ private SFTPv3FileAttributes statBoth(String path, int statMethod) throws IOException
+ {
+ int req_id = generateNextRequestID();
+
+ TypesWriter tw = new TypesWriter();
+ tw.writeString(path, charsetName);
+
+ if (debug != null)
+ {
+ debug.println("Sending SSH_FXP_STAT/SSH_FXP_LSTAT...");
+ debug.flush();
+ }
+
+ sendMessage(statMethod, req_id, tw.getBytes());
+
+ byte[] resp = receiveMessage(34000);
+
+ if (debug != null)
+ {
+ debug.println("Got REPLY.");
+ debug.flush();
+ }
+
+ TypesReader tr = new TypesReader(resp);
+
+ int t = tr.readByte();
+
+ int rep_id = tr.readUINT32();
+ if (rep_id != req_id)
+ throw new IOException("The server sent an invalid id field.");
+
+ if (t == Packet.SSH_FXP_ATTRS)
+ {
+ return readAttrs(tr);
+ }
+
+ if (t != Packet.SSH_FXP_STATUS)
+ throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
+
+ int errorCode = tr.readUINT32();
+
+ throw new SFTPException(tr.readString(), errorCode);
+ }
+
+ /**
+ * Retrieve the file attributes of a file. This method
+ * follows symbolic links on the server.
+ *
+ * @see #lstat(String)
+ *
+ * @param path See the {@link SFTPv3Client comment} for the class for more details.
+ * @return a SFTPv3FileAttributes object.
+ * @throws IOException
+ */
+ public SFTPv3FileAttributes stat(String path) throws IOException
+ {
+ return statBoth(path, Packet.SSH_FXP_STAT);
+ }
+
+ /**
+ * Retrieve the file attributes of a file. This method
+ * does NOT follow symbolic links on the server.
+ *
+ * @see #stat(String)
+ *
+ * @param path See the {@link SFTPv3Client comment} for the class for more details.
+ * @return a SFTPv3FileAttributes object.
+ * @throws IOException
+ */
+ public SFTPv3FileAttributes lstat(String path) throws IOException
+ {
+ return statBoth(path, Packet.SSH_FXP_LSTAT);
+ }
+
+ /**
+ * Read the target of a symbolic link.
+ *
+ * @param path See the {@link SFTPv3Client comment} for the class for more details.
+ * @return The target of the link.
+ * @throws IOException
+ */
+ public String readLink(String path) throws IOException
+ {
+ int req_id = generateNextRequestID();
+
+ TypesWriter tw = new TypesWriter();
+ tw.writeString(path, charsetName);
+
+ if (debug != null)
+ {
+ debug.println("Sending SSH_FXP_READLINK...");
+ debug.flush();
+ }
+
+ sendMessage(Packet.SSH_FXP_READLINK, req_id, tw.getBytes());
+
+ byte[] resp = receiveMessage(34000);
+
+ if (debug != null)
+ {
+ debug.println("Got REPLY.");
+ debug.flush();
+ }
+
+ TypesReader tr = new TypesReader(resp);
+
+ int t = tr.readByte();
+
+ int rep_id = tr.readUINT32();
+ if (rep_id != req_id)
+ throw new IOException("The server sent an invalid id field.");
+
+ if (t == Packet.SSH_FXP_NAME)
+ {
+ int count = tr.readUINT32();
+
+ if (count != 1)
+ throw new IOException("The server sent an invalid SSH_FXP_NAME packet.");
+
+ return tr.readString(charsetName);
+ }
+
+ if (t != Packet.SSH_FXP_STATUS)
+ throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
+
+ int errorCode = tr.readUINT32();
+
+ throw new SFTPException(tr.readString(), errorCode);
+ }
+
+ private void expectStatusOKMessage(int id) throws IOException
+ {
+ byte[] resp = receiveMessage(34000);
+
+ if (debug != null)
+ {
+ debug.println("Got REPLY.");
+ debug.flush();
+ }
+
+ TypesReader tr = new TypesReader(resp);
+
+ int t = tr.readByte();
+
+ int rep_id = tr.readUINT32();
+ if (rep_id != id)
+ throw new IOException("The server sent an invalid id field.");
+
+ if (t != Packet.SSH_FXP_STATUS)
+ throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
+
+ int errorCode = tr.readUINT32();
+
+ if (errorCode == ErrorCodes.SSH_FX_OK)
+ return;
+
+ throw new SFTPException(tr.readString(), errorCode);
+ }
+
+ /**
+ * Modify the attributes of a file. Used for operations such as changing
+ * the ownership, permissions or access times, as well as for truncating a file.
+ *
+ * @param path See the {@link SFTPv3Client comment} for the class for more details.
+ * @param attr A SFTPv3FileAttributes object. Specifies the modifications to be
+ * made to the attributes of the file. Empty fields will be ignored.
+ * @throws IOException
+ */
+ public void setstat(String path, SFTPv3FileAttributes attr) throws IOException
+ {
+ int req_id = generateNextRequestID();
+
+ TypesWriter tw = new TypesWriter();
+ tw.writeString(path, charsetName);
+ tw.writeBytes(createAttrs(attr));
+
+ if (debug != null)
+ {
+ debug.println("Sending SSH_FXP_SETSTAT...");
+ debug.flush();
+ }
+
+ sendMessage(Packet.SSH_FXP_SETSTAT, req_id, tw.getBytes());
+
+ expectStatusOKMessage(req_id);
+ }
+
+ /**
+ * Modify the attributes of a file. Used for operations such as changing
+ * the ownership, permissions or access times, as well as for truncating a file.
+ *
+ * @param handle a SFTPv3FileHandle handle
+ * @param attr A SFTPv3FileAttributes object. Specifies the modifications to be
+ * made to the attributes of the file. Empty fields will be ignored.
+ * @throws IOException
+ */
+ public void fsetstat(SFTPv3FileHandle handle, SFTPv3FileAttributes attr) throws IOException
+ {
+ checkHandleValidAndOpen(handle);
+
+ int req_id = generateNextRequestID();
+
+ TypesWriter tw = new TypesWriter();
+ tw.writeString(handle.fileHandle, 0, handle.fileHandle.length);
+ tw.writeBytes(createAttrs(attr));
+
+ if (debug != null)
+ {
+ debug.println("Sending SSH_FXP_FSETSTAT...");
+ debug.flush();
+ }
+
+ sendMessage(Packet.SSH_FXP_FSETSTAT, req_id, tw.getBytes());
+
+ expectStatusOKMessage(req_id);
+ }
+
+ /**
+ * Create a symbolic link on the server. Creates a link "src" that points
+ * to "target".
+ *
+ * @param src See the {@link SFTPv3Client comment} for the class for more details.
+ * @param target See the {@link SFTPv3Client comment} for the class for more details.
+ * @throws IOException
+ */
+ public void createSymlink(String src, String target) throws IOException
+ {
+ int req_id = generateNextRequestID();
+
+ /* Either I am too stupid to understand the SFTP draft
+ * or the OpenSSH guys changed the semantics of src and target.
+ */
+
+ TypesWriter tw = new TypesWriter();
+ tw.writeString(target, charsetName);
+ tw.writeString(src, charsetName);
+
+ if (debug != null)
+ {
+ debug.println("Sending SSH_FXP_SYMLINK...");
+ debug.flush();
+ }
+
+ sendMessage(Packet.SSH_FXP_SYMLINK, req_id, tw.getBytes());
+
+ expectStatusOKMessage(req_id);
+ }
+
+ /**
+ * Have the server canonicalize any given path name to an absolute path.
+ * This is useful for converting path names containing ".." components or
+ * relative pathnames without a leading slash into absolute paths.
+ *
+ * @param path See the {@link SFTPv3Client comment} for the class for more details.
+ * @return An absolute path.
+ * @throws IOException
+ */
+ public String canonicalPath(String path) throws IOException
+ {
+ int req_id = generateNextRequestID();
+
+ TypesWriter tw = new TypesWriter();
+ tw.writeString(path, charsetName);
+
+ if (debug != null)
+ {
+ debug.println("Sending SSH_FXP_REALPATH...");
+ debug.flush();
+ }
+
+ sendMessage(Packet.SSH_FXP_REALPATH, req_id, tw.getBytes());
+
+ byte[] resp = receiveMessage(34000);
+
+ if (debug != null)
+ {
+ debug.println("Got REPLY.");
+ debug.flush();
+ }
+
+ TypesReader tr = new TypesReader(resp);
+
+ int t = tr.readByte();
+
+ int rep_id = tr.readUINT32();
+ if (rep_id != req_id)
+ throw new IOException("The server sent an invalid id field.");
+
+ if (t == Packet.SSH_FXP_NAME)
+ {
+ int count = tr.readUINT32();
+
+ if (count != 1)
+ throw new IOException("The server sent an invalid SSH_FXP_NAME packet.");
+
+ return tr.readString(charsetName);
+ }
+
+ if (t != Packet.SSH_FXP_STATUS)
+ throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
+
+ int errorCode = tr.readUINT32();
+
+ throw new SFTPException(tr.readString(), errorCode);
+ }
+
+ private final Vector scanDirectory(byte[] handle) throws IOException
+ {
+ Vector files = new Vector();
+
+ while (true)
+ {
+ int req_id = generateNextRequestID();
+
+ TypesWriter tw = new TypesWriter();
+ tw.writeString(handle, 0, handle.length);
+
+ if (debug != null)
+ {
+ debug.println("Sending SSH_FXP_READDIR...");
+ debug.flush();
+ }
+
+ sendMessage(Packet.SSH_FXP_READDIR, req_id, tw.getBytes());
+
+ /* Some servers send here a packet with size > 34000 */
+ /* To whom it may concern: please learn to read the specs. */
+
+ byte[] resp = receiveMessage(65536);
+
+ if (debug != null)
+ {
+ debug.println("Got REPLY.");
+ debug.flush();
+ }
+
+ TypesReader tr = new TypesReader(resp);
+
+ int t = tr.readByte();
+
+ int rep_id = tr.readUINT32();
+ if (rep_id != req_id)
+ throw new IOException("The server sent an invalid id field.");
+
+ if (t == Packet.SSH_FXP_NAME)
+ {
+ int count = tr.readUINT32();
+
+ if (debug != null)
+ debug.println("Parsing " + count + " name entries...");
+
+ while (count > 0)
+ {
+ SFTPv3DirectoryEntry dirEnt = new SFTPv3DirectoryEntry();
+
+ dirEnt.filename = tr.readString(charsetName);
+ dirEnt.longEntry = tr.readString(charsetName);
+
+ dirEnt.attributes = readAttrs(tr);
+ files.addElement(dirEnt);
+
+ if (debug != null)
+ debug.println("File: '" + dirEnt.filename + "'");
+ count--;
+ }
+ continue;
+ }
+
+ if (t != Packet.SSH_FXP_STATUS)
+ throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
+
+ int errorCode = tr.readUINT32();
+
+ if (errorCode == ErrorCodes.SSH_FX_EOF)
+ return files;
+
+ throw new SFTPException(tr.readString(), errorCode);
+ }
+ }
+
+ private final byte[] openDirectory(String path) throws IOException
+ {
+ int req_id = generateNextRequestID();
+
+ TypesWriter tw = new TypesWriter();
+ tw.writeString(path, charsetName);
+
+ if (debug != null)
+ {
+ debug.println("Sending SSH_FXP_OPENDIR...");
+ debug.flush();
+ }
+
+ sendMessage(Packet.SSH_FXP_OPENDIR, req_id, tw.getBytes());
+
+ byte[] resp = receiveMessage(34000);
+
+ TypesReader tr = new TypesReader(resp);
+
+ int t = tr.readByte();
+
+ int rep_id = tr.readUINT32();
+ if (rep_id != req_id)
+ throw new IOException("The server sent an invalid id field.");
+
+ if (t == Packet.SSH_FXP_HANDLE)
+ {
+ if (debug != null)
+ {
+ debug.println("Got SSH_FXP_HANDLE.");
+ debug.flush();
+ }
+
+ byte[] handle = tr.readByteString();
+ return handle;
+ }
+
+ if (t != Packet.SSH_FXP_STATUS)
+ throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
+
+ int errorCode = tr.readUINT32();
+ String errorMessage = tr.readString();
+
+ throw new SFTPException(errorMessage, errorCode);
+ }
+
+ private final String expandString(byte[] b, int off, int len)
+ {
+ StringBuffer sb = new StringBuffer();
+
+ for (int i = 0; i < len; i++)
+ {
+ int c = b[off + i] & 0xff;
+
+ if ((c >= 32) && (c <= 126))
+ {
+ sb.append((char) c);
+ }
+ else
+ {
+ sb.append("{0x" + Integer.toHexString(c) + "}");
+ }
+ }
+
+ return sb.toString();
+ }
+
+ private void init() throws IOException
+ {
+ /* Send SSH_FXP_INIT (version 3) */
+
+ final int client_version = 3;
+
+ if (debug != null)
+ debug.println("Sending SSH_FXP_INIT (" + client_version + ")...");
+
+ TypesWriter tw = new TypesWriter();
+ tw.writeUINT32(client_version);
+ sendMessage(Packet.SSH_FXP_INIT, 0, tw.getBytes());
+
+ /* Receive SSH_FXP_VERSION */
+
+ if (debug != null)
+ debug.println("Waiting for SSH_FXP_VERSION...");
+
+ TypesReader tr = new TypesReader(receiveMessage(34000)); /* Should be enough for any reasonable server */
+
+ int type = tr.readByte();
+
+ if (type != Packet.SSH_FXP_VERSION)
+ {
+ throw new IOException("The server did not send a SSH_FXP_VERSION packet (got " + type + ")");
+ }
+
+ protocol_version = tr.readUINT32();
+
+ if (debug != null)
+ debug.println("SSH_FXP_VERSION: protocol_version = " + protocol_version);
+
+ if (protocol_version != 3)
+ throw new IOException("Server version " + protocol_version + " is currently not supported");
+
+ /* Read and save extensions (if any) for later use */
+
+ while (tr.remain() != 0)
+ {
+ String name = tr.readString();
+ byte[] value = tr.readByteString();
+ server_extensions.put(name, value);
+
+ if (debug != null)
+ debug.println("SSH_FXP_VERSION: extension: " + name + " = '" + expandString(value, 0, value.length)
+ + "'");
+ }
+ }
+
+ /**
+ * Returns the negotiated SFTP protocol version between the client and the server.
+ *
+ * @return SFTP protocol version, i.e., "3".
+ *
+ */
+ public int getProtocolVersion()
+ {
+ return protocol_version;
+ }
+
+ /**
+ * Close this SFTP session. NEVER forget to call this method to free up
+ * resources - even if you got an exception from one of the other methods.
+ * Sometimes these other methods may throw an exception, saying that the
+ * underlying channel is closed (this can happen, e.g., if the other server
+ * sent a close message.) However, as long as you have not called the
+ * <code>close()</code> method, you are likely wasting resources.
+ *
+ */
+ public void close()
+ {
+ sess.close();
+ }
+
+ /**
+ * List the contents of a directory.
+ *
+ * @param dirName See the {@link SFTPv3Client comment} for the class for more details.
+ * @return A Vector containing {@link SFTPv3DirectoryEntry} objects.
+ * @throws IOException
+ */
+ public Vector ls(String dirName) throws IOException
+ {
+ byte[] handle = openDirectory(dirName);
+ Vector result = scanDirectory(handle);
+ closeHandle(handle);
+ return result;
+ }
+
+ /**
+ * Create a new directory.
+ *
+ * @param dirName See the {@link SFTPv3Client comment} for the class for more details.
+ * @param posixPermissions the permissions for this directory, e.g., "0700" (remember that
+ * this is octal noation). The server will likely apply a umask.
+ *
+ * @throws IOException
+ */
+ public void mkdir(String dirName, int posixPermissions) throws IOException
+ {
+ int req_id = generateNextRequestID();
+
+ TypesWriter tw = new TypesWriter();
+ tw.writeString(dirName, charsetName);
+ tw.writeUINT32(AttribFlags.SSH_FILEXFER_ATTR_PERMISSIONS);
+ tw.writeUINT32(posixPermissions);
+
+ sendMessage(Packet.SSH_FXP_MKDIR, req_id, tw.getBytes());
+
+ expectStatusOKMessage(req_id);
+ }
+
+ /**
+ * Remove a file.
+ *
+ * @param fileName See the {@link SFTPv3Client comment} for the class for more details.
+ * @throws IOException
+ */
+ public void rm(String fileName) throws IOException
+ {
+ int req_id = generateNextRequestID();
+
+ TypesWriter tw = new TypesWriter();
+ tw.writeString(fileName, charsetName);
+
+ sendMessage(Packet.SSH_FXP_REMOVE, req_id, tw.getBytes());
+
+ expectStatusOKMessage(req_id);
+ }
+
+ /**
+ * Remove an empty directory.
+ *
+ * @param dirName See the {@link SFTPv3Client comment} for the class for more details.
+ * @throws IOException
+ */
+ public void rmdir(String dirName) throws IOException
+ {
+ int req_id = generateNextRequestID();
+
+ TypesWriter tw = new TypesWriter();
+ tw.writeString(dirName, charsetName);
+
+ sendMessage(Packet.SSH_FXP_RMDIR, req_id, tw.getBytes());
+
+ expectStatusOKMessage(req_id);
+ }
+
+ /**
+ * Move a file or directory.
+ *
+ * @param oldPath See the {@link SFTPv3Client comment} for the class for more details.
+ * @param newPath See the {@link SFTPv3Client comment} for the class for more details.
+ * @throws IOException
+ */
+ public void mv(String oldPath, String newPath) throws IOException
+ {
+ int req_id = generateNextRequestID();
+
+ TypesWriter tw = new TypesWriter();
+ tw.writeString(oldPath, charsetName);
+ tw.writeString(newPath, charsetName);
+
+ sendMessage(Packet.SSH_FXP_RENAME, req_id, tw.getBytes());
+
+ expectStatusOKMessage(req_id);
+ }
+
+ /**
+ * Open a file for reading.
+ *
+ * @param fileName See the {@link SFTPv3Client comment} for the class for more details.
+ * @return a SFTPv3FileHandle handle
+ * @throws IOException
+ */
+ public SFTPv3FileHandle openFileRO(String fileName) throws IOException
+ {
+ return openFile(fileName, 0x00000001, null); // SSH_FXF_READ
+ }
+
+ /**
+ * Open a file for reading and writing.
+ *
+ * @param fileName See the {@link SFTPv3Client comment} for the class for more details.
+ * @return a SFTPv3FileHandle handle
+ * @throws IOException
+ */
+ public SFTPv3FileHandle openFileRW(String fileName) throws IOException
+ {
+ return openFile(fileName, 0x00000003, null); // SSH_FXF_READ | SSH_FXF_WRITE
+ }
+
+ // Append is broken (already in the specification, because there is no way to
+ // send a write operation (what offset to use??))
+ // public SFTPv3FileHandle openFileRWAppend(String fileName) throws IOException
+ // {
+ // return openFile(fileName, 0x00000007, null); // SSH_FXF_READ | SSH_FXF_WRITE | SSH_FXF_APPEND
+ // }
+
+ /**
+ * Create a file and open it for reading and writing.
+ * Same as {@link #createFile(String, SFTPv3FileAttributes) createFile(fileName, null)}.
+ *
+ * @param fileName See the {@link SFTPv3Client comment} for the class for more details.
+ * @return a SFTPv3FileHandle handle
+ * @throws IOException
+ */
+ public SFTPv3FileHandle createFile(String fileName) throws IOException
+ {
+ return createFile(fileName, null);
+ }
+
+ /**
+ * Create a file and open it for reading and writing.
+ * You can specify the default attributes of the file (the server may or may
+ * not respect your wishes).
+ *
+ * @param fileName See the {@link SFTPv3Client comment} for the class for more details.
+ * @param attr may be <code>null</code> to use server defaults. Probably only
+ * the <code>uid</code>, <code>gid</code> and <code>permissions</code>
+ * (remember the server may apply a umask) entries of the {@link SFTPv3FileHandle}
+ * structure make sense. You need only to set those fields where you want
+ * to override the server's defaults.
+ * @return a SFTPv3FileHandle handle
+ * @throws IOException
+ */
+ public SFTPv3FileHandle createFile(String fileName, SFTPv3FileAttributes attr) throws IOException
+ {
+ return openFile(fileName, 0x00000008 | 0x00000003, attr); // SSH_FXF_CREAT | SSH_FXF_READ | SSH_FXF_WRITE
+ }
+
+ /**
+ * Create a file (truncate it if it already exists) and open it for reading and writing.
+ * Same as {@link #createFileTruncate(String, SFTPv3FileAttributes) createFileTruncate(fileName, null)}.
+ *
+ * @param fileName See the {@link SFTPv3Client comment} for the class for more details.
+ * @return a SFTPv3FileHandle handle
+ * @throws IOException
+ */
+ public SFTPv3FileHandle createFileTruncate(String fileName) throws IOException
+ {
+ return createFileTruncate(fileName, null);
+ }
+
+ /**
+ * reate a file (truncate it if it already exists) and open it for reading and writing.
+ * You can specify the default attributes of the file (the server may or may
+ * not respect your wishes).
+ *
+ * @param fileName See the {@link SFTPv3Client comment} for the class for more details.
+ * @param attr may be <code>null</code> to use server defaults. Probably only
+ * the <code>uid</code>, <code>gid</code> and <code>permissions</code>
+ * (remember the server may apply a umask) entries of the {@link SFTPv3FileHandle}
+ * structure make sense. You need only to set those fields where you want
+ * to override the server's defaults.
+ * @return a SFTPv3FileHandle handle
+ * @throws IOException
+ */
+ public SFTPv3FileHandle createFileTruncate(String fileName, SFTPv3FileAttributes attr) throws IOException
+ {
+ return openFile(fileName, 0x00000018 | 0x00000003, attr); // SSH_FXF_CREAT | SSH_FXF_TRUNC | SSH_FXF_READ | SSH_FXF_WRITE
+ }
+
+ private byte[] createAttrs(SFTPv3FileAttributes attr)
+ {
+ TypesWriter tw = new TypesWriter();
+
+ int attrFlags = 0;
+
+ if (attr == null)
+ {
+ tw.writeUINT32(0);
+ }
+ else
+ {
+ if (attr.size != null)
+ attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_SIZE;
+
+ if ((attr.uid != null) && (attr.gid != null))
+ attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_V3_UIDGID;
+
+ if (attr.permissions != null)
+ attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_PERMISSIONS;
+
+ if ((attr.atime != null) && (attr.mtime != null))
+ attrFlags = attrFlags | AttribFlags.SSH_FILEXFER_ATTR_V3_ACMODTIME;
+
+ tw.writeUINT32(attrFlags);
+
+ if (attr.size != null)
+ tw.writeUINT64(attr.size.longValue());
+
+ if ((attr.uid != null) && (attr.gid != null))
+ {
+ tw.writeUINT32(attr.uid.intValue());
+ tw.writeUINT32(attr.gid.intValue());
+ }
+
+ if (attr.permissions != null)
+ tw.writeUINT32(attr.permissions.intValue());
+
+ if ((attr.atime != null) && (attr.mtime != null))
+ {
+ tw.writeUINT32(attr.atime.intValue());
+ tw.writeUINT32(attr.mtime.intValue());
+ }
+ }
+
+ return tw.getBytes();
+ }
+
+ private SFTPv3FileHandle openFile(String fileName, int flags, SFTPv3FileAttributes attr) throws IOException
+ {
+ int req_id = generateNextRequestID();
+
+ TypesWriter tw = new TypesWriter();
+ tw.writeString(fileName, charsetName);
+ tw.writeUINT32(flags);
+ tw.writeBytes(createAttrs(attr));
+
+ if (debug != null)
+ {
+ debug.println("Sending SSH_FXP_OPEN...");
+ debug.flush();
+ }
+
+ sendMessage(Packet.SSH_FXP_OPEN, req_id, tw.getBytes());
+
+ byte[] resp = receiveMessage(34000);
+
+ TypesReader tr = new TypesReader(resp);
+
+ int t = tr.readByte();
+
+ int rep_id = tr.readUINT32();
+ if (rep_id != req_id)
+ throw new IOException("The server sent an invalid id field.");
+
+ if (t == Packet.SSH_FXP_HANDLE)
+ {
+ if (debug != null)
+ {
+ debug.println("Got SSH_FXP_HANDLE.");
+ debug.flush();
+ }
+
+ return new SFTPv3FileHandle(this, tr.readByteString());
+ }
+
+ if (t != Packet.SSH_FXP_STATUS)
+ throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
+
+ int errorCode = tr.readUINT32();
+ String errorMessage = tr.readString();
+
+ throw new SFTPException(errorMessage, errorCode);
+ }
+
+ /**
+ * Read bytes from a file. No more than 32768 bytes may be read at once.
+ * Be aware that the semantics of read() are different than for Java streams.
+ * <p>
+ * <ul>
+ * <li>The server will read as many bytes as it can from the file (up to <code>len</code>),
+ * and return them.</li>
+ * <li>If EOF is encountered before reading any data, <code>-1</code> is returned.
+ * <li>If an error occurs, an exception is thrown</li>.
+ * <li>For normal disk files, it is guaranteed that the server will return the specified
+ * number of bytes, or up to end of file. For, e.g., device files this may return
+ * fewer bytes than requested.</li>
+ * </ul>
+ *
+ * @param handle a SFTPv3FileHandle handle
+ * @param fileOffset offset (in bytes) in the file
+ * @param dst the destination byte array
+ * @param dstoff offset in the destination byte array
+ * @param len how many bytes to read, 0 &lt; len &lt;= 32768 bytes
+ * @return the number of bytes that could be read, may be less than requested if
+ * the end of the file is reached, -1 is returned in case of <code>EOF</code>
+ * @throws IOException
+ */
+ public int read(SFTPv3FileHandle handle, long fileOffset, byte[] dst, int dstoff, int len) throws IOException
+ {
+ checkHandleValidAndOpen(handle);
+
+ if ((len > 32768) || (len <= 0))
+ throw new IllegalArgumentException("invalid len argument");
+
+ int req_id = generateNextRequestID();
+
+ TypesWriter tw = new TypesWriter();
+ tw.writeString(handle.fileHandle, 0, handle.fileHandle.length);
+ tw.writeUINT64(fileOffset);
+ tw.writeUINT32(len);
+
+ if (debug != null)
+ {
+ debug.println("Sending SSH_FXP_READ...");
+ debug.flush();
+ }
+
+ sendMessage(Packet.SSH_FXP_READ, req_id, tw.getBytes());
+
+ byte[] resp = receiveMessage(34000);
+
+ TypesReader tr = new TypesReader(resp);
+
+ int t = tr.readByte();
+
+ int rep_id = tr.readUINT32();
+ if (rep_id != req_id)
+ throw new IOException("The server sent an invalid id field.");
+
+ if (t == Packet.SSH_FXP_DATA)
+ {
+ if (debug != null)
+ {
+ debug.println("Got SSH_FXP_DATA...");
+ debug.flush();
+ }
+
+ int readLen = tr.readUINT32();
+
+ if ((readLen < 0) || (readLen > len))
+ throw new IOException("The server sent an invalid length field.");
+
+ tr.readBytes(dst, dstoff, readLen);
+
+ return readLen;
+ }
+
+ if (t != Packet.SSH_FXP_STATUS)
+ throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
+
+ int errorCode = tr.readUINT32();
+
+ if (errorCode == ErrorCodes.SSH_FX_EOF)
+ {
+ if (debug != null)
+ {
+ debug.println("Got SSH_FX_EOF.");
+ debug.flush();
+ }
+
+ return -1;
+ }
+
+ String errorMessage = tr.readString();
+
+ throw new SFTPException(errorMessage, errorCode);
+ }
+
+ /**
+ * Write bytes to a file. If <code>len</code> &gt; 32768, then the write operation will
+ * be split into multiple writes.
+ *
+ * @param handle a SFTPv3FileHandle handle.
+ * @param fileOffset offset (in bytes) in the file.
+ * @param src the source byte array.
+ * @param srcoff offset in the source byte array.
+ * @param len how many bytes to write.
+ * @throws IOException
+ */
+ public void write(SFTPv3FileHandle handle, long fileOffset, byte[] src, int srcoff, int len) throws IOException
+ {
+ checkHandleValidAndOpen(handle);
+
+ while (len > 0)
+ {
+ int writeRequestLen = len;
+
+ if (writeRequestLen > 32768)
+ writeRequestLen = 32768;
+
+ int req_id = generateNextRequestID();
+
+ TypesWriter tw = new TypesWriter();
+ tw.writeString(handle.fileHandle, 0, handle.fileHandle.length);
+ tw.writeUINT64(fileOffset);
+ tw.writeString(src, srcoff, writeRequestLen);
+
+ if (debug != null)
+ {
+ debug.println("Sending SSH_FXP_WRITE...");
+ debug.flush();
+ }
+
+ sendMessage(Packet.SSH_FXP_WRITE, req_id, tw.getBytes());
+
+ fileOffset += writeRequestLen;
+
+ srcoff += writeRequestLen;
+ len -= writeRequestLen;
+
+ byte[] resp = receiveMessage(34000);
+
+ TypesReader tr = new TypesReader(resp);
+
+ int t = tr.readByte();
+
+ int rep_id = tr.readUINT32();
+ if (rep_id != req_id)
+ throw new IOException("The server sent an invalid id field.");
+
+ if (t != Packet.SSH_FXP_STATUS)
+ throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
+
+ int errorCode = tr.readUINT32();
+
+ if (errorCode == ErrorCodes.SSH_FX_OK)
+ continue;
+
+ String errorMessage = tr.readString();
+
+ throw new SFTPException(errorMessage, errorCode);
+ }
+ }
+
+ /**
+ * Close a file.
+ *
+ * @param handle a SFTPv3FileHandle handle
+ * @throws IOException
+ */
+ public void closeFile(SFTPv3FileHandle handle) throws IOException
+ {
+ if (handle == null)
+ throw new IllegalArgumentException("the handle argument may not be null");
+
+ try
+ {
+ if (handle.isClosed == false)
+ {
+ closeHandle(handle.fileHandle);
+ }
+ }
+ finally
+ {
+ handle.isClosed = true;
+ }
+ }
+}
diff --git a/src/com/trilead/ssh2/SFTPv3DirectoryEntry.java b/src/com/trilead/ssh2/SFTPv3DirectoryEntry.java
index 370aefa..669ba87 100644
--- a/src/com/trilead/ssh2/SFTPv3DirectoryEntry.java
+++ b/src/com/trilead/ssh2/SFTPv3DirectoryEntry.java
@@ -1,38 +1,38 @@
-
-package com.trilead.ssh2;
-
-/**
- * A <code>SFTPv3DirectoryEntry</code> as returned by {@link SFTPv3Client#ls(String)}.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: SFTPv3DirectoryEntry.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-
-public class SFTPv3DirectoryEntry
-{
- /**
- * A relative name within the directory, without any path components.
- */
- public String filename;
-
- /**
- * An expanded format for the file name, similar to what is returned by
- * "ls -l" on Un*x systems.
- * <p>
- * The format of this field is unspecified by the SFTP v3 protocol.
- * It MUST be suitable for use in the output of a directory listing
- * command (in fact, the recommended operation for a directory listing
- * command is to simply display this data). However, clients SHOULD NOT
- * attempt to parse the longname field for file attributes; they SHOULD
- * use the attrs field instead.
- * <p>
- * The recommended format for the longname field is as follows:<br>
- * <code>-rwxr-xr-x 1 mjos staff 348911 Mar 25 14:29 t-filexfer</code>
- */
- public String longEntry;
-
- /**
- * The attributes of this entry.
- */
- public SFTPv3FileAttributes attributes;
-}
+
+package com.trilead.ssh2;
+
+/**
+ * A <code>SFTPv3DirectoryEntry</code> as returned by {@link SFTPv3Client#ls(String)}.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: SFTPv3DirectoryEntry.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+
+public class SFTPv3DirectoryEntry
+{
+ /**
+ * A relative name within the directory, without any path components.
+ */
+ public String filename;
+
+ /**
+ * An expanded format for the file name, similar to what is returned by
+ * "ls -l" on Un*x systems.
+ * <p>
+ * The format of this field is unspecified by the SFTP v3 protocol.
+ * It MUST be suitable for use in the output of a directory listing
+ * command (in fact, the recommended operation for a directory listing
+ * command is to simply display this data). However, clients SHOULD NOT
+ * attempt to parse the longname field for file attributes; they SHOULD
+ * use the attrs field instead.
+ * <p>
+ * The recommended format for the longname field is as follows:<br>
+ * <code>-rwxr-xr-x 1 mjos staff 348911 Mar 25 14:29 t-filexfer</code>
+ */
+ public String longEntry;
+
+ /**
+ * The attributes of this entry.
+ */
+ public SFTPv3FileAttributes attributes;
+}
diff --git a/src/com/trilead/ssh2/SFTPv3FileAttributes.java b/src/com/trilead/ssh2/SFTPv3FileAttributes.java
index 56c9c87..7b1d321 100644
--- a/src/com/trilead/ssh2/SFTPv3FileAttributes.java
+++ b/src/com/trilead/ssh2/SFTPv3FileAttributes.java
@@ -1,145 +1,145 @@
-
-package com.trilead.ssh2;
-
-/**
- * A <code>SFTPv3FileAttributes</code> object represents detail information
- * about a file on the server. Not all fields may/must be present.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: SFTPv3FileAttributes.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
- */
-
-public class SFTPv3FileAttributes
-{
- /**
- * The SIZE attribute. <code>NULL</code> if not present.
- */
- public Long size = null;
-
- /**
- * The UID attribute. <code>NULL</code> if not present.
- */
- public Integer uid = null;
-
- /**
- * The GID attribute. <code>NULL</code> if not present.
- */
- public Integer gid = null;
-
- /**
- * The POSIX permissions. <code>NULL</code> if not present.
- * <p>
- * Here is a list:
- * <p>
- * <pre>Note: these numbers are all OCTAL.
- *
- * S_IFMT 0170000 bitmask for the file type bitfields
- * S_IFSOCK 0140000 socket
- * S_IFLNK 0120000 symbolic link
- * S_IFREG 0100000 regular file
- * S_IFBLK 0060000 block device
- * S_IFDIR 0040000 directory
- * S_IFCHR 0020000 character device
- * S_IFIFO 0010000 fifo
- * S_ISUID 0004000 set UID bit
- * S_ISGID 0002000 set GID bit
- * S_ISVTX 0001000 sticky bit
- *
- * S_IRWXU 00700 mask for file owner permissions
- * S_IRUSR 00400 owner has read permission
- * S_IWUSR 00200 owner has write permission
- * S_IXUSR 00100 owner has execute permission
- * S_IRWXG 00070 mask for group permissions
- * S_IRGRP 00040 group has read permission
- * S_IWGRP 00020 group has write permission
- * S_IXGRP 00010 group has execute permission
- * S_IRWXO 00007 mask for permissions for others (not in group)
- * S_IROTH 00004 others have read permission
- * S_IWOTH 00002 others have write permisson
- * S_IXOTH 00001 others have execute permission
- * </pre>
- */
- public Integer permissions = null;
-
- /**
- * The ATIME attribute. Represented as seconds from Jan 1, 1970 in UTC.
- * <code>NULL</code> if not present.
- */
- public Long atime = null;
-
- /**
- * The MTIME attribute. Represented as seconds from Jan 1, 1970 in UTC.
- * <code>NULL</code> if not present.
- */
- public Long mtime = null;
-
- /**
- * Checks if this entry is a directory.
- *
- * @return Returns true if permissions are available and they indicate
- * that this entry represents a directory.
- */
- public boolean isDirectory()
- {
- if (permissions == null)
- return false;
-
- return ((permissions.intValue() & 0040000) != 0);
- }
-
- /**
- * Checks if this entry is a regular file.
- *
- * @return Returns true if permissions are available and they indicate
- * that this entry represents a regular file.
- */
- public boolean isRegularFile()
- {
- if (permissions == null)
- return false;
-
- return ((permissions.intValue() & 0100000) != 0);
- }
-
- /**
- * Checks if this entry is a a symlink.
- *
- * @return Returns true if permissions are available and they indicate
- * that this entry represents a symlink.
- */
- public boolean isSymlink()
- {
- if (permissions == null)
- return false;
-
- return ((permissions.intValue() & 0120000) != 0);
- }
-
- /**
- * Turn the POSIX permissions into a 7 digit octal representation.
- * Note: the returned value is first masked with <code>0177777</code>.
- *
- * @return <code>NULL</code> if permissions are not available.
- */
- public String getOctalPermissions()
- {
- if (permissions == null)
- return null;
-
- String res = Integer.toString(permissions.intValue() & 0177777, 8);
-
- StringBuffer sb = new StringBuffer();
-
- int leadingZeros = 7 - res.length();
-
- while (leadingZeros > 0)
- {
- sb.append('0');
- leadingZeros--;
- }
-
- sb.append(res);
-
- return sb.toString();
- }
-}
+
+package com.trilead.ssh2;
+
+/**
+ * A <code>SFTPv3FileAttributes</code> object represents detail information
+ * about a file on the server. Not all fields may/must be present.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: SFTPv3FileAttributes.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
+ */
+
+public class SFTPv3FileAttributes
+{
+ /**
+ * The SIZE attribute. <code>NULL</code> if not present.
+ */
+ public Long size = null;
+
+ /**
+ * The UID attribute. <code>NULL</code> if not present.
+ */
+ public Integer uid = null;
+
+ /**
+ * The GID attribute. <code>NULL</code> if not present.
+ */
+ public Integer gid = null;
+
+ /**
+ * The POSIX permissions. <code>NULL</code> if not present.
+ * <p>
+ * Here is a list:
+ * <p>
+ * <pre>Note: these numbers are all OCTAL.
+ *
+ * S_IFMT 0170000 bitmask for the file type bitfields
+ * S_IFSOCK 0140000 socket
+ * S_IFLNK 0120000 symbolic link
+ * S_IFREG 0100000 regular file
+ * S_IFBLK 0060000 block device
+ * S_IFDIR 0040000 directory
+ * S_IFCHR 0020000 character device
+ * S_IFIFO 0010000 fifo
+ * S_ISUID 0004000 set UID bit
+ * S_ISGID 0002000 set GID bit
+ * S_ISVTX 0001000 sticky bit
+ *
+ * S_IRWXU 00700 mask for file owner permissions
+ * S_IRUSR 00400 owner has read permission
+ * S_IWUSR 00200 owner has write permission
+ * S_IXUSR 00100 owner has execute permission
+ * S_IRWXG 00070 mask for group permissions
+ * S_IRGRP 00040 group has read permission
+ * S_IWGRP 00020 group has write permission
+ * S_IXGRP 00010 group has execute permission
+ * S_IRWXO 00007 mask for permissions for others (not in group)
+ * S_IROTH 00004 others have read permission
+ * S_IWOTH 00002 others have write permisson
+ * S_IXOTH 00001 others have execute permission
+ * </pre>
+ */
+ public Integer permissions = null;
+
+ /**
+ * The ATIME attribute. Represented as seconds from Jan 1, 1970 in UTC.
+ * <code>NULL</code> if not present.
+ */
+ public Long atime = null;
+
+ /**
+ * The MTIME attribute. Represented as seconds from Jan 1, 1970 in UTC.
+ * <code>NULL</code> if not present.
+ */
+ public Long mtime = null;
+
+ /**
+ * Checks if this entry is a directory.
+ *
+ * @return Returns true if permissions are available and they indicate
+ * that this entry represents a directory.
+ */
+ public boolean isDirectory()
+ {
+ if (permissions == null)
+ return false;
+
+ return ((permissions.intValue() & 0040000) != 0);
+ }
+
+ /**
+ * Checks if this entry is a regular file.
+ *
+ * @return Returns true if permissions are available and they indicate
+ * that this entry represents a regular file.
+ */
+ public boolean isRegularFile()
+ {
+ if (permissions == null)
+ return false;
+
+ return ((permissions.intValue() & 0100000) != 0);
+ }
+
+ /**
+ * Checks if this entry is a a symlink.
+ *
+ * @return Returns true if permissions are available and they indicate
+ * that this entry represents a symlink.
+ */
+ public boolean isSymlink()
+ {
+ if (permissions == null)
+ return false;
+
+ return ((permissions.intValue() & 0120000) != 0);
+ }
+
+ /**
+ * Turn the POSIX permissions into a 7 digit octal representation.
+ * Note: the returned value is first masked with <code>0177777</code>.
+ *
+ * @return <code>NULL</code> if permissions are not available.
+ */
+ public String getOctalPermissions()
+ {
+ if (permissions == null)
+ return null;
+
+ String res = Integer.toString(permissions.intValue() & 0177777, 8);
+
+ StringBuffer sb = new StringBuffer();
+
+ int leadingZeros = 7 - res.length();
+
+ while (leadingZeros > 0)
+ {
+ sb.append('0');
+ leadingZeros--;
+ }
+
+ sb.append(res);
+
+ return sb.toString();
+ }
+}
diff --git a/src/com/trilead/ssh2/SFTPv3FileHandle.java b/src/com/trilead/ssh2/SFTPv3FileHandle.java
index c8b5477..9b3dbb6 100644
--- a/src/com/trilead/ssh2/SFTPv3FileHandle.java
+++ b/src/com/trilead/ssh2/SFTPv3FileHandle.java
@@ -1,45 +1,45 @@
-
-package com.trilead.ssh2;
-
-/**
- * A <code>SFTPv3FileHandle</code>.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: SFTPv3FileHandle.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-
-public class SFTPv3FileHandle
-{
- final SFTPv3Client client;
- final byte[] fileHandle;
- boolean isClosed = false;
-
- /* The constructor is NOT public */
-
- SFTPv3FileHandle(SFTPv3Client client, byte[] h)
- {
- this.client = client;
- this.fileHandle = h;
- }
-
- /**
- * Get the SFTPv3Client instance which created this handle.
- *
- * @return A SFTPv3Client instance.
- */
- public SFTPv3Client getClient()
- {
- return client;
- }
-
- /**
- * Check if this handle was closed with the {@link SFTPv3Client#closeFile(SFTPv3FileHandle)} method
- * of the <code>SFTPv3Client</code> instance which created the handle.
- *
- * @return if the handle is closed.
- */
- public boolean isClosed()
- {
- return isClosed;
- }
-}
+
+package com.trilead.ssh2;
+
+/**
+ * A <code>SFTPv3FileHandle</code>.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: SFTPv3FileHandle.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+
+public class SFTPv3FileHandle
+{
+ final SFTPv3Client client;
+ final byte[] fileHandle;
+ boolean isClosed = false;
+
+ /* The constructor is NOT public */
+
+ SFTPv3FileHandle(SFTPv3Client client, byte[] h)
+ {
+ this.client = client;
+ this.fileHandle = h;
+ }
+
+ /**
+ * Get the SFTPv3Client instance which created this handle.
+ *
+ * @return A SFTPv3Client instance.
+ */
+ public SFTPv3Client getClient()
+ {
+ return client;
+ }
+
+ /**
+ * Check if this handle was closed with the {@link SFTPv3Client#closeFile(SFTPv3FileHandle)} method
+ * of the <code>SFTPv3Client</code> instance which created the handle.
+ *
+ * @return if the handle is closed.
+ */
+ public boolean isClosed()
+ {
+ return isClosed;
+ }
+}
diff --git a/src/com/trilead/ssh2/ServerHostKeyVerifier.java b/src/com/trilead/ssh2/ServerHostKeyVerifier.java
index 1c33454..ac65955 100644
--- a/src/com/trilead/ssh2/ServerHostKeyVerifier.java
+++ b/src/com/trilead/ssh2/ServerHostKeyVerifier.java
@@ -1,31 +1,31 @@
-
-package com.trilead.ssh2;
-
-/**
- * A callback interface used to implement a client specific method of checking
- * server host keys.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: ServerHostKeyVerifier.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-
-public interface ServerHostKeyVerifier
-{
- /**
- * The actual verifier method, it will be called by the key exchange code
- * on EVERY key exchange - this can happen several times during the lifetime
- * of a connection.
- * <p>
- * Note: SSH-2 servers are allowed to change their hostkey at ANY time.
- *
- * @param hostname the hostname used to create the {@link Connection} object
- * @param port the remote TCP port
- * @param serverHostKeyAlgorithm the public key algorithm (<code>ssh-rsa</code> or <code>ssh-dss</code>)
- * @param serverHostKey the server's public key blob
- * @return if the client wants to accept the server's host key - if not, the
- * connection will be closed.
- * @throws Exception Will be wrapped with an IOException, extended version of returning false =)
- */
- public boolean verifyServerHostKey(String hostname, int port, String serverHostKeyAlgorithm, byte[] serverHostKey)
- throws Exception;
-}
+
+package com.trilead.ssh2;
+
+/**
+ * A callback interface used to implement a client specific method of checking
+ * server host keys.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: ServerHostKeyVerifier.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+
+public interface ServerHostKeyVerifier
+{
+ /**
+ * The actual verifier method, it will be called by the key exchange code
+ * on EVERY key exchange - this can happen several times during the lifetime
+ * of a connection.
+ * <p>
+ * Note: SSH-2 servers are allowed to change their hostkey at ANY time.
+ *
+ * @param hostname the hostname used to create the {@link Connection} object
+ * @param port the remote TCP port
+ * @param serverHostKeyAlgorithm the public key algorithm (<code>ssh-rsa</code> or <code>ssh-dss</code>)
+ * @param serverHostKey the server's public key blob
+ * @return if the client wants to accept the server's host key - if not, the
+ * connection will be closed.
+ * @throws Exception Will be wrapped with an IOException, extended version of returning false =)
+ */
+ public boolean verifyServerHostKey(String hostname, int port, String serverHostKeyAlgorithm, byte[] serverHostKey)
+ throws Exception;
+}
diff --git a/src/com/trilead/ssh2/Session.java b/src/com/trilead/ssh2/Session.java
index fa0c36f..bf3c7e0 100644
--- a/src/com/trilead/ssh2/Session.java
+++ b/src/com/trilead/ssh2/Session.java
@@ -1,530 +1,530 @@
-
-package com.trilead.ssh2;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.security.SecureRandom;
-
-import com.trilead.ssh2.channel.Channel;
-import com.trilead.ssh2.channel.ChannelManager;
-import com.trilead.ssh2.channel.X11ServerData;
-
-
-/**
- * A <code>Session</code> is a remote execution of a program. "Program" means
- * in this context either a shell, an application or a system command. The
- * program may or may not have a tty. Only one single program can be started on
- * a session. However, multiple sessions can be active simultaneously.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: Session.java,v 1.2 2008/03/03 07:01:36 cplattne Exp $
- */
-public class Session
-{
- ChannelManager cm;
- Channel cn;
-
- boolean flag_pty_requested = false;
- boolean flag_x11_requested = false;
- boolean flag_execution_started = false;
- boolean flag_closed = false;
-
- String x11FakeCookie = null;
-
- final SecureRandom rnd;
-
- Session(ChannelManager cm, SecureRandom rnd) throws IOException
- {
- this.cm = cm;
- this.cn = cm.openSessionChannel();
- this.rnd = rnd;
- }
-
- /**
- * Basically just a wrapper for lazy people - identical to calling
- * <code>requestPTY("dumb", 0, 0, 0, 0, null)</code>.
- *
- * @throws IOException
- */
- public void requestDumbPTY() throws IOException
- {
- requestPTY("dumb", 0, 0, 0, 0, null);
- }
-
- /**
- * Basically just another wrapper for lazy people - identical to calling
- * <code>requestPTY(term, 0, 0, 0, 0, null)</code>.
- *
- * @throws IOException
- */
- public void requestPTY(String term) throws IOException
- {
- requestPTY(term, 0, 0, 0, 0, null);
- }
-
- /**
- * Allocate a pseudo-terminal for this session.
- * <p>
- * This method may only be called before a program or shell is started in
- * this session.
- * <p>
- * Different aspects can be specified:
- * <p>
- * <ul>
- * <li>The TERM environment variable value (e.g., vt100)</li>
- * <li>The terminal's dimensions.</li>
- * <li>The encoded terminal modes.</li>
- * </ul>
- * Zero dimension parameters are ignored. The character/row dimensions
- * override the pixel dimensions (when nonzero). Pixel dimensions refer to
- * the drawable area of the window. The dimension parameters are only
- * informational. The encoding of terminal modes (parameter
- * <code>terminal_modes</code>) is described in RFC4254.
- *
- * @param term
- * The TERM environment variable value (e.g., vt100)
- * @param term_width_characters
- * terminal width, characters (e.g., 80)
- * @param term_height_characters
- * terminal height, rows (e.g., 24)
- * @param term_width_pixels
- * terminal width, pixels (e.g., 640)
- * @param term_height_pixels
- * terminal height, pixels (e.g., 480)
- * @param terminal_modes
- * encoded terminal modes (may be <code>null</code>)
- * @throws IOException
- */
- public void requestPTY(String term, int term_width_characters, int term_height_characters, int term_width_pixels,
- int term_height_pixels, byte[] terminal_modes) throws IOException
- {
- if (term == null)
- throw new IllegalArgumentException("TERM cannot be null.");
-
- if ((terminal_modes != null) && (terminal_modes.length > 0))
- {
- if (terminal_modes[terminal_modes.length - 1] != 0)
- throw new IOException("Illegal terminal modes description, does not end in zero byte");
- }
- else
- terminal_modes = new byte[] { 0 };
-
- synchronized (this)
- {
- /* The following is just a nicer error, we would catch it anyway later in the channel code */
- if (flag_closed)
- throw new IOException("This session is closed.");
-
- if (flag_pty_requested)
- throw new IOException("A PTY was already requested.");
-
- if (flag_execution_started)
- throw new IOException(
- "Cannot request PTY at this stage anymore, a remote execution has already started.");
-
- flag_pty_requested = true;
- }
-
- cm.requestPTY(cn, term, term_width_characters, term_height_characters, term_width_pixels, term_height_pixels,
- terminal_modes);
- }
-
- /**
- * Inform other side of connection that our PTY has resized.
- * <p>
- * Zero dimension parameters are ignored. The character/row dimensions
- * override the pixel dimensions (when nonzero). Pixel dimensions refer to
- * the drawable area of the window. The dimension parameters are only
- * informational.
- *
- * @param term_width_characters
- * terminal width, characters (e.g., 80)
- * @param term_height_characters
- * terminal height, rows (e.g., 24)
- * @param term_width_pixels
- * terminal width, pixels (e.g., 640)
- * @param term_height_pixels
- * terminal height, pixels (e.g., 480)
- * @throws IOException
- */
- public void resizePTY(int term_width_characters, int term_height_characters, int term_width_pixels,
- int term_height_pixels) throws IOException {
- synchronized (this)
- {
- /* The following is just a nicer error, we would catch it anyway later in the channel code */
- if (flag_closed)
- throw new IOException("This session is closed.");
- }
-
- cm.resizePTY(cn, term_width_characters, term_height_characters, term_width_pixels, term_height_pixels);
- }
-
- /**
- * Request X11 forwarding for the current session.
- * <p>
- * You have to supply the name and port of your X-server.
- * <p>
- * This method may only be called before a program or shell is started in
- * this session.
- *
- * @param hostname the hostname of the real (target) X11 server (e.g., 127.0.0.1)
- * @param port the port of the real (target) X11 server (e.g., 6010)
- * @param cookie if non-null, then present this cookie to the real X11 server
- * @param singleConnection if true, then the server is instructed to only forward one single
- * connection, no more connections shall be forwarded after first, or after the session
- * channel has been closed
- * @throws IOException
- */
- public void requestX11Forwarding(String hostname, int port, byte[] cookie, boolean singleConnection)
- throws IOException
- {
- if (hostname == null)
- throw new IllegalArgumentException("hostname argument may not be null");
-
- synchronized (this)
- {
- /* The following is just a nicer error, we would catch it anyway later in the channel code */
- if (flag_closed)
- throw new IOException("This session is closed.");
-
- if (flag_x11_requested)
- throw new IOException("X11 forwarding was already requested.");
-
- if (flag_execution_started)
- throw new IOException(
- "Cannot request X11 forwarding at this stage anymore, a remote execution has already started.");
-
- flag_x11_requested = true;
- }
-
- /* X11ServerData - used to store data about the target X11 server */
-
- X11ServerData x11data = new X11ServerData();
-
- x11data.hostname = hostname;
- x11data.port = port;
- x11data.x11_magic_cookie = cookie; /* if non-null, then present this cookie to the real X11 server */
-
- /* Generate fake cookie - this one is used between remote clients and our proxy */
-
- byte[] fakeCookie = new byte[16];
- String hexEncodedFakeCookie;
-
- /* Make sure that this fake cookie is unique for this connection */
-
- while (true)
- {
- rnd.nextBytes(fakeCookie);
-
- /* Generate also hex representation of fake cookie */
-
- StringBuffer tmp = new StringBuffer(32);
- for (int i = 0; i < fakeCookie.length; i++)
- {
- String digit2 = Integer.toHexString(fakeCookie[i] & 0xff);
- tmp.append((digit2.length() == 2) ? digit2 : "0" + digit2);
- }
- hexEncodedFakeCookie = tmp.toString();
-
- /* Well, yes, chances are low, but we want to be on the safe side */
-
- if (cm.checkX11Cookie(hexEncodedFakeCookie) == null)
- break;
- }
-
- /* Ask for X11 forwarding */
-
- cm.requestX11(cn, singleConnection, "MIT-MAGIC-COOKIE-1", hexEncodedFakeCookie, 0);
-
- /* OK, that went fine, get ready to accept X11 connections... */
- /* ... but only if the user has not called close() in the meantime =) */
-
- synchronized (this)
- {
- if (flag_closed == false)
- {
- this.x11FakeCookie = hexEncodedFakeCookie;
- cm.registerX11Cookie(hexEncodedFakeCookie, x11data);
- }
- }
-
- /* Now it is safe to start remote X11 programs */
- }
-
- /**
- * Execute a command on the remote machine.
- *
- * @param cmd
- * The command to execute on the remote host.
- * @throws IOException
- */
- public void execCommand(String cmd) throws IOException
- {
- if (cmd == null)
- throw new IllegalArgumentException("cmd argument may not be null");
-
- synchronized (this)
- {
- /* The following is just a nicer error, we would catch it anyway later in the channel code */
- if (flag_closed)
- throw new IOException("This session is closed.");
-
- if (flag_execution_started)
- throw new IOException("A remote execution has already started.");
-
- flag_execution_started = true;
- }
-
- cm.requestExecCommand(cn, cmd);
- }
-
- /**
- * Start a shell on the remote machine.
- *
- * @throws IOException
- */
- public void startShell() throws IOException
- {
- synchronized (this)
- {
- /* The following is just a nicer error, we would catch it anyway later in the channel code */
- if (flag_closed)
- throw new IOException("This session is closed.");
-
- if (flag_execution_started)
- throw new IOException("A remote execution has already started.");
-
- flag_execution_started = true;
- }
-
- cm.requestShell(cn);
- }
-
- /**
- * Start a subsystem on the remote machine.
- * Unless you know what you are doing, you will never need this.
- *
- * @param name the name of the subsystem.
- * @throws IOException
- */
- public void startSubSystem(String name) throws IOException
- {
- if (name == null)
- throw new IllegalArgumentException("name argument may not be null");
-
- synchronized (this)
- {
- /* The following is just a nicer error, we would catch it anyway later in the channel code */
- if (flag_closed)
- throw new IOException("This session is closed.");
-
- if (flag_execution_started)
- throw new IOException("A remote execution has already started.");
-
- flag_execution_started = true;
- }
-
- cm.requestSubSystem(cn, name);
- }
-
- /**
- * This method can be used to perform end-to-end session (i.e., SSH channel)
- * testing. It sends a 'ping' message to the server and waits for the 'pong'
- * from the server.
- * <p>
- * Implementation details: this method sends a SSH_MSG_CHANNEL_REQUEST request
- * ('trilead-ping') to the server and waits for the SSH_MSG_CHANNEL_FAILURE reply
- * packet.
- *
- * @throws IOException in case of any problem or when the session is closed
- */
- public void ping() throws IOException
- {
- synchronized (this)
- {
- /*
- * The following is just a nicer error, we would catch it anyway
- * later in the channel code
- */
- if (flag_closed)
- throw new IOException("This session is closed.");
- }
-
- cm.requestChannelTrileadPing(cn);
- }
-
- /**
- * Request authentication agent forwarding.
- * @param agent object that implements the callbacks
- *
- * @throws IOException in case of any problem or when the session is closed
- */
- public synchronized boolean requestAuthAgentForwarding(AuthAgentCallback agent) throws IOException
- {
- synchronized (this)
- {
- /*
- * The following is just a nicer error, we would catch it anyway
- * later in the channel code
- */
- if (flag_closed)
- throw new IOException("This session is closed.");
- }
-
- return cm.requestChannelAgentForwarding(cn, agent);
- }
-
- public InputStream getStdout()
- {
- return cn.getStdoutStream();
- }
-
- public InputStream getStderr()
- {
- return cn.getStderrStream();
- }
-
- public OutputStream getStdin()
- {
- return cn.getStdinStream();
- }
-
- /**
- * This method blocks until there is more data available on either the
- * stdout or stderr InputStream of this <code>Session</code>. Very useful
- * if you do not want to use two parallel threads for reading from the two
- * InputStreams. One can also specify a timeout. NOTE: do NOT call this
- * method if you use concurrent threads that operate on either of the two
- * InputStreams of this <code>Session</code> (otherwise this method may
- * block, even though more data is available).
- *
- * @param timeout
- * The (non-negative) timeout in <code>ms</code>. <code>0</code> means no
- * timeout, the call may block forever.
- * @return
- * <ul>
- * <li><code>0</code> if no more data will arrive.</li>
- * <li><code>1</code> if more data is available.</li>
- * <li><code>-1</code> if a timeout occurred.</li>
- * </ul>
- *
- * @throws IOException
- * @deprecated This method has been replaced with a much more powerful wait-for-condition
- * interface and therefore acts only as a wrapper.
- *
- */
- public int waitUntilDataAvailable(long timeout) throws IOException
- {
- if (timeout < 0)
- throw new IllegalArgumentException("timeout must not be negative!");
-
- int conditions = cm.waitForCondition(cn, timeout, ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA
- | ChannelCondition.EOF);
-
- if ((conditions & ChannelCondition.TIMEOUT) != 0)
- return -1;
-
- if ((conditions & (ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA)) != 0)
- return 1;
-
- /* Here we do not need to check separately for CLOSED, since CLOSED implies EOF */
-
- if ((conditions & ChannelCondition.EOF) != 0)
- return 0;
-
- throw new IllegalStateException("Unexpected condition result (" + conditions + ")");
- }
-
- /**
- * This method blocks until certain conditions hold true on the underlying SSH-2 channel.
- * <p>
- * This method returns as soon as one of the following happens:
- * <ul>
- * <li>at least of the specified conditions (see {@link ChannelCondition}) holds true</li>
- * <li>timeout > 0 and a timeout occured (TIMEOUT will be set in result conditions)</a>
- * <li>the underlying channel was closed (CLOSED will be set in result conditions)</a>
- * </ul>
- * <p>
- * In any case, the result value contains ALL current conditions, which may be more
- * than the specified condition set (i.e., never use the "==" operator to test for conditions
- * in the bitmask, see also comments in {@link ChannelCondition}).
- * <p>
- * Note: do NOT call this method if you want to wait for STDOUT_DATA or STDERR_DATA and
- * there are concurrent threads (e.g., StreamGobblers) that operate on either of the two
- * InputStreams of this <code>Session</code> (otherwise this method may
- * block, even though more data is available in the StreamGobblers).
- *
- * @param condition_set a bitmask based on {@link ChannelCondition} values
- * @param timeout non-negative timeout in ms, <code>0</code> means no timeout
- * @return all bitmask specifying all current conditions that are true
- */
-
- public int waitForCondition(int condition_set, long timeout)
- {
- if (timeout < 0)
- throw new IllegalArgumentException("timeout must be non-negative!");
-
- return cm.waitForCondition(cn, timeout, condition_set);
- }
-
- /**
- * Get the exit code/status from the remote command - if available. Be
- * careful - not all server implementations return this value. It is
- * generally a good idea to call this method only when all data from the
- * remote side has been consumed (see also the <code<WaitForCondition</code> method).
- *
- * @return An <code>Integer</code> holding the exit code, or
- * <code>null</code> if no exit code is (yet) available.
- */
- public Integer getExitStatus()
- {
- return cn.getExitStatus();
- }
-
- /**
- * Get the name of the signal by which the process on the remote side was
- * stopped - if available and applicable. Be careful - not all server
- * implementations return this value.
- *
- * @return An <code>String</code> holding the name of the signal, or
- * <code>null</code> if the process exited normally or is still
- * running (or if the server forgot to send this information).
- */
- public String getExitSignal()
- {
- return cn.getExitSignal();
- }
-
- /**
- * Close this session. NEVER forget to call this method to free up resources -
- * even if you got an exception from one of the other methods (or when
- * getting an Exception on the Input- or OutputStreams). Sometimes these other
- * methods may throw an exception, saying that the underlying channel is
- * closed (this can happen, e.g., if the other server sent a close message.)
- * However, as long as you have not called the <code>close()</code>
- * method, you may be wasting (local) resources.
- *
- */
- public void close()
- {
- synchronized (this)
- {
- if (flag_closed)
- return;
-
- flag_closed = true;
-
- if (x11FakeCookie != null)
- cm.unRegisterX11Cookie(x11FakeCookie, true);
-
- try
- {
- cm.closeChannel(cn, "Closed due to user request", true);
- }
- catch (IOException ignored)
- {
- }
- }
- }
-}
+
+package com.trilead.ssh2;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.SecureRandom;
+
+import com.trilead.ssh2.channel.Channel;
+import com.trilead.ssh2.channel.ChannelManager;
+import com.trilead.ssh2.channel.X11ServerData;
+
+
+/**
+ * A <code>Session</code> is a remote execution of a program. "Program" means
+ * in this context either a shell, an application or a system command. The
+ * program may or may not have a tty. Only one single program can be started on
+ * a session. However, multiple sessions can be active simultaneously.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: Session.java,v 1.2 2008/03/03 07:01:36 cplattne Exp $
+ */
+public class Session
+{
+ ChannelManager cm;
+ Channel cn;
+
+ boolean flag_pty_requested = false;
+ boolean flag_x11_requested = false;
+ boolean flag_execution_started = false;
+ boolean flag_closed = false;
+
+ String x11FakeCookie = null;
+
+ final SecureRandom rnd;
+
+ Session(ChannelManager cm, SecureRandom rnd) throws IOException
+ {
+ this.cm = cm;
+ this.cn = cm.openSessionChannel();
+ this.rnd = rnd;
+ }
+
+ /**
+ * Basically just a wrapper for lazy people - identical to calling
+ * <code>requestPTY("dumb", 0, 0, 0, 0, null)</code>.
+ *
+ * @throws IOException
+ */
+ public void requestDumbPTY() throws IOException
+ {
+ requestPTY("dumb", 0, 0, 0, 0, null);
+ }
+
+ /**
+ * Basically just another wrapper for lazy people - identical to calling
+ * <code>requestPTY(term, 0, 0, 0, 0, null)</code>.
+ *
+ * @throws IOException
+ */
+ public void requestPTY(String term) throws IOException
+ {
+ requestPTY(term, 0, 0, 0, 0, null);
+ }
+
+ /**
+ * Allocate a pseudo-terminal for this session.
+ * <p>
+ * This method may only be called before a program or shell is started in
+ * this session.
+ * <p>
+ * Different aspects can be specified:
+ * <p>
+ * <ul>
+ * <li>The TERM environment variable value (e.g., vt100)</li>
+ * <li>The terminal's dimensions.</li>
+ * <li>The encoded terminal modes.</li>
+ * </ul>
+ * Zero dimension parameters are ignored. The character/row dimensions
+ * override the pixel dimensions (when nonzero). Pixel dimensions refer to
+ * the drawable area of the window. The dimension parameters are only
+ * informational. The encoding of terminal modes (parameter
+ * <code>terminal_modes</code>) is described in RFC4254.
+ *
+ * @param term
+ * The TERM environment variable value (e.g., vt100)
+ * @param term_width_characters
+ * terminal width, characters (e.g., 80)
+ * @param term_height_characters
+ * terminal height, rows (e.g., 24)
+ * @param term_width_pixels
+ * terminal width, pixels (e.g., 640)
+ * @param term_height_pixels
+ * terminal height, pixels (e.g., 480)
+ * @param terminal_modes
+ * encoded terminal modes (may be <code>null</code>)
+ * @throws IOException
+ */
+ public void requestPTY(String term, int term_width_characters, int term_height_characters, int term_width_pixels,
+ int term_height_pixels, byte[] terminal_modes) throws IOException
+ {
+ if (term == null)
+ throw new IllegalArgumentException("TERM cannot be null.");
+
+ if ((terminal_modes != null) && (terminal_modes.length > 0))
+ {
+ if (terminal_modes[terminal_modes.length - 1] != 0)
+ throw new IOException("Illegal terminal modes description, does not end in zero byte");
+ }
+ else
+ terminal_modes = new byte[] { 0 };
+
+ synchronized (this)
+ {
+ /* The following is just a nicer error, we would catch it anyway later in the channel code */
+ if (flag_closed)
+ throw new IOException("This session is closed.");
+
+ if (flag_pty_requested)
+ throw new IOException("A PTY was already requested.");
+
+ if (flag_execution_started)
+ throw new IOException(
+ "Cannot request PTY at this stage anymore, a remote execution has already started.");
+
+ flag_pty_requested = true;
+ }
+
+ cm.requestPTY(cn, term, term_width_characters, term_height_characters, term_width_pixels, term_height_pixels,
+ terminal_modes);
+ }
+
+ /**
+ * Inform other side of connection that our PTY has resized.
+ * <p>
+ * Zero dimension parameters are ignored. The character/row dimensions
+ * override the pixel dimensions (when nonzero). Pixel dimensions refer to
+ * the drawable area of the window. The dimension parameters are only
+ * informational.
+ *
+ * @param term_width_characters
+ * terminal width, characters (e.g., 80)
+ * @param term_height_characters
+ * terminal height, rows (e.g., 24)
+ * @param term_width_pixels
+ * terminal width, pixels (e.g., 640)
+ * @param term_height_pixels
+ * terminal height, pixels (e.g., 480)
+ * @throws IOException
+ */
+ public void resizePTY(int term_width_characters, int term_height_characters, int term_width_pixels,
+ int term_height_pixels) throws IOException {
+ synchronized (this)
+ {
+ /* The following is just a nicer error, we would catch it anyway later in the channel code */
+ if (flag_closed)
+ throw new IOException("This session is closed.");
+ }
+
+ cm.resizePTY(cn, term_width_characters, term_height_characters, term_width_pixels, term_height_pixels);
+ }
+
+ /**
+ * Request X11 forwarding for the current session.
+ * <p>
+ * You have to supply the name and port of your X-server.
+ * <p>
+ * This method may only be called before a program or shell is started in
+ * this session.
+ *
+ * @param hostname the hostname of the real (target) X11 server (e.g., 127.0.0.1)
+ * @param port the port of the real (target) X11 server (e.g., 6010)
+ * @param cookie if non-null, then present this cookie to the real X11 server
+ * @param singleConnection if true, then the server is instructed to only forward one single
+ * connection, no more connections shall be forwarded after first, or after the session
+ * channel has been closed
+ * @throws IOException
+ */
+ public void requestX11Forwarding(String hostname, int port, byte[] cookie, boolean singleConnection)
+ throws IOException
+ {
+ if (hostname == null)
+ throw new IllegalArgumentException("hostname argument may not be null");
+
+ synchronized (this)
+ {
+ /* The following is just a nicer error, we would catch it anyway later in the channel code */
+ if (flag_closed)
+ throw new IOException("This session is closed.");
+
+ if (flag_x11_requested)
+ throw new IOException("X11 forwarding was already requested.");
+
+ if (flag_execution_started)
+ throw new IOException(
+ "Cannot request X11 forwarding at this stage anymore, a remote execution has already started.");
+
+ flag_x11_requested = true;
+ }
+
+ /* X11ServerData - used to store data about the target X11 server */
+
+ X11ServerData x11data = new X11ServerData();
+
+ x11data.hostname = hostname;
+ x11data.port = port;
+ x11data.x11_magic_cookie = cookie; /* if non-null, then present this cookie to the real X11 server */
+
+ /* Generate fake cookie - this one is used between remote clients and our proxy */
+
+ byte[] fakeCookie = new byte[16];
+ String hexEncodedFakeCookie;
+
+ /* Make sure that this fake cookie is unique for this connection */
+
+ while (true)
+ {
+ rnd.nextBytes(fakeCookie);
+
+ /* Generate also hex representation of fake cookie */
+
+ StringBuffer tmp = new StringBuffer(32);
+ for (int i = 0; i < fakeCookie.length; i++)
+ {
+ String digit2 = Integer.toHexString(fakeCookie[i] & 0xff);
+ tmp.append((digit2.length() == 2) ? digit2 : "0" + digit2);
+ }
+ hexEncodedFakeCookie = tmp.toString();
+
+ /* Well, yes, chances are low, but we want to be on the safe side */
+
+ if (cm.checkX11Cookie(hexEncodedFakeCookie) == null)
+ break;
+ }
+
+ /* Ask for X11 forwarding */
+
+ cm.requestX11(cn, singleConnection, "MIT-MAGIC-COOKIE-1", hexEncodedFakeCookie, 0);
+
+ /* OK, that went fine, get ready to accept X11 connections... */
+ /* ... but only if the user has not called close() in the meantime =) */
+
+ synchronized (this)
+ {
+ if (flag_closed == false)
+ {
+ this.x11FakeCookie = hexEncodedFakeCookie;
+ cm.registerX11Cookie(hexEncodedFakeCookie, x11data);
+ }
+ }
+
+ /* Now it is safe to start remote X11 programs */
+ }
+
+ /**
+ * Execute a command on the remote machine.
+ *
+ * @param cmd
+ * The command to execute on the remote host.
+ * @throws IOException
+ */
+ public void execCommand(String cmd) throws IOException
+ {
+ if (cmd == null)
+ throw new IllegalArgumentException("cmd argument may not be null");
+
+ synchronized (this)
+ {
+ /* The following is just a nicer error, we would catch it anyway later in the channel code */
+ if (flag_closed)
+ throw new IOException("This session is closed.");
+
+ if (flag_execution_started)
+ throw new IOException("A remote execution has already started.");
+
+ flag_execution_started = true;
+ }
+
+ cm.requestExecCommand(cn, cmd);
+ }
+
+ /**
+ * Start a shell on the remote machine.
+ *
+ * @throws IOException
+ */
+ public void startShell() throws IOException
+ {
+ synchronized (this)
+ {
+ /* The following is just a nicer error, we would catch it anyway later in the channel code */
+ if (flag_closed)
+ throw new IOException("This session is closed.");
+
+ if (flag_execution_started)
+ throw new IOException("A remote execution has already started.");
+
+ flag_execution_started = true;
+ }
+
+ cm.requestShell(cn);
+ }
+
+ /**
+ * Start a subsystem on the remote machine.
+ * Unless you know what you are doing, you will never need this.
+ *
+ * @param name the name of the subsystem.
+ * @throws IOException
+ */
+ public void startSubSystem(String name) throws IOException
+ {
+ if (name == null)
+ throw new IllegalArgumentException("name argument may not be null");
+
+ synchronized (this)
+ {
+ /* The following is just a nicer error, we would catch it anyway later in the channel code */
+ if (flag_closed)
+ throw new IOException("This session is closed.");
+
+ if (flag_execution_started)
+ throw new IOException("A remote execution has already started.");
+
+ flag_execution_started = true;
+ }
+
+ cm.requestSubSystem(cn, name);
+ }
+
+ /**
+ * This method can be used to perform end-to-end session (i.e., SSH channel)
+ * testing. It sends a 'ping' message to the server and waits for the 'pong'
+ * from the server.
+ * <p>
+ * Implementation details: this method sends a SSH_MSG_CHANNEL_REQUEST request
+ * ('trilead-ping') to the server and waits for the SSH_MSG_CHANNEL_FAILURE reply
+ * packet.
+ *
+ * @throws IOException in case of any problem or when the session is closed
+ */
+ public void ping() throws IOException
+ {
+ synchronized (this)
+ {
+ /*
+ * The following is just a nicer error, we would catch it anyway
+ * later in the channel code
+ */
+ if (flag_closed)
+ throw new IOException("This session is closed.");
+ }
+
+ cm.requestChannelTrileadPing(cn);
+ }
+
+ /**
+ * Request authentication agent forwarding.
+ * @param agent object that implements the callbacks
+ *
+ * @throws IOException in case of any problem or when the session is closed
+ */
+ public synchronized boolean requestAuthAgentForwarding(AuthAgentCallback agent) throws IOException
+ {
+ synchronized (this)
+ {
+ /*
+ * The following is just a nicer error, we would catch it anyway
+ * later in the channel code
+ */
+ if (flag_closed)
+ throw new IOException("This session is closed.");
+ }
+
+ return cm.requestChannelAgentForwarding(cn, agent);
+ }
+
+ public InputStream getStdout()
+ {
+ return cn.getStdoutStream();
+ }
+
+ public InputStream getStderr()
+ {
+ return cn.getStderrStream();
+ }
+
+ public OutputStream getStdin()
+ {
+ return cn.getStdinStream();
+ }
+
+ /**
+ * This method blocks until there is more data available on either the
+ * stdout or stderr InputStream of this <code>Session</code>. Very useful
+ * if you do not want to use two parallel threads for reading from the two
+ * InputStreams. One can also specify a timeout. NOTE: do NOT call this
+ * method if you use concurrent threads that operate on either of the two
+ * InputStreams of this <code>Session</code> (otherwise this method may
+ * block, even though more data is available).
+ *
+ * @param timeout
+ * The (non-negative) timeout in <code>ms</code>. <code>0</code> means no
+ * timeout, the call may block forever.
+ * @return
+ * <ul>
+ * <li><code>0</code> if no more data will arrive.</li>
+ * <li><code>1</code> if more data is available.</li>
+ * <li><code>-1</code> if a timeout occurred.</li>
+ * </ul>
+ *
+ * @throws IOException
+ * @deprecated This method has been replaced with a much more powerful wait-for-condition
+ * interface and therefore acts only as a wrapper.
+ *
+ */
+ public int waitUntilDataAvailable(long timeout) throws IOException
+ {
+ if (timeout < 0)
+ throw new IllegalArgumentException("timeout must not be negative!");
+
+ int conditions = cm.waitForCondition(cn, timeout, ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA
+ | ChannelCondition.EOF);
+
+ if ((conditions & ChannelCondition.TIMEOUT) != 0)
+ return -1;
+
+ if ((conditions & (ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA)) != 0)
+ return 1;
+
+ /* Here we do not need to check separately for CLOSED, since CLOSED implies EOF */
+
+ if ((conditions & ChannelCondition.EOF) != 0)
+ return 0;
+
+ throw new IllegalStateException("Unexpected condition result (" + conditions + ")");
+ }
+
+ /**
+ * This method blocks until certain conditions hold true on the underlying SSH-2 channel.
+ * <p>
+ * This method returns as soon as one of the following happens:
+ * <ul>
+ * <li>at least of the specified conditions (see {@link ChannelCondition}) holds true</li>
+ * <li>timeout > 0 and a timeout occured (TIMEOUT will be set in result conditions)</a>
+ * <li>the underlying channel was closed (CLOSED will be set in result conditions)</a>
+ * </ul>
+ * <p>
+ * In any case, the result value contains ALL current conditions, which may be more
+ * than the specified condition set (i.e., never use the "==" operator to test for conditions
+ * in the bitmask, see also comments in {@link ChannelCondition}).
+ * <p>
+ * Note: do NOT call this method if you want to wait for STDOUT_DATA or STDERR_DATA and
+ * there are concurrent threads (e.g., StreamGobblers) that operate on either of the two
+ * InputStreams of this <code>Session</code> (otherwise this method may
+ * block, even though more data is available in the StreamGobblers).
+ *
+ * @param condition_set a bitmask based on {@link ChannelCondition} values
+ * @param timeout non-negative timeout in ms, <code>0</code> means no timeout
+ * @return all bitmask specifying all current conditions that are true
+ */
+
+ public int waitForCondition(int condition_set, long timeout)
+ {
+ if (timeout < 0)
+ throw new IllegalArgumentException("timeout must be non-negative!");
+
+ return cm.waitForCondition(cn, timeout, condition_set);
+ }
+
+ /**
+ * Get the exit code/status from the remote command - if available. Be
+ * careful - not all server implementations return this value. It is
+ * generally a good idea to call this method only when all data from the
+ * remote side has been consumed (see also the <code<WaitForCondition</code> method).
+ *
+ * @return An <code>Integer</code> holding the exit code, or
+ * <code>null</code> if no exit code is (yet) available.
+ */
+ public Integer getExitStatus()
+ {
+ return cn.getExitStatus();
+ }
+
+ /**
+ * Get the name of the signal by which the process on the remote side was
+ * stopped - if available and applicable. Be careful - not all server
+ * implementations return this value.
+ *
+ * @return An <code>String</code> holding the name of the signal, or
+ * <code>null</code> if the process exited normally or is still
+ * running (or if the server forgot to send this information).
+ */
+ public String getExitSignal()
+ {
+ return cn.getExitSignal();
+ }
+
+ /**
+ * Close this session. NEVER forget to call this method to free up resources -
+ * even if you got an exception from one of the other methods (or when
+ * getting an Exception on the Input- or OutputStreams). Sometimes these other
+ * methods may throw an exception, saying that the underlying channel is
+ * closed (this can happen, e.g., if the other server sent a close message.)
+ * However, as long as you have not called the <code>close()</code>
+ * method, you may be wasting (local) resources.
+ *
+ */
+ public void close()
+ {
+ synchronized (this)
+ {
+ if (flag_closed)
+ return;
+
+ flag_closed = true;
+
+ if (x11FakeCookie != null)
+ cm.unRegisterX11Cookie(x11FakeCookie, true);
+
+ try
+ {
+ cm.closeChannel(cn, "Closed due to user request", true);
+ }
+ catch (IOException ignored)
+ {
+ }
+ }
+ }
+}
diff --git a/src/com/trilead/ssh2/StreamGobbler.java b/src/com/trilead/ssh2/StreamGobbler.java
index 1e87bb1..e93c388 100644
--- a/src/com/trilead/ssh2/StreamGobbler.java
+++ b/src/com/trilead/ssh2/StreamGobbler.java
@@ -1,229 +1,229 @@
-
-package com.trilead.ssh2;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * A <code>StreamGobbler</code> is an InputStream that uses an internal worker
- * thread to constantly consume input from another InputStream. It uses a buffer
- * to store the consumed data. The buffer size is automatically adjusted, if needed.
- * <p>
- * This class is sometimes very convenient - if you wrap a session's STDOUT and STDERR
- * InputStreams with instances of this class, then you don't have to bother about
- * the shared window of STDOUT and STDERR in the low level SSH-2 protocol,
- * since all arriving data will be immediatelly consumed by the worker threads.
- * Also, as a side effect, the streams will be buffered (e.g., single byte
- * read() operations are faster).
- * <p>
- * Other SSH for Java libraries include this functionality by default in
- * their STDOUT and STDERR InputStream implementations, however, please be aware
- * that this approach has also a downside:
- * <p>
- * If you do not call the StreamGobbler's <code>read()</code> method often enough
- * and the peer is constantly sending huge amounts of data, then you will sooner or later
- * encounter a low memory situation due to the aggregated data (well, it also depends on the Java heap size).
- * Joe Average will like this class anyway - a paranoid programmer would never use such an approach.
- * <p>
- * The term "StreamGobbler" was taken from an article called "When Runtime.exec() won't",
- * see http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: StreamGobbler.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-
-public class StreamGobbler extends InputStream
-{
- class GobblerThread extends Thread
- {
- public void run()
- {
- byte[] buff = new byte[8192];
-
- while (true)
- {
- try
- {
- int avail = is.read(buff);
-
- synchronized (synchronizer)
- {
- if (avail <= 0)
- {
- isEOF = true;
- synchronizer.notifyAll();
- break;
- }
-
- int space_available = buffer.length - write_pos;
-
- if (space_available < avail)
- {
- /* compact/resize buffer */
-
- int unread_size = write_pos - read_pos;
- int need_space = unread_size + avail;
-
- byte[] new_buffer = buffer;
-
- if (need_space > buffer.length)
- {
- int inc = need_space / 3;
- inc = (inc < 256) ? 256 : inc;
- inc = (inc > 8192) ? 8192 : inc;
- new_buffer = new byte[need_space + inc];
- }
-
- if (unread_size > 0)
- System.arraycopy(buffer, read_pos, new_buffer, 0, unread_size);
-
- buffer = new_buffer;
-
- read_pos = 0;
- write_pos = unread_size;
- }
-
- System.arraycopy(buff, 0, buffer, write_pos, avail);
- write_pos += avail;
-
- synchronizer.notifyAll();
- }
- }
- catch (IOException e)
- {
- synchronized (synchronizer)
- {
- exception = e;
- synchronizer.notifyAll();
- break;
- }
- }
- }
- }
- }
-
- private InputStream is;
- private GobblerThread t;
-
- private Object synchronizer = new Object();
-
- private boolean isEOF = false;
- private boolean isClosed = false;
- private IOException exception = null;
-
- private byte[] buffer = new byte[2048];
- private int read_pos = 0;
- private int write_pos = 0;
-
- public StreamGobbler(InputStream is)
- {
- this.is = is;
- t = new GobblerThread();
- t.setDaemon(true);
- t.start();
- }
-
- public int read() throws IOException
- {
- synchronized (synchronizer)
- {
- if (isClosed)
- throw new IOException("This StreamGobbler is closed.");
-
- while (read_pos == write_pos)
- {
- if (exception != null)
- throw exception;
-
- if (isEOF)
- return -1;
-
- try
- {
- synchronizer.wait();
- }
- catch (InterruptedException e)
- {
- }
- }
-
- int b = buffer[read_pos++] & 0xff;
-
- return b;
- }
- }
-
- public int available() throws IOException
- {
- synchronized (synchronizer)
- {
- if (isClosed)
- throw new IOException("This StreamGobbler is closed.");
-
- return write_pos - read_pos;
- }
- }
-
- public int read(byte[] b) throws IOException
- {
- return read(b, 0, b.length);
- }
-
- public void close() throws IOException
- {
- synchronized (synchronizer)
- {
- if (isClosed)
- return;
- isClosed = true;
- isEOF = true;
- synchronizer.notifyAll();
- is.close();
- }
- }
-
- public int read(byte[] b, int off, int len) throws IOException
- {
- if (b == null)
- throw new NullPointerException();
-
- if ((off < 0) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0) || (off > b.length))
- throw new IndexOutOfBoundsException();
-
- if (len == 0)
- return 0;
-
- synchronized (synchronizer)
- {
- if (isClosed)
- throw new IOException("This StreamGobbler is closed.");
-
- while (read_pos == write_pos)
- {
- if (exception != null)
- throw exception;
-
- if (isEOF)
- return -1;
-
- try
- {
- synchronizer.wait();
- }
- catch (InterruptedException e)
- {
- }
- }
-
- int avail = write_pos - read_pos;
-
- avail = (avail > len) ? len : avail;
-
- System.arraycopy(buffer, read_pos, b, off, avail);
-
- read_pos += avail;
-
- return avail;
- }
- }
-}
+
+package com.trilead.ssh2;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A <code>StreamGobbler</code> is an InputStream that uses an internal worker
+ * thread to constantly consume input from another InputStream. It uses a buffer
+ * to store the consumed data. The buffer size is automatically adjusted, if needed.
+ * <p>
+ * This class is sometimes very convenient - if you wrap a session's STDOUT and STDERR
+ * InputStreams with instances of this class, then you don't have to bother about
+ * the shared window of STDOUT and STDERR in the low level SSH-2 protocol,
+ * since all arriving data will be immediatelly consumed by the worker threads.
+ * Also, as a side effect, the streams will be buffered (e.g., single byte
+ * read() operations are faster).
+ * <p>
+ * Other SSH for Java libraries include this functionality by default in
+ * their STDOUT and STDERR InputStream implementations, however, please be aware
+ * that this approach has also a downside:
+ * <p>
+ * If you do not call the StreamGobbler's <code>read()</code> method often enough
+ * and the peer is constantly sending huge amounts of data, then you will sooner or later
+ * encounter a low memory situation due to the aggregated data (well, it also depends on the Java heap size).
+ * Joe Average will like this class anyway - a paranoid programmer would never use such an approach.
+ * <p>
+ * The term "StreamGobbler" was taken from an article called "When Runtime.exec() won't",
+ * see http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: StreamGobbler.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+
+public class StreamGobbler extends InputStream
+{
+ class GobblerThread extends Thread
+ {
+ public void run()
+ {
+ byte[] buff = new byte[8192];
+
+ while (true)
+ {
+ try
+ {
+ int avail = is.read(buff);
+
+ synchronized (synchronizer)
+ {
+ if (avail <= 0)
+ {
+ isEOF = true;
+ synchronizer.notifyAll();
+ break;
+ }
+
+ int space_available = buffer.length - write_pos;
+
+ if (space_available < avail)
+ {
+ /* compact/resize buffer */
+
+ int unread_size = write_pos - read_pos;
+ int need_space = unread_size + avail;
+
+ byte[] new_buffer = buffer;
+
+ if (need_space > buffer.length)
+ {
+ int inc = need_space / 3;
+ inc = (inc < 256) ? 256 : inc;
+ inc = (inc > 8192) ? 8192 : inc;
+ new_buffer = new byte[need_space + inc];
+ }
+
+ if (unread_size > 0)
+ System.arraycopy(buffer, read_pos, new_buffer, 0, unread_size);
+
+ buffer = new_buffer;
+
+ read_pos = 0;
+ write_pos = unread_size;
+ }
+
+ System.arraycopy(buff, 0, buffer, write_pos, avail);
+ write_pos += avail;
+
+ synchronizer.notifyAll();
+ }
+ }
+ catch (IOException e)
+ {
+ synchronized (synchronizer)
+ {
+ exception = e;
+ synchronizer.notifyAll();
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ private InputStream is;
+ private GobblerThread t;
+
+ private Object synchronizer = new Object();
+
+ private boolean isEOF = false;
+ private boolean isClosed = false;
+ private IOException exception = null;
+
+ private byte[] buffer = new byte[2048];
+ private int read_pos = 0;
+ private int write_pos = 0;
+
+ public StreamGobbler(InputStream is)
+ {
+ this.is = is;
+ t = new GobblerThread();
+ t.setDaemon(true);
+ t.start();
+ }
+
+ public int read() throws IOException
+ {
+ synchronized (synchronizer)
+ {
+ if (isClosed)
+ throw new IOException("This StreamGobbler is closed.");
+
+ while (read_pos == write_pos)
+ {
+ if (exception != null)
+ throw exception;
+
+ if (isEOF)
+ return -1;
+
+ try
+ {
+ synchronizer.wait();
+ }
+ catch (InterruptedException e)
+ {
+ }
+ }
+
+ int b = buffer[read_pos++] & 0xff;
+
+ return b;
+ }
+ }
+
+ public int available() throws IOException
+ {
+ synchronized (synchronizer)
+ {
+ if (isClosed)
+ throw new IOException("This StreamGobbler is closed.");
+
+ return write_pos - read_pos;
+ }
+ }
+
+ public int read(byte[] b) throws IOException
+ {
+ return read(b, 0, b.length);
+ }
+
+ public void close() throws IOException
+ {
+ synchronized (synchronizer)
+ {
+ if (isClosed)
+ return;
+ isClosed = true;
+ isEOF = true;
+ synchronizer.notifyAll();
+ is.close();
+ }
+ }
+
+ public int read(byte[] b, int off, int len) throws IOException
+ {
+ if (b == null)
+ throw new NullPointerException();
+
+ if ((off < 0) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0) || (off > b.length))
+ throw new IndexOutOfBoundsException();
+
+ if (len == 0)
+ return 0;
+
+ synchronized (synchronizer)
+ {
+ if (isClosed)
+ throw new IOException("This StreamGobbler is closed.");
+
+ while (read_pos == write_pos)
+ {
+ if (exception != null)
+ throw exception;
+
+ if (isEOF)
+ return -1;
+
+ try
+ {
+ synchronizer.wait();
+ }
+ catch (InterruptedException e)
+ {
+ }
+ }
+
+ int avail = write_pos - read_pos;
+
+ avail = (avail > len) ? len : avail;
+
+ System.arraycopy(buffer, read_pos, b, off, avail);
+
+ read_pos += avail;
+
+ return avail;
+ }
+ }
+}
diff --git a/src/com/trilead/ssh2/auth/AuthenticationManager.java b/src/com/trilead/ssh2/auth/AuthenticationManager.java
index 43c226a..e551495 100644
--- a/src/com/trilead/ssh2/auth/AuthenticationManager.java
+++ b/src/com/trilead/ssh2/auth/AuthenticationManager.java
@@ -1,427 +1,466 @@
-
-package com.trilead.ssh2.auth;
-
-import java.io.IOException;
-import java.security.SecureRandom;
-import java.util.Vector;
-
-import com.trilead.ssh2.InteractiveCallback;
-import com.trilead.ssh2.crypto.PEMDecoder;
-import com.trilead.ssh2.packets.PacketServiceAccept;
-import com.trilead.ssh2.packets.PacketServiceRequest;
-import com.trilead.ssh2.packets.PacketUserauthBanner;
-import com.trilead.ssh2.packets.PacketUserauthFailure;
-import com.trilead.ssh2.packets.PacketUserauthInfoRequest;
-import com.trilead.ssh2.packets.PacketUserauthInfoResponse;
-import com.trilead.ssh2.packets.PacketUserauthRequestInteractive;
-import com.trilead.ssh2.packets.PacketUserauthRequestNone;
-import com.trilead.ssh2.packets.PacketUserauthRequestPassword;
-import com.trilead.ssh2.packets.PacketUserauthRequestPublicKey;
-import com.trilead.ssh2.packets.Packets;
-import com.trilead.ssh2.packets.TypesWriter;
-import com.trilead.ssh2.signature.DSAPrivateKey;
-import com.trilead.ssh2.signature.DSASHA1Verify;
-import com.trilead.ssh2.signature.DSASignature;
-import com.trilead.ssh2.signature.RSAPrivateKey;
-import com.trilead.ssh2.signature.RSASHA1Verify;
-import com.trilead.ssh2.signature.RSASignature;
-import com.trilead.ssh2.transport.MessageHandler;
-import com.trilead.ssh2.transport.TransportManager;
-
-
-/**
- * AuthenticationManager.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: AuthenticationManager.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public class AuthenticationManager implements MessageHandler
-{
- TransportManager tm;
-
- Vector packets = new Vector();
- boolean connectionClosed = false;
-
- String banner;
-
- String[] remainingMethods = new String[0];
- boolean isPartialSuccess = false;
-
- boolean authenticated = false;
- boolean initDone = false;
-
- public AuthenticationManager(TransportManager tm)
- {
- this.tm = tm;
- }
-
- boolean methodPossible(String methName)
- {
- if (remainingMethods == null)
- return false;
-
- for (int i = 0; i < remainingMethods.length; i++)
- {
- if (remainingMethods[i].compareTo(methName) == 0)
- return true;
- }
- return false;
- }
-
- byte[] deQueue() throws IOException
- {
- synchronized (packets)
- {
- while (packets.size() == 0)
- {
- if (connectionClosed)
- throw (IOException) new IOException("The connection is closed.").initCause(tm
- .getReasonClosedCause());
-
- try
- {
- packets.wait();
- }
- catch (InterruptedException ign)
- {
- }
- }
- /* This sequence works with J2ME */
- byte[] res = (byte[]) packets.firstElement();
- packets.removeElementAt(0);
- return res;
- }
- }
-
- byte[] getNextMessage() throws IOException
- {
- while (true)
- {
- byte[] msg = deQueue();
-
- if (msg[0] != Packets.SSH_MSG_USERAUTH_BANNER)
- return msg;
-
- PacketUserauthBanner sb = new PacketUserauthBanner(msg, 0, msg.length);
-
- banner = sb.getBanner();
- }
- }
-
- public String[] getRemainingMethods(String user) throws IOException
- {
- initialize(user);
- return remainingMethods;
- }
-
- public boolean getPartialSuccess()
- {
- return isPartialSuccess;
- }
-
- private boolean initialize(String user) throws IOException
- {
- if (initDone == false)
- {
- tm.registerMessageHandler(this, 0, 255);
-
- PacketServiceRequest sr = new PacketServiceRequest("ssh-userauth");
- tm.sendMessage(sr.getPayload());
-
- PacketUserauthRequestNone urn = new PacketUserauthRequestNone("ssh-connection", user);
- tm.sendMessage(urn.getPayload());
-
- byte[] msg = getNextMessage();
- new PacketServiceAccept(msg, 0, msg.length);
- msg = getNextMessage();
-
- initDone = true;
-
- if (msg[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
- {
- authenticated = true;
- tm.removeMessageHandler(this, 0, 255);
- return true;
- }
-
- if (msg[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
- {
- PacketUserauthFailure puf = new PacketUserauthFailure(msg, 0, msg.length);
-
- remainingMethods = puf.getAuthThatCanContinue();
- isPartialSuccess = puf.isPartialSuccess();
- return false;
- }
-
- throw new IOException("Unexpected SSH message (type " + msg[0] + ")");
- }
- return authenticated;
- }
-
- public boolean authenticatePublicKey(String user, char[] PEMPrivateKey, String password, SecureRandom rnd)
- throws IOException
- {
- Object key = PEMDecoder.decode(PEMPrivateKey, password);
-
- return authenticatePublicKey(user, key, rnd);
- }
-
- public boolean authenticatePublicKey(String user, Object key, SecureRandom rnd)
- throws IOException
- {
- try
- {
- initialize(user);
-
- if (methodPossible("publickey") == false)
- throw new IOException("Authentication method publickey not supported by the server at this stage.");
-
- if (key instanceof DSAPrivateKey)
- {
- DSAPrivateKey pk = (DSAPrivateKey) key;
-
- byte[] pk_enc = DSASHA1Verify.encodeSSHDSAPublicKey(pk.getPublicKey());
-
- TypesWriter tw = new TypesWriter();
-
- byte[] H = tm.getSessionIdentifier();
-
- tw.writeString(H, 0, H.length);
- tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
- tw.writeString(user);
- tw.writeString("ssh-connection");
- tw.writeString("publickey");
- tw.writeBoolean(true);
- tw.writeString("ssh-dss");
- tw.writeString(pk_enc, 0, pk_enc.length);
-
- byte[] msg = tw.getBytes();
-
- DSASignature ds = DSASHA1Verify.generateSignature(msg, pk, rnd);
-
- byte[] ds_enc = DSASHA1Verify.encodeSSHDSASignature(ds);
-
- PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user,
- "ssh-dss", pk_enc, ds_enc);
- tm.sendMessage(ua.getPayload());
- }
- else if (key instanceof RSAPrivateKey)
- {
- RSAPrivateKey pk = (RSAPrivateKey) key;
-
- byte[] pk_enc = RSASHA1Verify.encodeSSHRSAPublicKey(pk.getPublicKey());
-
- TypesWriter tw = new TypesWriter();
- {
- byte[] H = tm.getSessionIdentifier();
-
- tw.writeString(H, 0, H.length);
- tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
- tw.writeString(user);
- tw.writeString("ssh-connection");
- tw.writeString("publickey");
- tw.writeBoolean(true);
- tw.writeString("ssh-rsa");
- tw.writeString(pk_enc, 0, pk_enc.length);
- }
-
- byte[] msg = tw.getBytes();
-
- RSASignature ds = RSASHA1Verify.generateSignature(msg, pk);
-
- byte[] rsa_sig_enc = RSASHA1Verify.encodeSSHRSASignature(ds);
-
- PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user,
- "ssh-rsa", pk_enc, rsa_sig_enc);
-
- tm.sendMessage(ua.getPayload());
-
- }
- else
- {
- throw new IOException("Unknown private key type returned by the PEM decoder.");
- }
-
- byte[] ar = getNextMessage();
- if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
- {
- authenticated = true;
- tm.removeMessageHandler(this, 0, 255);
- return true;
- }
-
- if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
- {
- PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);
-
- remainingMethods = puf.getAuthThatCanContinue();
- isPartialSuccess = puf.isPartialSuccess();
-
- return false;
- }
-
- throw new IOException("Unexpected SSH message (type " + ar[0] + ")");
-
- }
- catch (IOException e)
- {
-e.printStackTrace();
- tm.close(e, false);
- throw (IOException) new IOException("Publickey authentication failed.").initCause(e);
- }
- }
-
- public boolean authenticateNone(String user) throws IOException
- {
- try
- {
- initialize(user);
- return authenticated;
- }
- catch (IOException e)
- {
- tm.close(e, false);
- throw (IOException) new IOException("None authentication failed.").initCause(e);
- }
- }
-
- public boolean authenticatePassword(String user, String pass) throws IOException
- {
- try
- {
- initialize(user);
-
- if (methodPossible("password") == false)
- throw new IOException("Authentication method password not supported by the server at this stage.");
-
- PacketUserauthRequestPassword ua = new PacketUserauthRequestPassword("ssh-connection", user, pass);
- tm.sendMessage(ua.getPayload());
-
- byte[] ar = getNextMessage();
-
- if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
- {
- authenticated = true;
- tm.removeMessageHandler(this, 0, 255);
- return true;
- }
-
- if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
- {
- PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);
-
- remainingMethods = puf.getAuthThatCanContinue();
- isPartialSuccess = puf.isPartialSuccess();
-
- return false;
- }
-
- throw new IOException("Unexpected SSH message (type " + ar[0] + ")");
-
- }
- catch (IOException e)
- {
- tm.close(e, false);
- throw (IOException) new IOException("Password authentication failed.").initCause(e);
- }
- }
-
- public boolean authenticateInteractive(String user, String[] submethods, InteractiveCallback cb) throws IOException
- {
- try
- {
- initialize(user);
-
- if (methodPossible("keyboard-interactive") == false)
- throw new IOException(
- "Authentication method keyboard-interactive not supported by the server at this stage.");
-
- if (submethods == null)
- submethods = new String[0];
-
- PacketUserauthRequestInteractive ua = new PacketUserauthRequestInteractive("ssh-connection", user,
- submethods);
-
- tm.sendMessage(ua.getPayload());
-
- while (true)
- {
- byte[] ar = getNextMessage();
-
- if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
- {
- authenticated = true;
- tm.removeMessageHandler(this, 0, 255);
- return true;
- }
-
- if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
- {
- PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);
-
- remainingMethods = puf.getAuthThatCanContinue();
- isPartialSuccess = puf.isPartialSuccess();
-
- return false;
- }
-
- if (ar[0] == Packets.SSH_MSG_USERAUTH_INFO_REQUEST)
- {
- PacketUserauthInfoRequest pui = new PacketUserauthInfoRequest(ar, 0, ar.length);
-
- String[] responses;
-
- try
- {
- responses = cb.replyToChallenge(pui.getName(), pui.getInstruction(), pui.getNumPrompts(), pui
- .getPrompt(), pui.getEcho());
- }
- catch (Exception e)
- {
- throw (IOException) new IOException("Exception in callback.").initCause(e);
- }
-
- if (responses == null)
- throw new IOException("Your callback may not return NULL!");
-
- PacketUserauthInfoResponse puir = new PacketUserauthInfoResponse(responses);
- tm.sendMessage(puir.getPayload());
-
- continue;
- }
-
- throw new IOException("Unexpected SSH message (type " + ar[0] + ")");
- }
- }
- catch (IOException e)
- {
- tm.close(e, false);
- throw (IOException) new IOException("Keyboard-interactive authentication failed.").initCause(e);
- }
- }
-
- public void handleMessage(byte[] msg, int msglen) throws IOException
- {
- synchronized (packets)
- {
- if (msg == null)
- {
- connectionClosed = true;
- }
- else
- {
- byte[] tmp = new byte[msglen];
- System.arraycopy(msg, 0, tmp, 0, msglen);
- packets.addElement(tmp);
- }
-
- packets.notifyAll();
-
- if (packets.size() > 5)
- {
- connectionClosed = true;
- throw new IOException("Error, peer is flooding us with authentication packets.");
- }
- }
- }
-}
+
+package com.trilead.ssh2.auth;
+
+import java.io.IOException;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.SecureRandom;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.Vector;
+
+import com.trilead.ssh2.InteractiveCallback;
+import com.trilead.ssh2.crypto.PEMDecoder;
+import com.trilead.ssh2.packets.PacketServiceAccept;
+import com.trilead.ssh2.packets.PacketServiceRequest;
+import com.trilead.ssh2.packets.PacketUserauthBanner;
+import com.trilead.ssh2.packets.PacketUserauthFailure;
+import com.trilead.ssh2.packets.PacketUserauthInfoRequest;
+import com.trilead.ssh2.packets.PacketUserauthInfoResponse;
+import com.trilead.ssh2.packets.PacketUserauthRequestInteractive;
+import com.trilead.ssh2.packets.PacketUserauthRequestNone;
+import com.trilead.ssh2.packets.PacketUserauthRequestPassword;
+import com.trilead.ssh2.packets.PacketUserauthRequestPublicKey;
+import com.trilead.ssh2.packets.Packets;
+import com.trilead.ssh2.packets.TypesWriter;
+import com.trilead.ssh2.signature.DSASHA1Verify;
+import com.trilead.ssh2.signature.ECDSASHA2Verify;
+import com.trilead.ssh2.signature.RSASHA1Verify;
+import com.trilead.ssh2.transport.MessageHandler;
+import com.trilead.ssh2.transport.TransportManager;
+
+
+/**
+ * AuthenticationManager.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: AuthenticationManager.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
+ */
+public class AuthenticationManager implements MessageHandler
+{
+ TransportManager tm;
+
+ Vector packets = new Vector();
+ boolean connectionClosed = false;
+
+ String banner;
+
+ String[] remainingMethods = new String[0];
+ boolean isPartialSuccess = false;
+
+ boolean authenticated = false;
+ boolean initDone = false;
+
+ public AuthenticationManager(TransportManager tm)
+ {
+ this.tm = tm;
+ }
+
+ boolean methodPossible(String methName)
+ {
+ if (remainingMethods == null)
+ return false;
+
+ for (int i = 0; i < remainingMethods.length; i++)
+ {
+ if (remainingMethods[i].compareTo(methName) == 0)
+ return true;
+ }
+ return false;
+ }
+
+ byte[] deQueue() throws IOException
+ {
+ synchronized (packets)
+ {
+ while (packets.size() == 0)
+ {
+ if (connectionClosed)
+ throw (IOException) new IOException("The connection is closed.").initCause(tm
+ .getReasonClosedCause());
+
+ try
+ {
+ packets.wait();
+ }
+ catch (InterruptedException ign)
+ {
+ }
+ }
+ /* This sequence works with J2ME */
+ byte[] res = (byte[]) packets.firstElement();
+ packets.removeElementAt(0);
+ return res;
+ }
+ }
+
+ byte[] getNextMessage() throws IOException
+ {
+ while (true)
+ {
+ byte[] msg = deQueue();
+
+ if (msg[0] != Packets.SSH_MSG_USERAUTH_BANNER)
+ return msg;
+
+ PacketUserauthBanner sb = new PacketUserauthBanner(msg, 0, msg.length);
+
+ banner = sb.getBanner();
+ }
+ }
+
+ public String[] getRemainingMethods(String user) throws IOException
+ {
+ initialize(user);
+ return remainingMethods;
+ }
+
+ public boolean getPartialSuccess()
+ {
+ return isPartialSuccess;
+ }
+
+ private boolean initialize(String user) throws IOException
+ {
+ if (initDone == false)
+ {
+ tm.registerMessageHandler(this, 0, 255);
+
+ PacketServiceRequest sr = new PacketServiceRequest("ssh-userauth");
+ tm.sendMessage(sr.getPayload());
+
+ PacketUserauthRequestNone urn = new PacketUserauthRequestNone("ssh-connection", user);
+ tm.sendMessage(urn.getPayload());
+
+ byte[] msg = getNextMessage();
+ new PacketServiceAccept(msg, 0, msg.length);
+ msg = getNextMessage();
+
+ initDone = true;
+
+ if (msg[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
+ {
+ authenticated = true;
+ tm.removeMessageHandler(this, 0, 255);
+ return true;
+ }
+
+ if (msg[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
+ {
+ PacketUserauthFailure puf = new PacketUserauthFailure(msg, 0, msg.length);
+
+ remainingMethods = puf.getAuthThatCanContinue();
+ isPartialSuccess = puf.isPartialSuccess();
+ return false;
+ }
+
+ throw new IOException("Unexpected SSH message (type " + msg[0] + ")");
+ }
+ return authenticated;
+ }
+
+ public boolean authenticatePublicKey(String user, char[] PEMPrivateKey, String password, SecureRandom rnd)
+ throws IOException
+ {
+ KeyPair pair = PEMDecoder.decode(PEMPrivateKey, password);
+
+ return authenticatePublicKey(user, pair, rnd);
+ }
+
+ public boolean authenticatePublicKey(String user, KeyPair pair, SecureRandom rnd)
+ throws IOException
+ {
+ PrivateKey key = pair.getPrivate();
+
+ try
+ {
+ initialize(user);
+
+ if (methodPossible("publickey") == false)
+ throw new IOException("Authentication method publickey not supported by the server at this stage.");
+
+ if (key instanceof DSAPrivateKey)
+ {
+ DSAPrivateKey pk = (DSAPrivateKey) key;
+
+ byte[] pk_enc = DSASHA1Verify.encodeSSHDSAPublicKey((DSAPublicKey) pair.getPublic());
+
+ TypesWriter tw = new TypesWriter();
+
+ byte[] H = tm.getSessionIdentifier();
+
+ tw.writeString(H, 0, H.length);
+ tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
+ tw.writeString(user);
+ tw.writeString("ssh-connection");
+ tw.writeString("publickey");
+ tw.writeBoolean(true);
+ tw.writeString("ssh-dss");
+ tw.writeString(pk_enc, 0, pk_enc.length);
+
+ byte[] msg = tw.getBytes();
+
+ byte[] ds = DSASHA1Verify.generateSignature(msg, pk, rnd);
+
+ byte[] ds_enc = DSASHA1Verify.encodeSSHDSASignature(ds);
+
+ PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user,
+ "ssh-dss", pk_enc, ds_enc);
+ tm.sendMessage(ua.getPayload());
+ }
+ else if (key instanceof RSAPrivateKey)
+ {
+ RSAPrivateKey pk = (RSAPrivateKey) key;
+
+ byte[] pk_enc = RSASHA1Verify.encodeSSHRSAPublicKey((RSAPublicKey) pair.getPublic());
+
+ TypesWriter tw = new TypesWriter();
+ {
+ byte[] H = tm.getSessionIdentifier();
+
+ tw.writeString(H, 0, H.length);
+ tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
+ tw.writeString(user);
+ tw.writeString("ssh-connection");
+ tw.writeString("publickey");
+ tw.writeBoolean(true);
+ tw.writeString("ssh-rsa");
+ tw.writeString(pk_enc, 0, pk_enc.length);
+ }
+
+ byte[] msg = tw.getBytes();
+
+ byte[] ds = RSASHA1Verify.generateSignature(msg, pk);
+
+ byte[] rsa_sig_enc = RSASHA1Verify.encodeSSHRSASignature(ds);
+
+ PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user,
+ "ssh-rsa", pk_enc, rsa_sig_enc);
+
+ tm.sendMessage(ua.getPayload());
+ }
+ else if (key instanceof ECPrivateKey)
+ {
+ ECPrivateKey pk = (ECPrivateKey) key;
+ final String algo = ECDSASHA2Verify.ECDSA_SHA2_PREFIX
+ + ECDSASHA2Verify.getCurveName(pk.getParams());
+
+ byte[] pk_enc = ECDSASHA2Verify.encodeSSHECDSAPublicKey((ECPublicKey) pair.getPublic());
+
+ TypesWriter tw = new TypesWriter();
+ {
+ byte[] H = tm.getSessionIdentifier();
+
+ tw.writeString(H, 0, H.length);
+ tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
+ tw.writeString(user);
+ tw.writeString("ssh-connection");
+ tw.writeString("publickey");
+ tw.writeBoolean(true);
+ tw.writeString(algo);
+ tw.writeString(pk_enc, 0, pk_enc.length);
+ }
+
+ byte[] msg = tw.getBytes();
+
+ byte[] ds = ECDSASHA2Verify.generateSignature(msg, pk);
+
+ byte[] ec_sig_enc = ECDSASHA2Verify.encodeSSHECDSASignature(ds, pk.getParams());
+
+ PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user,
+ algo, pk_enc, ec_sig_enc);
+
+ tm.sendMessage(ua.getPayload());
+ }
+ else
+ {
+ throw new IOException("Unknown private key type returned by the PEM decoder.");
+ }
+
+ byte[] ar = getNextMessage();
+ if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
+ {
+ authenticated = true;
+ tm.removeMessageHandler(this, 0, 255);
+ return true;
+ }
+
+ if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
+ {
+ PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);
+
+ remainingMethods = puf.getAuthThatCanContinue();
+ isPartialSuccess = puf.isPartialSuccess();
+
+ return false;
+ }
+
+ throw new IOException("Unexpected SSH message (type " + ar[0] + ")");
+
+ }
+ catch (IOException e)
+ {
+e.printStackTrace();
+ tm.close(e, false);
+ throw (IOException) new IOException("Publickey authentication failed.").initCause(e);
+ }
+ }
+
+ public boolean authenticateNone(String user) throws IOException
+ {
+ try
+ {
+ initialize(user);
+ return authenticated;
+ }
+ catch (IOException e)
+ {
+ tm.close(e, false);
+ throw (IOException) new IOException("None authentication failed.").initCause(e);
+ }
+ }
+
+ public boolean authenticatePassword(String user, String pass) throws IOException
+ {
+ try
+ {
+ initialize(user);
+
+ if (methodPossible("password") == false)
+ throw new IOException("Authentication method password not supported by the server at this stage.");
+
+ PacketUserauthRequestPassword ua = new PacketUserauthRequestPassword("ssh-connection", user, pass);
+ tm.sendMessage(ua.getPayload());
+
+ byte[] ar = getNextMessage();
+
+ if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
+ {
+ authenticated = true;
+ tm.removeMessageHandler(this, 0, 255);
+ return true;
+ }
+
+ if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
+ {
+ PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);
+
+ remainingMethods = puf.getAuthThatCanContinue();
+ isPartialSuccess = puf.isPartialSuccess();
+
+ return false;
+ }
+
+ throw new IOException("Unexpected SSH message (type " + ar[0] + ")");
+
+ }
+ catch (IOException e)
+ {
+ tm.close(e, false);
+ throw (IOException) new IOException("Password authentication failed.").initCause(e);
+ }
+ }
+
+ public boolean authenticateInteractive(String user, String[] submethods, InteractiveCallback cb) throws IOException
+ {
+ try
+ {
+ initialize(user);
+
+ if (methodPossible("keyboard-interactive") == false)
+ throw new IOException(
+ "Authentication method keyboard-interactive not supported by the server at this stage.");
+
+ if (submethods == null)
+ submethods = new String[0];
+
+ PacketUserauthRequestInteractive ua = new PacketUserauthRequestInteractive("ssh-connection", user,
+ submethods);
+
+ tm.sendMessage(ua.getPayload());
+
+ while (true)
+ {
+ byte[] ar = getNextMessage();
+
+ if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
+ {
+ authenticated = true;
+ tm.removeMessageHandler(this, 0, 255);
+ return true;
+ }
+
+ if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
+ {
+ PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);
+
+ remainingMethods = puf.getAuthThatCanContinue();
+ isPartialSuccess = puf.isPartialSuccess();
+
+ return false;
+ }
+
+ if (ar[0] == Packets.SSH_MSG_USERAUTH_INFO_REQUEST)
+ {
+ PacketUserauthInfoRequest pui = new PacketUserauthInfoRequest(ar, 0, ar.length);
+
+ String[] responses;
+
+ try
+ {
+ responses = cb.replyToChallenge(pui.getName(), pui.getInstruction(), pui.getNumPrompts(), pui
+ .getPrompt(), pui.getEcho());
+ }
+ catch (Exception e)
+ {
+ throw (IOException) new IOException("Exception in callback.").initCause(e);
+ }
+
+ if (responses == null)
+ throw new IOException("Your callback may not return NULL!");
+
+ PacketUserauthInfoResponse puir = new PacketUserauthInfoResponse(responses);
+ tm.sendMessage(puir.getPayload());
+
+ continue;
+ }
+
+ throw new IOException("Unexpected SSH message (type " + ar[0] + ")");
+ }
+ }
+ catch (IOException e)
+ {
+ tm.close(e, false);
+ throw (IOException) new IOException("Keyboard-interactive authentication failed.").initCause(e);
+ }
+ }
+
+ public void handleMessage(byte[] msg, int msglen) throws IOException
+ {
+ synchronized (packets)
+ {
+ if (msg == null)
+ {
+ connectionClosed = true;
+ }
+ else
+ {
+ byte[] tmp = new byte[msglen];
+ System.arraycopy(msg, 0, tmp, 0, msglen);
+ packets.addElement(tmp);
+ }
+
+ packets.notifyAll();
+
+ if (packets.size() > 5)
+ {
+ connectionClosed = true;
+ throw new IOException("Error, peer is flooding us with authentication packets.");
+ }
+ }
+ }
+}
diff --git a/src/com/trilead/ssh2/channel/AuthAgentForwardThread.java b/src/com/trilead/ssh2/channel/AuthAgentForwardThread.java
index 57b9a5e..c6831e6 100644
--- a/src/com/trilead/ssh2/channel/AuthAgentForwardThread.java
+++ b/src/com/trilead/ssh2/channel/AuthAgentForwardThread.java
@@ -21,7 +21,24 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
import java.security.SecureRandom;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.spec.DSAPrivateKeySpec;
+import java.security.spec.DSAPublicKeySpec;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPrivateKeySpec;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.security.spec.RSAPrivateCrtKeySpec;
+import java.security.spec.RSAPublicKeySpec;
import java.util.Map;
import java.util.Map.Entry;
@@ -29,12 +46,9 @@ import com.trilead.ssh2.AuthAgentCallback;
import com.trilead.ssh2.log.Logger;
import com.trilead.ssh2.packets.TypesReader;
import com.trilead.ssh2.packets.TypesWriter;
-import com.trilead.ssh2.signature.DSAPrivateKey;
import com.trilead.ssh2.signature.DSASHA1Verify;
-import com.trilead.ssh2.signature.DSASignature;
-import com.trilead.ssh2.signature.RSAPrivateKey;
+import com.trilead.ssh2.signature.ECDSASHA2Verify;
import com.trilead.ssh2.signature.RSASHA1Verify;
-import com.trilead.ssh2.signature.RSASignature;
/**
* AuthAgentForwardThread.
@@ -268,20 +282,31 @@ public class AuthAgentForwardThread extends Thread implements IChannelWorkerThre
String type = tr.readString();
- Object key;
String comment;
+ String keyType;
+ KeySpec pubSpec;
+ KeySpec privSpec;
if (type.equals("ssh-rsa")) {
+ keyType = "RSA";
+
BigInteger n = tr.readMPINT();
BigInteger e = tr.readMPINT();
BigInteger d = tr.readMPINT();
- tr.readMPINT(); // iqmp
- tr.readMPINT(); // p
- tr.readMPINT(); // q
+ BigInteger iqmp = tr.readMPINT();
+ BigInteger p = tr.readMPINT();
+ BigInteger q = tr.readMPINT();
comment = tr.readString();
- key = new RSAPrivateKey(d, e, n);
+ // Derive the extra values Java needs.
+ BigInteger dmp1 = d.mod(p.subtract(BigInteger.ONE));
+ BigInteger dmq1 = d.mod(q.subtract(BigInteger.ONE));
+
+ pubSpec = new RSAPublicKeySpec(n, e);
+ privSpec = new RSAPrivateCrtKeySpec(n, e, d, p, q, dmp1, dmq1, iqmp);
} else if (type.equals("ssh-dss")) {
+ keyType = "DSA";
+
BigInteger p = tr.readMPINT();
BigInteger q = tr.readMPINT();
BigInteger g = tr.readMPINT();
@@ -289,12 +314,56 @@ public class AuthAgentForwardThread extends Thread implements IChannelWorkerThre
BigInteger x = tr.readMPINT();
comment = tr.readString();
- key = new DSAPrivateKey(p, q, g, y, x);
+ pubSpec = new DSAPublicKeySpec(y, p, q, g);
+ privSpec = new DSAPrivateKeySpec(x, p, q, g);
+ } else if (type.equals("ecdsa-sha2-nistp256")) {
+ keyType = "EC";
+
+ String curveName = tr.readString();
+ byte[] groupBytes = tr.readByteString();
+ BigInteger exponent = tr.readMPINT();
+ comment = tr.readString();
+
+ if (!"nistp256".equals(curveName)) {
+ log.log(2, "Invalid curve name for ecdsa-sha2-nistp256: " + curveName);
+ os.write(SSH_AGENT_FAILURE);
+ return;
+ }
+
+ ECParameterSpec nistp256 = ECDSASHA2Verify.EllipticCurves.nistp256;
+ ECPoint group = ECDSASHA2Verify.decodeECPoint(groupBytes, nistp256.getCurve());
+ if (group == null) {
+ // TODO log error
+ os.write(SSH_AGENT_FAILURE);
+ return;
+ }
+
+ pubSpec = new ECPublicKeySpec(group, nistp256);
+ privSpec = new ECPrivateKeySpec(exponent, nistp256);
} else {
+ log.log(2, "Unknown key type: " + type);
os.write(SSH_AGENT_FAILURE);
return;
}
+ PublicKey pubKey;
+ PrivateKey privKey;
+ try {
+ KeyFactory kf = KeyFactory.getInstance(keyType);
+ pubKey = kf.generatePublic(pubSpec);
+ privKey = kf.generatePrivate(privSpec);
+ } catch (NoSuchAlgorithmException ex) {
+ // TODO: log error
+ os.write(SSH_AGENT_FAILURE);
+ return;
+ } catch (InvalidKeySpecException ex) {
+ // TODO: log error
+ os.write(SSH_AGENT_FAILURE);
+ return;
+ }
+
+ KeyPair pair = new KeyPair(pubKey, privKey);
+
boolean confirmUse = false;
int lifetime = 0;
@@ -313,7 +382,7 @@ public class AuthAgentForwardThread extends Thread implements IChannelWorkerThre
}
}
- if (authAgent.addIdentity(key, comment, confirmUse, lifetime))
+ if (authAgent.addIdentity(pair, comment, confirmUse, lifetime))
os.write(SSH_AGENT_SUCCESS);
else
os.write(SSH_AGENT_FAILURE);
@@ -390,7 +459,7 @@ public class AuthAgentForwardThread extends Thread implements IChannelWorkerThre
if (failWhenLocked())
return;
- byte[] publicKey = tr.readByteString();
+ byte[] publicKeyBytes = tr.readByteString();
byte[] challenge = tr.readByteString();
int flags = tr.readUINT32();
@@ -401,22 +470,23 @@ public class AuthAgentForwardThread extends Thread implements IChannelWorkerThre
return;
}
- Object trileadKey = authAgent.getPrivateKey(publicKey);
+ KeyPair pair = authAgent.getKeyPair(publicKeyBytes);
- if (trileadKey == null) {
+ if (pair == null) {
os.write(SSH_AGENT_FAILURE);
return;
}
byte[] response;
- if (trileadKey instanceof RSAPrivateKey) {
- RSASignature signature = RSASHA1Verify.generateSignature(challenge,
- (RSAPrivateKey) trileadKey);
+ PrivateKey privKey = pair.getPrivate();
+ if (privKey instanceof RSAPrivateKey) {
+ byte[] signature = RSASHA1Verify.generateSignature(challenge,
+ (RSAPrivateKey) privKey);
response = RSASHA1Verify.encodeSSHRSASignature(signature);
- } else if (trileadKey instanceof DSAPrivateKey) {
- DSASignature signature = DSASHA1Verify.generateSignature(challenge,
- (DSAPrivateKey) trileadKey, new SecureRandom());
+ } else if (privKey instanceof DSAPrivateKey) {
+ byte[] signature = DSASHA1Verify.generateSignature(challenge,
+ (DSAPrivateKey) privKey, new SecureRandom());
response = DSASHA1Verify.encodeSSHDSASignature(signature);
} else {
os.write(SSH_AGENT_FAILURE);
diff --git a/src/com/trilead/ssh2/channel/Channel.java b/src/com/trilead/ssh2/channel/Channel.java
index 4b19a48..8365f12 100644
--- a/src/com/trilead/ssh2/channel/Channel.java
+++ b/src/com/trilead/ssh2/channel/Channel.java
@@ -1,207 +1,207 @@
-
-package com.trilead.ssh2.channel;
-
-/**
- * Channel.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: Channel.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public class Channel
-{
- /*
- * OK. Here is an important part of the JVM Specification:
- * (http://java.sun.com/docs/books/vmspec/2nd-edition/html/Threads.doc.html#22214)
- *
- * Any association between locks and variables is purely conventional.
- * Locking any lock conceptually flushes all variables from a thread's
- * working memory, and unlocking any lock forces the writing out to main
- * memory of all variables that the thread has assigned. That a lock may be
- * associated with a particular object or a class is purely a convention.
- * (...)
- *
- * If a thread uses a particular shared variable only after locking a
- * particular lock and before the corresponding unlocking of that same lock,
- * then the thread will read the shared value of that variable from main
- * memory after the lock operation, if necessary, and will copy back to main
- * memory the value most recently assigned to that variable before the
- * unlock operation.
- *
- * This, in conjunction with the mutual exclusion rules for locks, suffices
- * to guarantee that values are correctly transmitted from one thread to
- * another through shared variables.
- *
- * ====> Always keep that in mind when modifying the Channel/ChannelManger
- * code.
- *
- */
-
- static final int STATE_OPENING = 1;
- static final int STATE_OPEN = 2;
- static final int STATE_CLOSED = 4;
-
- static final int CHANNEL_BUFFER_SIZE = 30000;
-
- /*
- * To achieve correctness, the following rules have to be respected when
- * accessing this object:
- */
-
- // These fields can always be read
- final ChannelManager cm;
- final ChannelOutputStream stdinStream;
- final ChannelInputStream stdoutStream;
- final ChannelInputStream stderrStream;
-
- // These two fields will only be written while the Channel is in state
- // STATE_OPENING.
- // The code makes sure that the two fields are written out when the state is
- // changing to STATE_OPEN.
- // Therefore, if you know that the Channel is in state STATE_OPEN, then you
- // can read these two fields without synchronizing on the Channel. However, make
- // sure that you get the latest values (e.g., flush caches by synchronizing on any
- // object). However, to be on the safe side, you can lock the channel.
-
- int localID = -1;
- int remoteID = -1;
-
- /*
- * Make sure that we never send a data/EOF/WindowChange msg after a CLOSE
- * msg.
- *
- * This is a little bit complicated, but we have to do it in that way, since
- * we cannot keep a lock on the Channel during the send operation (this
- * would block sometimes the receiver thread, and, in extreme cases, can
- * lead to a deadlock on both sides of the connection (senders are blocked
- * since the receive buffers on the other side are full, and receiver
- * threads wait for the senders to finish). It all depends on the
- * implementation on the other side. But we cannot make any assumptions, we
- * have to assume the worst case. Confused? Just believe me.
- */
-
- /*
- * If you send a message on a channel, then you have to aquire the
- * "channelSendLock" and check the "closeMessageSent" flag (this variable
- * may only be accessed while holding the "channelSendLock" !!!
- *
- * BTW: NEVER EVER SEND MESSAGES FROM THE RECEIVE THREAD - see explanation
- * above.
- */
-
- final Object channelSendLock = new Object();
- boolean closeMessageSent = false;
-
- /*
- * Stop memory fragmentation by allocating this often used buffer.
- * May only be used while holding the channelSendLock
- */
-
- final byte[] msgWindowAdjust = new byte[9];
-
- // If you access (read or write) any of the following fields, then you have
- // to synchronize on the channel.
-
- int state = STATE_OPENING;
-
- boolean closeMessageRecv = false;
-
- /* This is a stupid implementation. At the moment we can only wait
- * for one pending request per channel.
- */
- int successCounter = 0;
- int failedCounter = 0;
-
- int localWindow = 0; /* locally, we use a small window, < 2^31 */
- long remoteWindow = 0; /* long for readable 2^32 - 1 window support */
-
- int localMaxPacketSize = -1;
- int remoteMaxPacketSize = -1;
-
- final byte[] stdoutBuffer = new byte[CHANNEL_BUFFER_SIZE];
- final byte[] stderrBuffer = new byte[CHANNEL_BUFFER_SIZE];
-
- int stdoutReadpos = 0;
- int stdoutWritepos = 0;
- int stderrReadpos = 0;
- int stderrWritepos = 0;
-
- boolean EOF = false;
-
- Integer exit_status;
-
- String exit_signal;
-
- // we keep the x11 cookie so that this channel can be closed when this
- // specific x11 forwarding gets stopped
-
- String hexX11FakeCookie;
-
- // reasonClosed is special, since we sometimes need to access it
- // while holding the channelSendLock.
- // We protect it with a private short term lock.
-
- private final Object reasonClosedLock = new Object();
- private String reasonClosed = null;
-
- public Channel(ChannelManager cm)
- {
- this.cm = cm;
-
- this.localWindow = CHANNEL_BUFFER_SIZE;
- this.localMaxPacketSize = 35000 - 1024; // leave enough slack
-
- this.stdinStream = new ChannelOutputStream(this);
- this.stdoutStream = new ChannelInputStream(this, false);
- this.stderrStream = new ChannelInputStream(this, true);
- }
-
- /* Methods to allow access from classes outside of this package */
-
- public ChannelInputStream getStderrStream()
- {
- return stderrStream;
- }
-
- public ChannelOutputStream getStdinStream()
- {
- return stdinStream;
- }
-
- public ChannelInputStream getStdoutStream()
- {
- return stdoutStream;
- }
-
- public String getExitSignal()
- {
- synchronized (this)
- {
- return exit_signal;
- }
- }
-
- public Integer getExitStatus()
- {
- synchronized (this)
- {
- return exit_status;
- }
- }
-
- public String getReasonClosed()
- {
- synchronized (reasonClosedLock)
- {
- return reasonClosed;
- }
- }
-
- public void setReasonClosed(String reasonClosed)
- {
- synchronized (reasonClosedLock)
- {
- if (this.reasonClosed == null)
- this.reasonClosed = reasonClosed;
- }
- }
-}
+
+package com.trilead.ssh2.channel;
+
+/**
+ * Channel.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: Channel.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+public class Channel
+{
+ /*
+ * OK. Here is an important part of the JVM Specification:
+ * (http://java.sun.com/docs/books/vmspec/2nd-edition/html/Threads.doc.html#22214)
+ *
+ * Any association between locks and variables is purely conventional.
+ * Locking any lock conceptually flushes all variables from a thread's
+ * working memory, and unlocking any lock forces the writing out to main
+ * memory of all variables that the thread has assigned. That a lock may be
+ * associated with a particular object or a class is purely a convention.
+ * (...)
+ *
+ * If a thread uses a particular shared variable only after locking a
+ * particular lock and before the corresponding unlocking of that same lock,
+ * then the thread will read the shared value of that variable from main
+ * memory after the lock operation, if necessary, and will copy back to main
+ * memory the value most recently assigned to that variable before the
+ * unlock operation.
+ *
+ * This, in conjunction with the mutual exclusion rules for locks, suffices
+ * to guarantee that values are correctly transmitted from one thread to
+ * another through shared variables.
+ *
+ * ====> Always keep that in mind when modifying the Channel/ChannelManger
+ * code.
+ *
+ */
+
+ static final int STATE_OPENING = 1;
+ static final int STATE_OPEN = 2;
+ static final int STATE_CLOSED = 4;
+
+ static final int CHANNEL_BUFFER_SIZE = 30000;
+
+ /*
+ * To achieve correctness, the following rules have to be respected when
+ * accessing this object:
+ */
+
+ // These fields can always be read
+ final ChannelManager cm;
+ final ChannelOutputStream stdinStream;
+ final ChannelInputStream stdoutStream;
+ final ChannelInputStream stderrStream;
+
+ // These two fields will only be written while the Channel is in state
+ // STATE_OPENING.
+ // The code makes sure that the two fields are written out when the state is
+ // changing to STATE_OPEN.
+ // Therefore, if you know that the Channel is in state STATE_OPEN, then you
+ // can read these two fields without synchronizing on the Channel. However, make
+ // sure that you get the latest values (e.g., flush caches by synchronizing on any
+ // object). However, to be on the safe side, you can lock the channel.
+
+ int localID = -1;
+ int remoteID = -1;
+
+ /*
+ * Make sure that we never send a data/EOF/WindowChange msg after a CLOSE
+ * msg.
+ *
+ * This is a little bit complicated, but we have to do it in that way, since
+ * we cannot keep a lock on the Channel during the send operation (this
+ * would block sometimes the receiver thread, and, in extreme cases, can
+ * lead to a deadlock on both sides of the connection (senders are blocked
+ * since the receive buffers on the other side are full, and receiver
+ * threads wait for the senders to finish). It all depends on the
+ * implementation on the other side. But we cannot make any assumptions, we
+ * have to assume the worst case. Confused? Just believe me.
+ */
+
+ /*
+ * If you send a message on a channel, then you have to aquire the
+ * "channelSendLock" and check the "closeMessageSent" flag (this variable
+ * may only be accessed while holding the "channelSendLock" !!!
+ *
+ * BTW: NEVER EVER SEND MESSAGES FROM THE RECEIVE THREAD - see explanation
+ * above.
+ */
+
+ final Object channelSendLock = new Object();
+ boolean closeMessageSent = false;
+
+ /*
+ * Stop memory fragmentation by allocating this often used buffer.
+ * May only be used while holding the channelSendLock
+ */
+
+ final byte[] msgWindowAdjust = new byte[9];
+
+ // If you access (read or write) any of the following fields, then you have
+ // to synchronize on the channel.
+
+ int state = STATE_OPENING;
+
+ boolean closeMessageRecv = false;
+
+ /* This is a stupid implementation. At the moment we can only wait
+ * for one pending request per channel.
+ */
+ int successCounter = 0;
+ int failedCounter = 0;
+
+ int localWindow = 0; /* locally, we use a small window, < 2^31 */
+ long remoteWindow = 0; /* long for readable 2^32 - 1 window support */
+
+ int localMaxPacketSize = -1;
+ int remoteMaxPacketSize = -1;
+
+ final byte[] stdoutBuffer = new byte[CHANNEL_BUFFER_SIZE];
+ final byte[] stderrBuffer = new byte[CHANNEL_BUFFER_SIZE];
+
+ int stdoutReadpos = 0;
+ int stdoutWritepos = 0;
+ int stderrReadpos = 0;
+ int stderrWritepos = 0;
+
+ boolean EOF = false;
+
+ Integer exit_status;
+
+ String exit_signal;
+
+ // we keep the x11 cookie so that this channel can be closed when this
+ // specific x11 forwarding gets stopped
+
+ String hexX11FakeCookie;
+
+ // reasonClosed is special, since we sometimes need to access it
+ // while holding the channelSendLock.
+ // We protect it with a private short term lock.
+
+ private final Object reasonClosedLock = new Object();
+ private String reasonClosed = null;
+
+ public Channel(ChannelManager cm)
+ {
+ this.cm = cm;
+
+ this.localWindow = CHANNEL_BUFFER_SIZE;
+ this.localMaxPacketSize = 35000 - 1024; // leave enough slack
+
+ this.stdinStream = new ChannelOutputStream(this);
+ this.stdoutStream = new ChannelInputStream(this, false);
+ this.stderrStream = new ChannelInputStream(this, true);
+ }
+
+ /* Methods to allow access from classes outside of this package */
+
+ public ChannelInputStream getStderrStream()
+ {
+ return stderrStream;
+ }
+
+ public ChannelOutputStream getStdinStream()
+ {
+ return stdinStream;
+ }
+
+ public ChannelInputStream getStdoutStream()
+ {
+ return stdoutStream;
+ }
+
+ public String getExitSignal()
+ {
+ synchronized (this)
+ {
+ return exit_signal;
+ }
+ }
+
+ public Integer getExitStatus()
+ {
+ synchronized (this)
+ {
+ return exit_status;
+ }
+ }
+
+ public String getReasonClosed()
+ {
+ synchronized (reasonClosedLock)
+ {
+ return reasonClosed;
+ }
+ }
+
+ public void setReasonClosed(String reasonClosed)
+ {
+ synchronized (reasonClosedLock)
+ {
+ if (this.reasonClosed == null)
+ this.reasonClosed = reasonClosed;
+ }
+ }
+}
diff --git a/src/com/trilead/ssh2/channel/ChannelInputStream.java b/src/com/trilead/ssh2/channel/ChannelInputStream.java
index a6d936f..f88522c 100644
--- a/src/com/trilead/ssh2/channel/ChannelInputStream.java
+++ b/src/com/trilead/ssh2/channel/ChannelInputStream.java
@@ -1,86 +1,86 @@
-
-package com.trilead.ssh2.channel;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * ChannelInputStream.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: ChannelInputStream.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public final class ChannelInputStream extends InputStream
-{
- Channel c;
-
- boolean isClosed = false;
- boolean isEOF = false;
- boolean extendedFlag = false;
-
- ChannelInputStream(Channel c, boolean isExtended)
- {
- this.c = c;
- this.extendedFlag = isExtended;
- }
-
- public int available() throws IOException
- {
- if (isEOF)
- return 0;
-
- int avail = c.cm.getAvailable(c, extendedFlag);
-
- /* We must not return -1 on EOF */
-
- return (avail > 0) ? avail : 0;
- }
-
- public void close() throws IOException
- {
- isClosed = true;
- }
-
- public int read(byte[] b, int off, int len) throws IOException
- {
- if (b == null)
- throw new NullPointerException();
-
- if ((off < 0) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0) || (off > b.length))
- throw new IndexOutOfBoundsException();
-
- if (len == 0)
- return 0;
-
- if (isEOF)
- return -1;
-
- int ret = c.cm.getChannelData(c, extendedFlag, b, off, len);
-
- if (ret == -1)
- {
- isEOF = true;
- }
-
- return ret;
- }
-
- public int read(byte[] b) throws IOException
- {
- return read(b, 0, b.length);
- }
-
- public int read() throws IOException
- {
- /* Yes, this stream is pure and unbuffered, a single byte read() is slow */
-
- final byte b[] = new byte[1];
-
- int ret = read(b, 0, 1);
-
- if (ret != 1)
- return -1;
-
- return b[0] & 0xff;
- }
-}
+
+package com.trilead.ssh2.channel;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * ChannelInputStream.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: ChannelInputStream.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+public final class ChannelInputStream extends InputStream
+{
+ Channel c;
+
+ boolean isClosed = false;
+ boolean isEOF = false;
+ boolean extendedFlag = false;
+
+ ChannelInputStream(Channel c, boolean isExtended)
+ {
+ this.c = c;
+ this.extendedFlag = isExtended;
+ }
+
+ public int available() throws IOException
+ {
+ if (isEOF)
+ return 0;
+
+ int avail = c.cm.getAvailable(c, extendedFlag);
+
+ /* We must not return -1 on EOF */
+
+ return (avail > 0) ? avail : 0;
+ }
+
+ public void close() throws IOException
+ {
+ isClosed = true;
+ }
+
+ public int read(byte[] b, int off, int len) throws IOException
+ {
+ if (b == null)
+ throw new NullPointerException();
+
+ if ((off < 0) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0) || (off > b.length))
+ throw new IndexOutOfBoundsException();
+
+ if (len == 0)
+ return 0;
+
+ if (isEOF)
+ return -1;
+
+ int ret = c.cm.getChannelData(c, extendedFlag, b, off, len);
+
+ if (ret == -1)
+ {
+ isEOF = true;
+ }
+
+ return ret;
+ }
+
+ public int read(byte[] b) throws IOException
+ {
+ return read(b, 0, b.length);
+ }
+
+ public int read() throws IOException
+ {
+ /* Yes, this stream is pure and unbuffered, a single byte read() is slow */
+
+ final byte b[] = new byte[1];
+
+ int ret = read(b, 0, 1);
+
+ if (ret != 1)
+ return -1;
+
+ return b[0] & 0xff;
+ }
+}
diff --git a/src/com/trilead/ssh2/channel/ChannelManager.java b/src/com/trilead/ssh2/channel/ChannelManager.java
index 630e0cc..88beffd 100644
--- a/src/com/trilead/ssh2/channel/ChannelManager.java
+++ b/src/com/trilead/ssh2/channel/ChannelManager.java
@@ -1,1756 +1,1756 @@
-
-package com.trilead.ssh2.channel;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Vector;
-
-import com.trilead.ssh2.AuthAgentCallback;
-import com.trilead.ssh2.ChannelCondition;
-import com.trilead.ssh2.log.Logger;
-import com.trilead.ssh2.packets.PacketChannelAuthAgentReq;
-import com.trilead.ssh2.packets.PacketChannelOpenConfirmation;
-import com.trilead.ssh2.packets.PacketChannelOpenFailure;
-import com.trilead.ssh2.packets.PacketChannelTrileadPing;
-import com.trilead.ssh2.packets.PacketGlobalCancelForwardRequest;
-import com.trilead.ssh2.packets.PacketGlobalForwardRequest;
-import com.trilead.ssh2.packets.PacketGlobalTrileadPing;
-import com.trilead.ssh2.packets.PacketOpenDirectTCPIPChannel;
-import com.trilead.ssh2.packets.PacketOpenSessionChannel;
-import com.trilead.ssh2.packets.PacketSessionExecCommand;
-import com.trilead.ssh2.packets.PacketSessionPtyRequest;
-import com.trilead.ssh2.packets.PacketSessionPtyResize;
-import com.trilead.ssh2.packets.PacketSessionStartShell;
-import com.trilead.ssh2.packets.PacketSessionSubsystemRequest;
-import com.trilead.ssh2.packets.PacketSessionX11Request;
-import com.trilead.ssh2.packets.Packets;
-import com.trilead.ssh2.packets.TypesReader;
-import com.trilead.ssh2.transport.MessageHandler;
-import com.trilead.ssh2.transport.TransportManager;
-
-/**
- * ChannelManager. Please read the comments in Channel.java.
- * <p>
- * Besides the crypto part, this is the core of the library.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: ChannelManager.java,v 1.2 2008/03/03 07:01:36 cplattne Exp $
- */
-public class ChannelManager implements MessageHandler
-{
- private static final Logger log = Logger.getLogger(ChannelManager.class);
-
- private HashMap x11_magic_cookies = new HashMap();
-
- private TransportManager tm;
-
- private Vector channels = new Vector();
- private int nextLocalChannel = 100;
- private boolean shutdown = false;
- private int globalSuccessCounter = 0;
- private int globalFailedCounter = 0;
-
- private HashMap remoteForwardings = new HashMap();
-
- private AuthAgentCallback authAgent;
-
- private Vector listenerThreads = new Vector();
-
- private boolean listenerThreadsAllowed = true;
-
- public ChannelManager(TransportManager tm)
- {
- this.tm = tm;
- tm.registerMessageHandler(this, 80, 100);
- }
-
- private Channel getChannel(int id)
- {
- synchronized (channels)
- {
- for (int i = 0; i < channels.size(); i++)
- {
- Channel c = (Channel) channels.elementAt(i);
- if (c.localID == id)
- return c;
- }
- }
- return null;
- }
-
- private void removeChannel(int id)
- {
- synchronized (channels)
- {
- for (int i = 0; i < channels.size(); i++)
- {
- Channel c = (Channel) channels.elementAt(i);
- if (c.localID == id)
- {
- channels.removeElementAt(i);
- break;
- }
- }
- }
- }
-
- private int addChannel(Channel c)
- {
- synchronized (channels)
- {
- channels.addElement(c);
- return nextLocalChannel++;
- }
- }
-
- private void waitUntilChannelOpen(Channel c) throws IOException
- {
- synchronized (c)
- {
- while (c.state == Channel.STATE_OPENING)
- {
- try
- {
- c.wait();
- }
- catch (InterruptedException ignore)
- {
- }
- }
-
- if (c.state != Channel.STATE_OPEN)
- {
- removeChannel(c.localID);
-
- String detail = c.getReasonClosed();
-
- if (detail == null)
- detail = "state: " + c.state;
-
- throw new IOException("Could not open channel (" + detail + ")");
- }
- }
- }
-
- private final boolean waitForGlobalRequestResult() throws IOException
- {
- synchronized (channels)
- {
- while ((globalSuccessCounter == 0) && (globalFailedCounter == 0))
- {
- if (shutdown)
- {
- throw new IOException("The connection is being shutdown");
- }
-
- try
- {
- channels.wait();
- }
- catch (InterruptedException ignore)
- {
- }
- }
-
- if ((globalFailedCounter == 0) && (globalSuccessCounter == 1))
- return true;
-
- if ((globalFailedCounter == 1) && (globalSuccessCounter == 0))
- return false;
-
- throw new IOException("Illegal state. The server sent " + globalSuccessCounter
- + " SSH_MSG_REQUEST_SUCCESS and " + globalFailedCounter + " SSH_MSG_REQUEST_FAILURE messages.");
- }
- }
-
- private final boolean waitForChannelRequestResult(Channel c) throws IOException
- {
- synchronized (c)
- {
- while ((c.successCounter == 0) && (c.failedCounter == 0))
- {
- if (c.state != Channel.STATE_OPEN)
- {
- String detail = c.getReasonClosed();
-
- if (detail == null)
- detail = "state: " + c.state;
-
- throw new IOException("This SSH2 channel is not open (" + detail + ")");
- }
-
- try
- {
- c.wait();
- }
- catch (InterruptedException ignore)
- {
- }
- }
-
- if ((c.failedCounter == 0) && (c.successCounter == 1))
- return true;
-
- if ((c.failedCounter == 1) && (c.successCounter == 0))
- return false;
-
- throw new IOException("Illegal state. The server sent " + c.successCounter
- + " SSH_MSG_CHANNEL_SUCCESS and " + c.failedCounter + " SSH_MSG_CHANNEL_FAILURE messages.");
- }
- }
-
- public void registerX11Cookie(String hexFakeCookie, X11ServerData data)
- {
- synchronized (x11_magic_cookies)
- {
- x11_magic_cookies.put(hexFakeCookie, data);
- }
- }
-
- public void unRegisterX11Cookie(String hexFakeCookie, boolean killChannels)
- {
- if (hexFakeCookie == null)
- throw new IllegalStateException("hexFakeCookie may not be null");
-
- synchronized (x11_magic_cookies)
- {
- x11_magic_cookies.remove(hexFakeCookie);
- }
-
- if (killChannels == false)
- return;
-
- if (log.isEnabled())
- log.log(50, "Closing all X11 channels for the given fake cookie");
-
- Vector channel_copy;
-
- synchronized (channels)
- {
- channel_copy = (Vector) channels.clone();
- }
-
- for (int i = 0; i < channel_copy.size(); i++)
- {
- Channel c = (Channel) channel_copy.elementAt(i);
-
- synchronized (c)
- {
- if (hexFakeCookie.equals(c.hexX11FakeCookie) == false)
- continue;
- }
-
- try
- {
- closeChannel(c, "Closing X11 channel since the corresponding session is closing", true);
- }
- catch (IOException e)
- {
- }
- }
- }
-
- public X11ServerData checkX11Cookie(String hexFakeCookie)
- {
- synchronized (x11_magic_cookies)
- {
- if (hexFakeCookie != null)
- return (X11ServerData) x11_magic_cookies.get(hexFakeCookie);
- }
- return null;
- }
-
- public void closeAllChannels()
- {
- if (log.isEnabled())
- log.log(50, "Closing all channels");
-
- Vector channel_copy;
-
- synchronized (channels)
- {
- channel_copy = (Vector) channels.clone();
- }
-
- for (int i = 0; i < channel_copy.size(); i++)
- {
- Channel c = (Channel) channel_copy.elementAt(i);
- try
- {
- closeChannel(c, "Closing all channels", true);
- }
- catch (IOException e)
- {
- }
- }
- }
-
- public void closeChannel(Channel c, String reason, boolean force) throws IOException
- {
- byte msg[] = new byte[5];
-
- synchronized (c)
- {
- if (force)
- {
- c.state = Channel.STATE_CLOSED;
- c.EOF = true;
- }
-
- c.setReasonClosed(reason);
-
- msg[0] = Packets.SSH_MSG_CHANNEL_CLOSE;
- msg[1] = (byte) (c.remoteID >> 24);
- msg[2] = (byte) (c.remoteID >> 16);
- msg[3] = (byte) (c.remoteID >> 8);
- msg[4] = (byte) (c.remoteID);
-
- c.notifyAll();
- }
-
- synchronized (c.channelSendLock)
- {
- if (c.closeMessageSent == true)
- return;
- tm.sendMessage(msg);
- c.closeMessageSent = true;
- }
-
- if (log.isEnabled())
- log.log(50, "Sent SSH_MSG_CHANNEL_CLOSE (channel " + c.localID + ")");
- }
-
- public void sendEOF(Channel c) throws IOException
- {
- byte[] msg = new byte[5];
-
- synchronized (c)
- {
- if (c.state != Channel.STATE_OPEN)
- return;
-
- msg[0] = Packets.SSH_MSG_CHANNEL_EOF;
- msg[1] = (byte) (c.remoteID >> 24);
- msg[2] = (byte) (c.remoteID >> 16);
- msg[3] = (byte) (c.remoteID >> 8);
- msg[4] = (byte) (c.remoteID);
- }
-
- synchronized (c.channelSendLock)
- {
- if (c.closeMessageSent == true)
- return;
- tm.sendMessage(msg);
- }
-
- if (log.isEnabled())
- log.log(50, "Sent EOF (Channel " + c.localID + "/" + c.remoteID + ")");
- }
-
- public void sendOpenConfirmation(Channel c) throws IOException
- {
- PacketChannelOpenConfirmation pcoc = null;
-
- synchronized (c)
- {
- if (c.state != Channel.STATE_OPENING)
- return;
-
- c.state = Channel.STATE_OPEN;
-
- pcoc = new PacketChannelOpenConfirmation(c.remoteID, c.localID, c.localWindow, c.localMaxPacketSize);
- }
-
- synchronized (c.channelSendLock)
- {
- if (c.closeMessageSent == true)
- return;
- tm.sendMessage(pcoc.getPayload());
- }
- }
-
- public void sendData(Channel c, byte[] buffer, int pos, int len) throws IOException
- {
- while (len > 0)
- {
- int thislen = 0;
- byte[] msg;
-
- synchronized (c)
- {
- while (true)
- {
- if (c.state == Channel.STATE_CLOSED)
- throw new IOException("SSH channel is closed. (" + c.getReasonClosed() + ")");
-
- if (c.state != Channel.STATE_OPEN)
- throw new IOException("SSH channel in strange state. (" + c.state + ")");
-
- if (c.remoteWindow != 0)
- break;
-
- try
- {
- c.wait();
- }
- catch (InterruptedException ignore)
- {
- }
- }
-
- /* len > 0, no sign extension can happen when comparing */
-
- thislen = (c.remoteWindow >= len) ? len : (int) c.remoteWindow;
-
- int estimatedMaxDataLen = c.remoteMaxPacketSize - (tm.getPacketOverheadEstimate() + 9);
-
- /* The worst case scenario =) a true bottleneck */
-
- if (estimatedMaxDataLen <= 0)
- {
- estimatedMaxDataLen = 1;
- }
-
- if (thislen > estimatedMaxDataLen)
- thislen = estimatedMaxDataLen;
-
- c.remoteWindow -= thislen;
-
- msg = new byte[1 + 8 + thislen];
-
- msg[0] = Packets.SSH_MSG_CHANNEL_DATA;
- msg[1] = (byte) (c.remoteID >> 24);
- msg[2] = (byte) (c.remoteID >> 16);
- msg[3] = (byte) (c.remoteID >> 8);
- msg[4] = (byte) (c.remoteID);
- msg[5] = (byte) (thislen >> 24);
- msg[6] = (byte) (thislen >> 16);
- msg[7] = (byte) (thislen >> 8);
- msg[8] = (byte) (thislen);
-
- System.arraycopy(buffer, pos, msg, 9, thislen);
- }
-
- synchronized (c.channelSendLock)
- {
- if (c.closeMessageSent == true)
- throw new IOException("SSH channel is closed. (" + c.getReasonClosed() + ")");
-
- tm.sendMessage(msg);
- }
-
- pos += thislen;
- len -= thislen;
- }
- }
-
- public int requestGlobalForward(String bindAddress, int bindPort, String targetAddress, int targetPort)
- throws IOException
- {
- RemoteForwardingData rfd = new RemoteForwardingData();
-
- rfd.bindAddress = bindAddress;
- rfd.bindPort = bindPort;
- rfd.targetAddress = targetAddress;
- rfd.targetPort = targetPort;
-
- synchronized (remoteForwardings)
- {
- Integer key = new Integer(bindPort);
-
- if (remoteForwardings.get(key) != null)
- {
- throw new IOException("There is already a forwarding for remote port " + bindPort);
- }
-
- remoteForwardings.put(key, rfd);
- }
-
- synchronized (channels)
- {
- globalSuccessCounter = globalFailedCounter = 0;
- }
-
- PacketGlobalForwardRequest pgf = new PacketGlobalForwardRequest(true, bindAddress, bindPort);
- tm.sendMessage(pgf.getPayload());
-
- if (log.isEnabled())
- log.log(50, "Requesting a remote forwarding ('" + bindAddress + "', " + bindPort + ")");
-
- try
- {
- if (waitForGlobalRequestResult() == false)
- throw new IOException("The server denied the request (did you enable port forwarding?)");
- }
- catch (IOException e)
- {
- synchronized (remoteForwardings)
- {
- remoteForwardings.remove(rfd);
- }
- throw e;
- }
-
- return bindPort;
- }
-
- public void requestCancelGlobalForward(int bindPort) throws IOException
- {
- RemoteForwardingData rfd = null;
-
- synchronized (remoteForwardings)
- {
- rfd = (RemoteForwardingData) remoteForwardings.get(new Integer(bindPort));
-
- if (rfd == null)
- throw new IOException("Sorry, there is no known remote forwarding for remote port " + bindPort);
- }
-
- synchronized (channels)
- {
- globalSuccessCounter = globalFailedCounter = 0;
- }
-
- PacketGlobalCancelForwardRequest pgcf = new PacketGlobalCancelForwardRequest(true, rfd.bindAddress,
- rfd.bindPort);
- tm.sendMessage(pgcf.getPayload());
-
- if (log.isEnabled())
- log.log(50, "Requesting cancelation of remote forward ('" + rfd.bindAddress + "', " + rfd.bindPort + ")");
-
- try
- {
- if (waitForGlobalRequestResult() == false)
- throw new IOException("The server denied the request.");
- }
- finally
- {
- synchronized (remoteForwardings)
- {
- /* Only now we are sure that no more forwarded connections will arrive */
- remoteForwardings.remove(rfd);
- }
- }
-
- }
-
- /**
- * @param agent
- * @throws IOException
- */
- public boolean requestChannelAgentForwarding(Channel c, AuthAgentCallback authAgent) throws IOException {
- synchronized (this)
- {
- if (this.authAgent != null)
- throw new IllegalStateException("Auth agent already exists");
-
- this.authAgent = authAgent;
- }
-
- synchronized (channels)
- {
- globalSuccessCounter = globalFailedCounter = 0;
- }
-
- if (log.isEnabled())
- log.log(50, "Requesting agent forwarding");
-
- PacketChannelAuthAgentReq aar = new PacketChannelAuthAgentReq(c.remoteID);
- tm.sendMessage(aar.getPayload());
-
- if (waitForChannelRequestResult(c) == false) {
- authAgent = null;
- return false;
- }
-
- return true;
- }
-
- public void registerThread(IChannelWorkerThread thr) throws IOException
- {
- synchronized (listenerThreads)
- {
- if (listenerThreadsAllowed == false)
- throw new IOException("Too late, this connection is closed.");
- listenerThreads.addElement(thr);
- }
- }
-
- public Channel openDirectTCPIPChannel(String host_to_connect, int port_to_connect, String originator_IP_address,
- int originator_port) throws IOException
- {
- Channel c = new Channel(this);
-
- synchronized (c)
- {
- c.localID = addChannel(c);
- // end of synchronized block forces writing out to main memory
- }
-
- PacketOpenDirectTCPIPChannel dtc = new PacketOpenDirectTCPIPChannel(c.localID, c.localWindow,
- c.localMaxPacketSize, host_to_connect, port_to_connect, originator_IP_address, originator_port);
-
- tm.sendMessage(dtc.getPayload());
-
- waitUntilChannelOpen(c);
-
- return c;
- }
-
- public Channel openSessionChannel() throws IOException
- {
- Channel c = new Channel(this);
-
- synchronized (c)
- {
- c.localID = addChannel(c);
- // end of synchronized block forces the writing out to main memory
- }
-
- if (log.isEnabled())
- log.log(50, "Sending SSH_MSG_CHANNEL_OPEN (Channel " + c.localID + ")");
-
- PacketOpenSessionChannel smo = new PacketOpenSessionChannel(c.localID, c.localWindow, c.localMaxPacketSize);
- tm.sendMessage(smo.getPayload());
-
- waitUntilChannelOpen(c);
-
- return c;
- }
-
- public void requestGlobalTrileadPing() throws IOException
- {
- synchronized (channels)
- {
- globalSuccessCounter = globalFailedCounter = 0;
- }
-
- PacketGlobalTrileadPing pgtp = new PacketGlobalTrileadPing();
-
- tm.sendMessage(pgtp.getPayload());
-
- if (log.isEnabled())
- log.log(50, "Sending SSH_MSG_GLOBAL_REQUEST 'trilead-ping'.");
-
- try
- {
- if (waitForGlobalRequestResult() == true)
- throw new IOException("Your server is alive - but buggy. "
- + "It replied with SSH_MSG_REQUEST_SUCCESS when it actually should not.");
-
- }
- catch (IOException e)
- {
- throw (IOException) new IOException("The ping request failed.").initCause(e);
- }
- }
-
- public void requestChannelTrileadPing(Channel c) throws IOException
- {
- PacketChannelTrileadPing pctp;
-
- synchronized (c)
- {
- if (c.state != Channel.STATE_OPEN)
- throw new IOException("Cannot ping this channel (" + c.getReasonClosed() + ")");
-
- pctp = new PacketChannelTrileadPing(c.remoteID);
-
- c.successCounter = c.failedCounter = 0;
- }
-
- synchronized (c.channelSendLock)
- {
- if (c.closeMessageSent)
- throw new IOException("Cannot ping this channel (" + c.getReasonClosed() + ")");
- tm.sendMessage(pctp.getPayload());
- }
-
- try
- {
- if (waitForChannelRequestResult(c) == true)
- throw new IOException("Your server is alive - but buggy. "
- + "It replied with SSH_MSG_SESSION_SUCCESS when it actually should not.");
-
- }
- catch (IOException e)
- {
- throw (IOException) new IOException("The ping request failed.").initCause(e);
- }
- }
-
- public void requestPTY(Channel c, String term, int term_width_characters, int term_height_characters,
- int term_width_pixels, int term_height_pixels, byte[] terminal_modes) throws IOException
- {
- PacketSessionPtyRequest spr;
-
- synchronized (c)
- {
- if (c.state != Channel.STATE_OPEN)
- throw new IOException("Cannot request PTY on this channel (" + c.getReasonClosed() + ")");
-
- spr = new PacketSessionPtyRequest(c.remoteID, true, term, term_width_characters, term_height_characters,
- term_width_pixels, term_height_pixels, terminal_modes);
-
- c.successCounter = c.failedCounter = 0;
- }
-
- synchronized (c.channelSendLock)
- {
- if (c.closeMessageSent)
- throw new IOException("Cannot request PTY on this channel (" + c.getReasonClosed() + ")");
- tm.sendMessage(spr.getPayload());
- }
-
- try
- {
- if (waitForChannelRequestResult(c) == false)
- throw new IOException("The server denied the request.");
- }
- catch (IOException e)
- {
- throw (IOException) new IOException("PTY request failed").initCause(e);
- }
- }
-
-
- public void resizePTY(Channel c, int term_width_characters, int term_height_characters,
- int term_width_pixels, int term_height_pixels) throws IOException {
- PacketSessionPtyResize spr;
-
- synchronized (c) {
- if (c.state != Channel.STATE_OPEN)
- throw new IOException("Cannot request PTY on this channel ("
- + c.getReasonClosed() + ")");
-
- spr = new PacketSessionPtyResize(c.remoteID, term_width_characters, term_height_characters,
- term_width_pixels, term_height_pixels);
- c.successCounter = c.failedCounter = 0;
- }
-
- synchronized (c.channelSendLock) {
- if (c.closeMessageSent)
- throw new IOException("Cannot request PTY on this channel ("
- + c.getReasonClosed() + ")");
- tm.sendMessage(spr.getPayload());
- }
- }
-
-
- public void requestX11(Channel c, boolean singleConnection, String x11AuthenticationProtocol,
- String x11AuthenticationCookie, int x11ScreenNumber) throws IOException
- {
- PacketSessionX11Request psr;
-
- synchronized (c)
- {
- if (c.state != Channel.STATE_OPEN)
- throw new IOException("Cannot request X11 on this channel (" + c.getReasonClosed() + ")");
-
- psr = new PacketSessionX11Request(c.remoteID, true, singleConnection, x11AuthenticationProtocol,
- x11AuthenticationCookie, x11ScreenNumber);
-
- c.successCounter = c.failedCounter = 0;
- }
-
- synchronized (c.channelSendLock)
- {
- if (c.closeMessageSent)
- throw new IOException("Cannot request X11 on this channel (" + c.getReasonClosed() + ")");
- tm.sendMessage(psr.getPayload());
- }
-
- if (log.isEnabled())
- log.log(50, "Requesting X11 forwarding (Channel " + c.localID + "/" + c.remoteID + ")");
-
- try
- {
- if (waitForChannelRequestResult(c) == false)
- throw new IOException("The server denied the request.");
- }
- catch (IOException e)
- {
- throw (IOException) new IOException("The X11 request failed.").initCause(e);
- }
- }
-
- public void requestSubSystem(Channel c, String subSystemName) throws IOException
- {
- PacketSessionSubsystemRequest ssr;
-
- synchronized (c)
- {
- if (c.state != Channel.STATE_OPEN)
- throw new IOException("Cannot request subsystem on this channel (" + c.getReasonClosed() + ")");
-
- ssr = new PacketSessionSubsystemRequest(c.remoteID, true, subSystemName);
-
- c.successCounter = c.failedCounter = 0;
- }
-
- synchronized (c.channelSendLock)
- {
- if (c.closeMessageSent)
- throw new IOException("Cannot request subsystem on this channel (" + c.getReasonClosed() + ")");
- tm.sendMessage(ssr.getPayload());
- }
-
- try
- {
- if (waitForChannelRequestResult(c) == false)
- throw new IOException("The server denied the request.");
- }
- catch (IOException e)
- {
- throw (IOException) new IOException("The subsystem request failed.").initCause(e);
- }
- }
-
- public void requestExecCommand(Channel c, String cmd) throws IOException
- {
- PacketSessionExecCommand sm;
-
- synchronized (c)
- {
- if (c.state != Channel.STATE_OPEN)
- throw new IOException("Cannot execute command on this channel (" + c.getReasonClosed() + ")");
-
- sm = new PacketSessionExecCommand(c.remoteID, true, cmd);
-
- c.successCounter = c.failedCounter = 0;
- }
-
- synchronized (c.channelSendLock)
- {
- if (c.closeMessageSent)
- throw new IOException("Cannot execute command on this channel (" + c.getReasonClosed() + ")");
- tm.sendMessage(sm.getPayload());
- }
-
- if (log.isEnabled())
- log.log(50, "Executing command (channel " + c.localID + ", '" + cmd + "')");
-
- try
- {
- if (waitForChannelRequestResult(c) == false)
- throw new IOException("The server denied the request.");
- }
- catch (IOException e)
- {
- throw (IOException) new IOException("The execute request failed.").initCause(e);
- }
- }
-
- public void requestShell(Channel c) throws IOException
- {
- PacketSessionStartShell sm;
-
- synchronized (c)
- {
- if (c.state != Channel.STATE_OPEN)
- throw new IOException("Cannot start shell on this channel (" + c.getReasonClosed() + ")");
-
- sm = new PacketSessionStartShell(c.remoteID, true);
-
- c.successCounter = c.failedCounter = 0;
- }
-
- synchronized (c.channelSendLock)
- {
- if (c.closeMessageSent)
- throw new IOException("Cannot start shell on this channel (" + c.getReasonClosed() + ")");
- tm.sendMessage(sm.getPayload());
- }
-
- try
- {
- if (waitForChannelRequestResult(c) == false)
- throw new IOException("The server denied the request.");
- }
- catch (IOException e)
- {
- throw (IOException) new IOException("The shell request failed.").initCause(e);
- }
- }
-
- public void msgChannelExtendedData(byte[] msg, int msglen) throws IOException
- {
- if (msglen <= 13)
- throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong size (" + msglen + ")");
-
- int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
- int dataType = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);
- int len = ((msg[9] & 0xff) << 24) | ((msg[10] & 0xff) << 16) | ((msg[11] & 0xff) << 8) | (msg[12] & 0xff);
-
- Channel c = getChannel(id);
-
- if (c == null)
- throw new IOException("Unexpected SSH_MSG_CHANNEL_EXTENDED_DATA message for non-existent channel " + id);
-
- if (dataType != Packets.SSH_EXTENDED_DATA_STDERR)
- throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has unknown type (" + dataType + ")");
-
- if (len != (msglen - 13))
- throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong len (calculated " + (msglen - 13)
- + ", got " + len + ")");
-
- if (log.isEnabled())
- log.log(80, "Got SSH_MSG_CHANNEL_EXTENDED_DATA (channel " + id + ", " + len + ")");
-
- synchronized (c)
- {
- if (c.state == Channel.STATE_CLOSED)
- return; // ignore
-
- if (c.state != Channel.STATE_OPEN)
- throw new IOException("Got SSH_MSG_CHANNEL_EXTENDED_DATA, but channel is not in correct state ("
- + c.state + ")");
-
- if (c.localWindow < len)
- throw new IOException("Remote sent too much data, does not fit into window.");
-
- c.localWindow -= len;
-
- System.arraycopy(msg, 13, c.stderrBuffer, c.stderrWritepos, len);
- c.stderrWritepos += len;
-
- c.notifyAll();
- }
- }
-
- /**
- * Wait until for a condition.
- *
- * @param c
- * Channel
- * @param timeout
- * in ms, 0 means no timeout.
- * @param condition_mask
- * minimum event mask
- * @return all current events
- *
- */
- public int waitForCondition(Channel c, long timeout, int condition_mask)
- {
- long end_time = 0;
- boolean end_time_set = false;
-
- synchronized (c)
- {
- while (true)
- {
- int current_cond = 0;
-
- int stdoutAvail = c.stdoutWritepos - c.stdoutReadpos;
- int stderrAvail = c.stderrWritepos - c.stderrReadpos;
-
- if (stdoutAvail > 0)
- current_cond = current_cond | ChannelCondition.STDOUT_DATA;
-
- if (stderrAvail > 0)
- current_cond = current_cond | ChannelCondition.STDERR_DATA;
-
- if (c.EOF)
- current_cond = current_cond | ChannelCondition.EOF;
-
- if (c.getExitStatus() != null)
- current_cond = current_cond | ChannelCondition.EXIT_STATUS;
-
- if (c.getExitSignal() != null)
- current_cond = current_cond | ChannelCondition.EXIT_SIGNAL;
-
- if (c.state == Channel.STATE_CLOSED)
- return current_cond | ChannelCondition.CLOSED | ChannelCondition.EOF;
-
- if ((current_cond & condition_mask) != 0)
- return current_cond;
-
- if (timeout > 0)
- {
- if (!end_time_set)
- {
- end_time = System.currentTimeMillis() + timeout;
- end_time_set = true;
- }
- else
- {
- timeout = end_time - System.currentTimeMillis();
-
- if (timeout <= 0)
- return current_cond | ChannelCondition.TIMEOUT;
- }
- }
-
- try
- {
- if (timeout > 0)
- c.wait(timeout);
- else
- c.wait();
- }
- catch (InterruptedException e)
- {
- }
- }
- }
- }
-
- public int getAvailable(Channel c, boolean extended) throws IOException
- {
- synchronized (c)
- {
- int avail;
-
- if (extended)
- avail = c.stderrWritepos - c.stderrReadpos;
- else
- avail = c.stdoutWritepos - c.stdoutReadpos;
-
- return ((avail > 0) ? avail : (c.EOF ? -1 : 0));
- }
- }
-
- public int getChannelData(Channel c, boolean extended, byte[] target, int off, int len) throws IOException
- {
- int copylen = 0;
- int increment = 0;
- int remoteID = 0;
- int localID = 0;
-
- synchronized (c)
- {
- int stdoutAvail = 0;
- int stderrAvail = 0;
-
- while (true)
- {
- /*
- * Data available? We have to return remaining data even if the
- * channel is already closed.
- */
-
- stdoutAvail = c.stdoutWritepos - c.stdoutReadpos;
- stderrAvail = c.stderrWritepos - c.stderrReadpos;
-
- if ((!extended) && (stdoutAvail != 0))
- break;
-
- if ((extended) && (stderrAvail != 0))
- break;
-
- /* Do not wait if more data will never arrive (EOF or CLOSED) */
-
- if ((c.EOF) || (c.state != Channel.STATE_OPEN))
- return -1;
-
- try
- {
- c.wait();
- }
- catch (InterruptedException ignore)
- {
- }
- }
-
- /* OK, there is some data. Return it. */
-
- if (!extended)
- {
- copylen = (stdoutAvail > len) ? len : stdoutAvail;
- System.arraycopy(c.stdoutBuffer, c.stdoutReadpos, target, off, copylen);
- c.stdoutReadpos += copylen;
-
- if (c.stdoutReadpos != c.stdoutWritepos)
-
- System.arraycopy(c.stdoutBuffer, c.stdoutReadpos, c.stdoutBuffer, 0, c.stdoutWritepos
- - c.stdoutReadpos);
-
- c.stdoutWritepos -= c.stdoutReadpos;
- c.stdoutReadpos = 0;
- }
- else
- {
- copylen = (stderrAvail > len) ? len : stderrAvail;
- System.arraycopy(c.stderrBuffer, c.stderrReadpos, target, off, copylen);
- c.stderrReadpos += copylen;
-
- if (c.stderrReadpos != c.stderrWritepos)
-
- System.arraycopy(c.stderrBuffer, c.stderrReadpos, c.stderrBuffer, 0, c.stderrWritepos
- - c.stderrReadpos);
-
- c.stderrWritepos -= c.stderrReadpos;
- c.stderrReadpos = 0;
- }
-
- if (c.state != Channel.STATE_OPEN)
- return copylen;
-
- if (c.localWindow < ((Channel.CHANNEL_BUFFER_SIZE + 1) / 2))
- {
- int minFreeSpace = Math.min(Channel.CHANNEL_BUFFER_SIZE - c.stdoutWritepos, Channel.CHANNEL_BUFFER_SIZE
- - c.stderrWritepos);
-
- increment = minFreeSpace - c.localWindow;
- c.localWindow = minFreeSpace;
- }
-
- remoteID = c.remoteID; /* read while holding the lock */
- localID = c.localID; /* read while holding the lock */
- }
-
- /*
- * If a consumer reads stdout and stdin in parallel, we may end up with
- * sending two msgWindowAdjust messages. Luckily, it
- * does not matter in which order they arrive at the server.
- */
-
- if (increment > 0)
- {
- if (log.isEnabled())
- log.log(80, "Sending SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + localID + ", " + increment + ")");
-
- synchronized (c.channelSendLock)
- {
- byte[] msg = c.msgWindowAdjust;
-
- msg[0] = Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST;
- msg[1] = (byte) (remoteID >> 24);
- msg[2] = (byte) (remoteID >> 16);
- msg[3] = (byte) (remoteID >> 8);
- msg[4] = (byte) (remoteID);
- msg[5] = (byte) (increment >> 24);
- msg[6] = (byte) (increment >> 16);
- msg[7] = (byte) (increment >> 8);
- msg[8] = (byte) (increment);
-
- if (c.closeMessageSent == false)
- tm.sendMessage(msg);
- }
- }
-
- return copylen;
- }
-
- public void msgChannelData(byte[] msg, int msglen) throws IOException
- {
- if (msglen <= 9)
- throw new IOException("SSH_MSG_CHANNEL_DATA message has wrong size (" + msglen + ")");
-
- int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
- int len = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);
-
- Channel c = getChannel(id);
-
- if (c == null)
- throw new IOException("Unexpected SSH_MSG_CHANNEL_DATA message for non-existent channel " + id);
-
- if (len != (msglen - 9))
- throw new IOException("SSH_MSG_CHANNEL_DATA message has wrong len (calculated " + (msglen - 9) + ", got "
- + len + ")");
-
- if (log.isEnabled())
- log.log(80, "Got SSH_MSG_CHANNEL_DATA (channel " + id + ", " + len + ")");
-
- synchronized (c)
- {
- if (c.state == Channel.STATE_CLOSED)
- return; // ignore
-
- if (c.state != Channel.STATE_OPEN)
- throw new IOException("Got SSH_MSG_CHANNEL_DATA, but channel is not in correct state (" + c.state + ")");
-
- if (c.localWindow < len)
- throw new IOException("Remote sent too much data, does not fit into window.");
-
- c.localWindow -= len;
-
- System.arraycopy(msg, 9, c.stdoutBuffer, c.stdoutWritepos, len);
- c.stdoutWritepos += len;
-
- c.notifyAll();
- }
- }
-
- public void msgChannelWindowAdjust(byte[] msg, int msglen) throws IOException
- {
- if (msglen != 9)
- throw new IOException("SSH_MSG_CHANNEL_WINDOW_ADJUST message has wrong size (" + msglen + ")");
-
- int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
- int windowChange = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);
-
- Channel c = getChannel(id);
-
- if (c == null)
- throw new IOException("Unexpected SSH_MSG_CHANNEL_WINDOW_ADJUST message for non-existent channel " + id);
-
- synchronized (c)
- {
- final long huge = 0xFFFFffffL; /* 2^32 - 1 */
-
- c.remoteWindow += (windowChange & huge); /* avoid sign extension */
-
- /* TODO - is this a good heuristic? */
-
- if ((c.remoteWindow > huge))
- c.remoteWindow = huge;
-
- c.notifyAll();
- }
-
- if (log.isEnabled())
- log.log(80, "Got SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + id + ", " + windowChange + ")");
- }
-
- public void msgChannelOpen(byte[] msg, int msglen) throws IOException
- {
- TypesReader tr = new TypesReader(msg, 0, msglen);
-
- tr.readByte(); // skip packet type
- String channelType = tr.readString();
- int remoteID = tr.readUINT32(); /* sender channel */
- int remoteWindow = tr.readUINT32(); /* initial window size */
- int remoteMaxPacketSize = tr.readUINT32(); /* maximum packet size */
-
- if ("x11".equals(channelType))
- {
- synchronized (x11_magic_cookies)
- {
- /* If we did not request X11 forwarding, then simply ignore this bogus request. */
-
- if (x11_magic_cookies.size() == 0)
- {
- PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID,
- Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED, "X11 forwarding not activated", "");
-
- tm.sendAsynchronousMessage(pcof.getPayload());
-
- if (log.isEnabled())
- log.log(20, "Unexpected X11 request, denying it!");
-
- return;
- }
- }
-
- String remoteOriginatorAddress = tr.readString();
- int remoteOriginatorPort = tr.readUINT32();
-
- Channel c = new Channel(this);
-
- synchronized (c)
- {
- c.remoteID = remoteID;
- c.remoteWindow = remoteWindow & 0xFFFFffffL; /* properly convert UINT32 to long */
- c.remoteMaxPacketSize = remoteMaxPacketSize;
- c.localID = addChannel(c);
- }
-
- /*
- * The open confirmation message will be sent from another thread
- */
-
- RemoteX11AcceptThread rxat = new RemoteX11AcceptThread(c, remoteOriginatorAddress, remoteOriginatorPort);
- rxat.setDaemon(true);
- rxat.start();
-
- return;
- }
-
- if ("forwarded-tcpip".equals(channelType))
- {
- String remoteConnectedAddress = tr.readString(); /* address that was connected */
- int remoteConnectedPort = tr.readUINT32(); /* port that was connected */
- String remoteOriginatorAddress = tr.readString(); /* originator IP address */
- int remoteOriginatorPort = tr.readUINT32(); /* originator port */
-
- RemoteForwardingData rfd = null;
-
- synchronized (remoteForwardings)
- {
- rfd = (RemoteForwardingData) remoteForwardings.get(new Integer(remoteConnectedPort));
- }
-
- if (rfd == null)
- {
- PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID,
- Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
- "No thanks, unknown port in forwarded-tcpip request", "");
-
- /* Always try to be polite. */
-
- tm.sendAsynchronousMessage(pcof.getPayload());
-
- if (log.isEnabled())
- log.log(20, "Unexpected forwarded-tcpip request, denying it!");
-
- return;
- }
-
- Channel c = new Channel(this);
-
- synchronized (c)
- {
- c.remoteID = remoteID;
- c.remoteWindow = remoteWindow & 0xFFFFffffL; /* convert UINT32 to long */
- c.remoteMaxPacketSize = remoteMaxPacketSize;
- c.localID = addChannel(c);
- }
-
- /*
- * The open confirmation message will be sent from another thread.
- */
-
- RemoteAcceptThread rat = new RemoteAcceptThread(c, remoteConnectedAddress, remoteConnectedPort,
- remoteOriginatorAddress, remoteOriginatorPort, rfd.targetAddress, rfd.targetPort);
-
- rat.setDaemon(true);
- rat.start();
-
- return;
- }
-
- if ("auth-agent@openssh.com".equals(channelType)) {
- Channel c = new Channel(this);
-
- synchronized (c)
- {
- c.remoteID = remoteID;
- c.remoteWindow = remoteWindow & 0xFFFFffffL; /* properly convert UINT32 to long */
- c.remoteMaxPacketSize = remoteMaxPacketSize;
- c.localID = addChannel(c);
- }
-
- AuthAgentForwardThread aat = new AuthAgentForwardThread(c, authAgent);
-
- aat.setDaemon(true);
- aat.start();
-
- return;
- }
-
- /* Tell the server that we have no idea what it is talking about */
-
- PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID, Packets.SSH_OPEN_UNKNOWN_CHANNEL_TYPE,
- "Unknown channel type", "");
-
- tm.sendAsynchronousMessage(pcof.getPayload());
-
- if (log.isEnabled())
- log.log(20, "The peer tried to open an unsupported channel type (" + channelType + ")");
- }
-
- public void msgChannelRequest(byte[] msg, int msglen) throws IOException
- {
- TypesReader tr = new TypesReader(msg, 0, msglen);
-
- tr.readByte(); // skip packet type
- int id = tr.readUINT32();
-
- Channel c = getChannel(id);
-
- if (c == null)
- throw new IOException("Unexpected SSH_MSG_CHANNEL_REQUEST message for non-existent channel " + id);
-
- String type = tr.readString("US-ASCII");
- boolean wantReply = tr.readBoolean();
-
- if (log.isEnabled())
- log.log(80, "Got SSH_MSG_CHANNEL_REQUEST (channel " + id + ", '" + type + "')");
-
- if (type.equals("exit-status"))
- {
- if (wantReply != false)
- throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message, 'want reply' is true");
-
- int exit_status = tr.readUINT32();
-
- if (tr.remain() != 0)
- throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
-
- synchronized (c)
- {
- c.exit_status = new Integer(exit_status);
- c.notifyAll();
- }
-
- if (log.isEnabled())
- log.log(50, "Got EXIT STATUS (channel " + id + ", status " + exit_status + ")");
-
- return;
- }
-
- if (type.equals("exit-signal"))
- {
- if (wantReply != false)
- throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message, 'want reply' is true");
-
- String signame = tr.readString("US-ASCII");
- tr.readBoolean();
- tr.readString();
- tr.readString();
-
- if (tr.remain() != 0)
- throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
-
- synchronized (c)
- {
- c.exit_signal = signame;
- c.notifyAll();
- }
-
- if (log.isEnabled())
- log.log(50, "Got EXIT SIGNAL (channel " + id + ", signal " + signame + ")");
-
- return;
- }
-
- /* We simply ignore unknown channel requests, however, if the server wants a reply,
- * then we signal that we have no idea what it is about.
- */
-
- if (wantReply)
- {
- byte[] reply = new byte[5];
-
- reply[0] = Packets.SSH_MSG_CHANNEL_FAILURE;
- reply[1] = (byte) (c.remoteID >> 24);
- reply[2] = (byte) (c.remoteID >> 16);
- reply[3] = (byte) (c.remoteID >> 8);
- reply[4] = (byte) (c.remoteID);
-
- tm.sendAsynchronousMessage(reply);
- }
-
- if (log.isEnabled())
- log.log(50, "Channel request '" + type + "' is not known, ignoring it");
- }
-
- public void msgChannelEOF(byte[] msg, int msglen) throws IOException
- {
- if (msglen != 5)
- throw new IOException("SSH_MSG_CHANNEL_EOF message has wrong size (" + msglen + ")");
-
- int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
-
- Channel c = getChannel(id);
-
- if (c == null)
- throw new IOException("Unexpected SSH_MSG_CHANNEL_EOF message for non-existent channel " + id);
-
- synchronized (c)
- {
- c.EOF = true;
- c.notifyAll();
- }
-
- if (log.isEnabled())
- log.log(50, "Got SSH_MSG_CHANNEL_EOF (channel " + id + ")");
- }
-
- public void msgChannelClose(byte[] msg, int msglen) throws IOException
- {
- if (msglen != 5)
- throw new IOException("SSH_MSG_CHANNEL_CLOSE message has wrong size (" + msglen + ")");
-
- int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
-
- Channel c = getChannel(id);
-
- if (c == null)
- throw new IOException("Unexpected SSH_MSG_CHANNEL_CLOSE message for non-existent channel " + id);
-
- synchronized (c)
- {
- c.EOF = true;
- c.state = Channel.STATE_CLOSED;
- c.setReasonClosed("Close requested by remote");
- c.closeMessageRecv = true;
-
- removeChannel(c.localID);
-
- c.notifyAll();
- }
-
- if (log.isEnabled())
- log.log(50, "Got SSH_MSG_CHANNEL_CLOSE (channel " + id + ")");
- }
-
- public void msgChannelSuccess(byte[] msg, int msglen) throws IOException
- {
- if (msglen != 5)
- throw new IOException("SSH_MSG_CHANNEL_SUCCESS message has wrong size (" + msglen + ")");
-
- int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
-
- Channel c = getChannel(id);
-
- if (c == null)
- throw new IOException("Unexpected SSH_MSG_CHANNEL_SUCCESS message for non-existent channel " + id);
-
- synchronized (c)
- {
- c.successCounter++;
- c.notifyAll();
- }
-
- if (log.isEnabled())
- log.log(80, "Got SSH_MSG_CHANNEL_SUCCESS (channel " + id + ")");
- }
-
- public void msgChannelFailure(byte[] msg, int msglen) throws IOException
- {
- if (msglen != 5)
- throw new IOException("SSH_MSG_CHANNEL_FAILURE message has wrong size (" + msglen + ")");
-
- int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
-
- Channel c = getChannel(id);
-
- if (c == null)
- throw new IOException("Unexpected SSH_MSG_CHANNEL_FAILURE message for non-existent channel " + id);
-
- synchronized (c)
- {
- c.failedCounter++;
- c.notifyAll();
- }
-
- if (log.isEnabled())
- log.log(50, "Got SSH_MSG_CHANNEL_FAILURE (channel " + id + ")");
- }
-
- public void msgChannelOpenConfirmation(byte[] msg, int msglen) throws IOException
- {
- PacketChannelOpenConfirmation sm = new PacketChannelOpenConfirmation(msg, 0, msglen);
-
- Channel c = getChannel(sm.recipientChannelID);
-
- if (c == null)
- throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for non-existent channel "
- + sm.recipientChannelID);
-
- synchronized (c)
- {
- if (c.state != Channel.STATE_OPENING)
- throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for channel "
- + sm.recipientChannelID);
-
- c.remoteID = sm.senderChannelID;
- c.remoteWindow = sm.initialWindowSize & 0xFFFFffffL; /* convert UINT32 to long */
- c.remoteMaxPacketSize = sm.maxPacketSize;
- c.state = Channel.STATE_OPEN;
- c.notifyAll();
- }
-
- if (log.isEnabled())
- log.log(50, "Got SSH_MSG_CHANNEL_OPEN_CONFIRMATION (channel " + sm.recipientChannelID + " / remote: "
- + sm.senderChannelID + ")");
- }
-
- public void msgChannelOpenFailure(byte[] msg, int msglen) throws IOException
- {
- if (msglen < 5)
- throw new IOException("SSH_MSG_CHANNEL_OPEN_FAILURE message has wrong size (" + msglen + ")");
-
- TypesReader tr = new TypesReader(msg, 0, msglen);
-
- tr.readByte(); // skip packet type
- int id = tr.readUINT32(); /* sender channel */
-
- Channel c = getChannel(id);
-
- if (c == null)
- throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_FAILURE message for non-existent channel " + id);
-
- int reasonCode = tr.readUINT32();
- String description = tr.readString("UTF-8");
-
- String reasonCodeSymbolicName = null;
-
- switch (reasonCode)
- {
- case 1:
- reasonCodeSymbolicName = "SSH_OPEN_ADMINISTRATIVELY_PROHIBITED";
- break;
- case 2:
- reasonCodeSymbolicName = "SSH_OPEN_CONNECT_FAILED";
- break;
- case 3:
- reasonCodeSymbolicName = "SSH_OPEN_UNKNOWN_CHANNEL_TYPE";
- break;
- case 4:
- reasonCodeSymbolicName = "SSH_OPEN_RESOURCE_SHORTAGE";
- break;
- default:
- reasonCodeSymbolicName = "UNKNOWN REASON CODE (" + reasonCode + ")";
- }
-
- StringBuffer descriptionBuffer = new StringBuffer();
- descriptionBuffer.append(description);
-
- for (int i = 0; i < descriptionBuffer.length(); i++)
- {
- char cc = descriptionBuffer.charAt(i);
-
- if ((cc >= 32) && (cc <= 126))
- continue;
- descriptionBuffer.setCharAt(i, '\uFFFD');
- }
-
- synchronized (c)
- {
- c.EOF = true;
- c.state = Channel.STATE_CLOSED;
- c.setReasonClosed("The server refused to open the channel (" + reasonCodeSymbolicName + ", '"
- + descriptionBuffer.toString() + "')");
- c.notifyAll();
- }
-
- if (log.isEnabled())
- log.log(50, "Got SSH_MSG_CHANNEL_OPEN_FAILURE (channel " + id + ")");
- }
-
- public void msgGlobalRequest(byte[] msg, int msglen) throws IOException
- {
- /* Currently we do not support any kind of global request */
-
- TypesReader tr = new TypesReader(msg, 0, msglen);
-
- tr.readByte(); // skip packet type
- String requestName = tr.readString();
- boolean wantReply = tr.readBoolean();
-
- if (wantReply)
- {
- byte[] reply_failure = new byte[1];
- reply_failure[0] = Packets.SSH_MSG_REQUEST_FAILURE;
-
- tm.sendAsynchronousMessage(reply_failure);
- }
-
- /* We do not clean up the requestName String - that is OK for debug */
-
- if (log.isEnabled())
- log.log(80, "Got SSH_MSG_GLOBAL_REQUEST (" + requestName + ")");
- }
-
- public void msgGlobalSuccess() throws IOException
- {
- synchronized (channels)
- {
- globalSuccessCounter++;
- channels.notifyAll();
- }
-
- if (log.isEnabled())
- log.log(80, "Got SSH_MSG_REQUEST_SUCCESS");
- }
-
- public void msgGlobalFailure() throws IOException
- {
- synchronized (channels)
- {
- globalFailedCounter++;
- channels.notifyAll();
- }
-
- if (log.isEnabled())
- log.log(80, "Got SSH_MSG_REQUEST_FAILURE");
- }
-
- public void handleMessage(byte[] msg, int msglen) throws IOException
- {
- if (msg == null)
- {
- if (log.isEnabled())
- log.log(50, "HandleMessage: got shutdown");
-
- synchronized (listenerThreads)
- {
- for (int i = 0; i < listenerThreads.size(); i++)
- {
- IChannelWorkerThread lat = (IChannelWorkerThread) listenerThreads.elementAt(i);
- lat.stopWorking();
- }
- listenerThreadsAllowed = false;
- }
-
- synchronized (channels)
- {
- shutdown = true;
-
- for (int i = 0; i < channels.size(); i++)
- {
- Channel c = (Channel) channels.elementAt(i);
- synchronized (c)
- {
- c.EOF = true;
- c.state = Channel.STATE_CLOSED;
- c.setReasonClosed("The connection is being shutdown");
- c.closeMessageRecv = true; /*
- * You never know, perhaps
- * we are waiting for a
- * pending close message
- * from the server...
- */
- c.notifyAll();
- }
- }
- /* Works with J2ME */
- channels.setSize(0);
- channels.trimToSize();
- channels.notifyAll(); /* Notify global response waiters */
- return;
- }
- }
-
- switch (msg[0])
- {
- case Packets.SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
- msgChannelOpenConfirmation(msg, msglen);
- break;
- case Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST:
- msgChannelWindowAdjust(msg, msglen);
- break;
- case Packets.SSH_MSG_CHANNEL_DATA:
- msgChannelData(msg, msglen);
- break;
- case Packets.SSH_MSG_CHANNEL_EXTENDED_DATA:
- msgChannelExtendedData(msg, msglen);
- break;
- case Packets.SSH_MSG_CHANNEL_REQUEST:
- msgChannelRequest(msg, msglen);
- break;
- case Packets.SSH_MSG_CHANNEL_EOF:
- msgChannelEOF(msg, msglen);
- break;
- case Packets.SSH_MSG_CHANNEL_OPEN:
- msgChannelOpen(msg, msglen);
- break;
- case Packets.SSH_MSG_CHANNEL_CLOSE:
- msgChannelClose(msg, msglen);
- break;
- case Packets.SSH_MSG_CHANNEL_SUCCESS:
- msgChannelSuccess(msg, msglen);
- break;
- case Packets.SSH_MSG_CHANNEL_FAILURE:
- msgChannelFailure(msg, msglen);
- break;
- case Packets.SSH_MSG_CHANNEL_OPEN_FAILURE:
- msgChannelOpenFailure(msg, msglen);
- break;
- case Packets.SSH_MSG_GLOBAL_REQUEST:
- msgGlobalRequest(msg, msglen);
- break;
- case Packets.SSH_MSG_REQUEST_SUCCESS:
- msgGlobalSuccess();
- break;
- case Packets.SSH_MSG_REQUEST_FAILURE:
- msgGlobalFailure();
- break;
- default:
- throw new IOException("Cannot handle unknown channel message " + (msg[0] & 0xff));
- }
- }
-}
+
+package com.trilead.ssh2.channel;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Vector;
+
+import com.trilead.ssh2.AuthAgentCallback;
+import com.trilead.ssh2.ChannelCondition;
+import com.trilead.ssh2.log.Logger;
+import com.trilead.ssh2.packets.PacketChannelAuthAgentReq;
+import com.trilead.ssh2.packets.PacketChannelOpenConfirmation;
+import com.trilead.ssh2.packets.PacketChannelOpenFailure;
+import com.trilead.ssh2.packets.PacketChannelTrileadPing;
+import com.trilead.ssh2.packets.PacketGlobalCancelForwardRequest;
+import com.trilead.ssh2.packets.PacketGlobalForwardRequest;
+import com.trilead.ssh2.packets.PacketGlobalTrileadPing;
+import com.trilead.ssh2.packets.PacketOpenDirectTCPIPChannel;
+import com.trilead.ssh2.packets.PacketOpenSessionChannel;
+import com.trilead.ssh2.packets.PacketSessionExecCommand;
+import com.trilead.ssh2.packets.PacketSessionPtyRequest;
+import com.trilead.ssh2.packets.PacketSessionPtyResize;
+import com.trilead.ssh2.packets.PacketSessionStartShell;
+import com.trilead.ssh2.packets.PacketSessionSubsystemRequest;
+import com.trilead.ssh2.packets.PacketSessionX11Request;
+import com.trilead.ssh2.packets.Packets;
+import com.trilead.ssh2.packets.TypesReader;
+import com.trilead.ssh2.transport.MessageHandler;
+import com.trilead.ssh2.transport.TransportManager;
+
+/**
+ * ChannelManager. Please read the comments in Channel.java.
+ * <p>
+ * Besides the crypto part, this is the core of the library.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: ChannelManager.java,v 1.2 2008/03/03 07:01:36 cplattne Exp $
+ */
+public class ChannelManager implements MessageHandler
+{
+ private static final Logger log = Logger.getLogger(ChannelManager.class);
+
+ private HashMap<String, X11ServerData> x11_magic_cookies = new HashMap<String, X11ServerData>();
+
+ private TransportManager tm;
+
+ private Vector<Channel> channels = new Vector<Channel>();
+ private int nextLocalChannel = 100;
+ private boolean shutdown = false;
+ private int globalSuccessCounter = 0;
+ private int globalFailedCounter = 0;
+
+ private HashMap<Integer, RemoteForwardingData> remoteForwardings = new HashMap<Integer, RemoteForwardingData>();
+
+ private AuthAgentCallback authAgent;
+
+ private Vector<IChannelWorkerThread> listenerThreads = new Vector<IChannelWorkerThread>();
+
+ private boolean listenerThreadsAllowed = true;
+
+ public ChannelManager(TransportManager tm)
+ {
+ this.tm = tm;
+ tm.registerMessageHandler(this, 80, 100);
+ }
+
+ private Channel getChannel(int id)
+ {
+ synchronized (channels)
+ {
+ for (int i = 0; i < channels.size(); i++)
+ {
+ Channel c = channels.elementAt(i);
+ if (c.localID == id)
+ return c;
+ }
+ }
+ return null;
+ }
+
+ private void removeChannel(int id)
+ {
+ synchronized (channels)
+ {
+ for (int i = 0; i < channels.size(); i++)
+ {
+ Channel c = channels.elementAt(i);
+ if (c.localID == id)
+ {
+ channels.removeElementAt(i);
+ break;
+ }
+ }
+ }
+ }
+
+ private int addChannel(Channel c)
+ {
+ synchronized (channels)
+ {
+ channels.addElement(c);
+ return nextLocalChannel++;
+ }
+ }
+
+ private void waitUntilChannelOpen(Channel c) throws IOException
+ {
+ synchronized (c)
+ {
+ while (c.state == Channel.STATE_OPENING)
+ {
+ try
+ {
+ c.wait();
+ }
+ catch (InterruptedException ignore)
+ {
+ }
+ }
+
+ if (c.state != Channel.STATE_OPEN)
+ {
+ removeChannel(c.localID);
+
+ String detail = c.getReasonClosed();
+
+ if (detail == null)
+ detail = "state: " + c.state;
+
+ throw new IOException("Could not open channel (" + detail + ")");
+ }
+ }
+ }
+
+ private final boolean waitForGlobalRequestResult() throws IOException
+ {
+ synchronized (channels)
+ {
+ while ((globalSuccessCounter == 0) && (globalFailedCounter == 0))
+ {
+ if (shutdown)
+ {
+ throw new IOException("The connection is being shutdown");
+ }
+
+ try
+ {
+ channels.wait();
+ }
+ catch (InterruptedException ignore)
+ {
+ }
+ }
+
+ if ((globalFailedCounter == 0) && (globalSuccessCounter == 1))
+ return true;
+
+ if ((globalFailedCounter == 1) && (globalSuccessCounter == 0))
+ return false;
+
+ throw new IOException("Illegal state. The server sent " + globalSuccessCounter
+ + " SSH_MSG_REQUEST_SUCCESS and " + globalFailedCounter + " SSH_MSG_REQUEST_FAILURE messages.");
+ }
+ }
+
+ private final boolean waitForChannelRequestResult(Channel c) throws IOException
+ {
+ synchronized (c)
+ {
+ while ((c.successCounter == 0) && (c.failedCounter == 0))
+ {
+ if (c.state != Channel.STATE_OPEN)
+ {
+ String detail = c.getReasonClosed();
+
+ if (detail == null)
+ detail = "state: " + c.state;
+
+ throw new IOException("This SSH2 channel is not open (" + detail + ")");
+ }
+
+ try
+ {
+ c.wait();
+ }
+ catch (InterruptedException ignore)
+ {
+ }
+ }
+
+ if ((c.failedCounter == 0) && (c.successCounter == 1))
+ return true;
+
+ if ((c.failedCounter == 1) && (c.successCounter == 0))
+ return false;
+
+ throw new IOException("Illegal state. The server sent " + c.successCounter
+ + " SSH_MSG_CHANNEL_SUCCESS and " + c.failedCounter + " SSH_MSG_CHANNEL_FAILURE messages.");
+ }
+ }
+
+ public void registerX11Cookie(String hexFakeCookie, X11ServerData data)
+ {
+ synchronized (x11_magic_cookies)
+ {
+ x11_magic_cookies.put(hexFakeCookie, data);
+ }
+ }
+
+ public void unRegisterX11Cookie(String hexFakeCookie, boolean killChannels)
+ {
+ if (hexFakeCookie == null)
+ throw new IllegalStateException("hexFakeCookie may not be null");
+
+ synchronized (x11_magic_cookies)
+ {
+ x11_magic_cookies.remove(hexFakeCookie);
+ }
+
+ if (killChannels == false)
+ return;
+
+ if (log.isEnabled())
+ log.log(50, "Closing all X11 channels for the given fake cookie");
+
+ Vector<Channel> channel_copy;
+
+ synchronized (channels)
+ {
+ channel_copy = (Vector<Channel>) channels.clone();
+ }
+
+ for (int i = 0; i < channel_copy.size(); i++)
+ {
+ Channel c = channel_copy.elementAt(i);
+
+ synchronized (c)
+ {
+ if (hexFakeCookie.equals(c.hexX11FakeCookie) == false)
+ continue;
+ }
+
+ try
+ {
+ closeChannel(c, "Closing X11 channel since the corresponding session is closing", true);
+ }
+ catch (IOException e)
+ {
+ }
+ }
+ }
+
+ public X11ServerData checkX11Cookie(String hexFakeCookie)
+ {
+ synchronized (x11_magic_cookies)
+ {
+ if (hexFakeCookie != null)
+ return x11_magic_cookies.get(hexFakeCookie);
+ }
+ return null;
+ }
+
+ public void closeAllChannels()
+ {
+ if (log.isEnabled())
+ log.log(50, "Closing all channels");
+
+ Vector<Channel> channel_copy;
+
+ synchronized (channels)
+ {
+ channel_copy = (Vector<Channel>) channels.clone();
+ }
+
+ for (int i = 0; i < channel_copy.size(); i++)
+ {
+ Channel c = channel_copy.elementAt(i);
+ try
+ {
+ closeChannel(c, "Closing all channels", true);
+ }
+ catch (IOException e)
+ {
+ }
+ }
+ }
+
+ public void closeChannel(Channel c, String reason, boolean force) throws IOException
+ {
+ byte msg[] = new byte[5];
+
+ synchronized (c)
+ {
+ if (force)
+ {
+ c.state = Channel.STATE_CLOSED;
+ c.EOF = true;
+ }
+
+ c.setReasonClosed(reason);
+
+ msg[0] = Packets.SSH_MSG_CHANNEL_CLOSE;
+ msg[1] = (byte) (c.remoteID >> 24);
+ msg[2] = (byte) (c.remoteID >> 16);
+ msg[3] = (byte) (c.remoteID >> 8);
+ msg[4] = (byte) (c.remoteID);
+
+ c.notifyAll();
+ }
+
+ synchronized (c.channelSendLock)
+ {
+ if (c.closeMessageSent == true)
+ return;
+ tm.sendMessage(msg);
+ c.closeMessageSent = true;
+ }
+
+ if (log.isEnabled())
+ log.log(50, "Sent SSH_MSG_CHANNEL_CLOSE (channel " + c.localID + ")");
+ }
+
+ public void sendEOF(Channel c) throws IOException
+ {
+ byte[] msg = new byte[5];
+
+ synchronized (c)
+ {
+ if (c.state != Channel.STATE_OPEN)
+ return;
+
+ msg[0] = Packets.SSH_MSG_CHANNEL_EOF;
+ msg[1] = (byte) (c.remoteID >> 24);
+ msg[2] = (byte) (c.remoteID >> 16);
+ msg[3] = (byte) (c.remoteID >> 8);
+ msg[4] = (byte) (c.remoteID);
+ }
+
+ synchronized (c.channelSendLock)
+ {
+ if (c.closeMessageSent == true)
+ return;
+ tm.sendMessage(msg);
+ }
+
+ if (log.isEnabled())
+ log.log(50, "Sent EOF (Channel " + c.localID + "/" + c.remoteID + ")");
+ }
+
+ public void sendOpenConfirmation(Channel c) throws IOException
+ {
+ PacketChannelOpenConfirmation pcoc = null;
+
+ synchronized (c)
+ {
+ if (c.state != Channel.STATE_OPENING)
+ return;
+
+ c.state = Channel.STATE_OPEN;
+
+ pcoc = new PacketChannelOpenConfirmation(c.remoteID, c.localID, c.localWindow, c.localMaxPacketSize);
+ }
+
+ synchronized (c.channelSendLock)
+ {
+ if (c.closeMessageSent == true)
+ return;
+ tm.sendMessage(pcoc.getPayload());
+ }
+ }
+
+ public void sendData(Channel c, byte[] buffer, int pos, int len) throws IOException
+ {
+ while (len > 0)
+ {
+ int thislen = 0;
+ byte[] msg;
+
+ synchronized (c)
+ {
+ while (true)
+ {
+ if (c.state == Channel.STATE_CLOSED)
+ throw new IOException("SSH channel is closed. (" + c.getReasonClosed() + ")");
+
+ if (c.state != Channel.STATE_OPEN)
+ throw new IOException("SSH channel in strange state. (" + c.state + ")");
+
+ if (c.remoteWindow != 0)
+ break;
+
+ try
+ {
+ c.wait();
+ }
+ catch (InterruptedException ignore)
+ {
+ }
+ }
+
+ /* len > 0, no sign extension can happen when comparing */
+
+ thislen = (c.remoteWindow >= len) ? len : (int) c.remoteWindow;
+
+ int estimatedMaxDataLen = c.remoteMaxPacketSize - (tm.getPacketOverheadEstimate() + 9);
+
+ /* The worst case scenario =) a true bottleneck */
+
+ if (estimatedMaxDataLen <= 0)
+ {
+ estimatedMaxDataLen = 1;
+ }
+
+ if (thislen > estimatedMaxDataLen)
+ thislen = estimatedMaxDataLen;
+
+ c.remoteWindow -= thislen;
+
+ msg = new byte[1 + 8 + thislen];
+
+ msg[0] = Packets.SSH_MSG_CHANNEL_DATA;
+ msg[1] = (byte) (c.remoteID >> 24);
+ msg[2] = (byte) (c.remoteID >> 16);
+ msg[3] = (byte) (c.remoteID >> 8);
+ msg[4] = (byte) (c.remoteID);
+ msg[5] = (byte) (thislen >> 24);
+ msg[6] = (byte) (thislen >> 16);
+ msg[7] = (byte) (thislen >> 8);
+ msg[8] = (byte) (thislen);
+
+ System.arraycopy(buffer, pos, msg, 9, thislen);
+ }
+
+ synchronized (c.channelSendLock)
+ {
+ if (c.closeMessageSent == true)
+ throw new IOException("SSH channel is closed. (" + c.getReasonClosed() + ")");
+
+ tm.sendMessage(msg);
+ }
+
+ pos += thislen;
+ len -= thislen;
+ }
+ }
+
+ public int requestGlobalForward(String bindAddress, int bindPort, String targetAddress, int targetPort)
+ throws IOException
+ {
+ RemoteForwardingData rfd = new RemoteForwardingData();
+
+ rfd.bindAddress = bindAddress;
+ rfd.bindPort = bindPort;
+ rfd.targetAddress = targetAddress;
+ rfd.targetPort = targetPort;
+
+ synchronized (remoteForwardings)
+ {
+ Integer key = Integer.valueOf(bindPort);
+
+ if (remoteForwardings.get(key) != null)
+ {
+ throw new IOException("There is already a forwarding for remote port " + bindPort);
+ }
+
+ remoteForwardings.put(key, rfd);
+ }
+
+ synchronized (channels)
+ {
+ globalSuccessCounter = globalFailedCounter = 0;
+ }
+
+ PacketGlobalForwardRequest pgf = new PacketGlobalForwardRequest(true, bindAddress, bindPort);
+ tm.sendMessage(pgf.getPayload());
+
+ if (log.isEnabled())
+ log.log(50, "Requesting a remote forwarding ('" + bindAddress + "', " + bindPort + ")");
+
+ try
+ {
+ if (waitForGlobalRequestResult() == false)
+ throw new IOException("The server denied the request (did you enable port forwarding?)");
+ }
+ catch (IOException e)
+ {
+ synchronized (remoteForwardings)
+ {
+ remoteForwardings.remove(rfd);
+ }
+ throw e;
+ }
+
+ return bindPort;
+ }
+
+ public void requestCancelGlobalForward(int bindPort) throws IOException
+ {
+ RemoteForwardingData rfd = null;
+
+ synchronized (remoteForwardings)
+ {
+ rfd = remoteForwardings.get(Integer.valueOf(bindPort));
+
+ if (rfd == null)
+ throw new IOException("Sorry, there is no known remote forwarding for remote port " + bindPort);
+ }
+
+ synchronized (channels)
+ {
+ globalSuccessCounter = globalFailedCounter = 0;
+ }
+
+ PacketGlobalCancelForwardRequest pgcf = new PacketGlobalCancelForwardRequest(true, rfd.bindAddress,
+ rfd.bindPort);
+ tm.sendMessage(pgcf.getPayload());
+
+ if (log.isEnabled())
+ log.log(50, "Requesting cancelation of remote forward ('" + rfd.bindAddress + "', " + rfd.bindPort + ")");
+
+ try
+ {
+ if (waitForGlobalRequestResult() == false)
+ throw new IOException("The server denied the request.");
+ }
+ finally
+ {
+ synchronized (remoteForwardings)
+ {
+ /* Only now we are sure that no more forwarded connections will arrive */
+ remoteForwardings.remove(rfd);
+ }
+ }
+
+ }
+
+ /**
+ * @param agent
+ * @throws IOException
+ */
+ public boolean requestChannelAgentForwarding(Channel c, AuthAgentCallback authAgent) throws IOException {
+ synchronized (this)
+ {
+ if (this.authAgent != null)
+ throw new IllegalStateException("Auth agent already exists");
+
+ this.authAgent = authAgent;
+ }
+
+ synchronized (channels)
+ {
+ globalSuccessCounter = globalFailedCounter = 0;
+ }
+
+ if (log.isEnabled())
+ log.log(50, "Requesting agent forwarding");
+
+ PacketChannelAuthAgentReq aar = new PacketChannelAuthAgentReq(c.remoteID);
+ tm.sendMessage(aar.getPayload());
+
+ if (waitForChannelRequestResult(c) == false) {
+ authAgent = null;
+ return false;
+ }
+
+ return true;
+ }
+
+ public void registerThread(IChannelWorkerThread thr) throws IOException
+ {
+ synchronized (listenerThreads)
+ {
+ if (listenerThreadsAllowed == false)
+ throw new IOException("Too late, this connection is closed.");
+ listenerThreads.addElement(thr);
+ }
+ }
+
+ public Channel openDirectTCPIPChannel(String host_to_connect, int port_to_connect, String originator_IP_address,
+ int originator_port) throws IOException
+ {
+ Channel c = new Channel(this);
+
+ synchronized (c)
+ {
+ c.localID = addChannel(c);
+ // end of synchronized block forces writing out to main memory
+ }
+
+ PacketOpenDirectTCPIPChannel dtc = new PacketOpenDirectTCPIPChannel(c.localID, c.localWindow,
+ c.localMaxPacketSize, host_to_connect, port_to_connect, originator_IP_address, originator_port);
+
+ tm.sendMessage(dtc.getPayload());
+
+ waitUntilChannelOpen(c);
+
+ return c;
+ }
+
+ public Channel openSessionChannel() throws IOException
+ {
+ Channel c = new Channel(this);
+
+ synchronized (c)
+ {
+ c.localID = addChannel(c);
+ // end of synchronized block forces the writing out to main memory
+ }
+
+ if (log.isEnabled())
+ log.log(50, "Sending SSH_MSG_CHANNEL_OPEN (Channel " + c.localID + ")");
+
+ PacketOpenSessionChannel smo = new PacketOpenSessionChannel(c.localID, c.localWindow, c.localMaxPacketSize);
+ tm.sendMessage(smo.getPayload());
+
+ waitUntilChannelOpen(c);
+
+ return c;
+ }
+
+ public void requestGlobalTrileadPing() throws IOException
+ {
+ synchronized (channels)
+ {
+ globalSuccessCounter = globalFailedCounter = 0;
+ }
+
+ PacketGlobalTrileadPing pgtp = new PacketGlobalTrileadPing();
+
+ tm.sendMessage(pgtp.getPayload());
+
+ if (log.isEnabled())
+ log.log(50, "Sending SSH_MSG_GLOBAL_REQUEST 'trilead-ping'.");
+
+ try
+ {
+ if (waitForGlobalRequestResult() == true)
+ throw new IOException("Your server is alive - but buggy. "
+ + "It replied with SSH_MSG_REQUEST_SUCCESS when it actually should not.");
+
+ }
+ catch (IOException e)
+ {
+ throw (IOException) new IOException("The ping request failed.").initCause(e);
+ }
+ }
+
+ public void requestChannelTrileadPing(Channel c) throws IOException
+ {
+ PacketChannelTrileadPing pctp;
+
+ synchronized (c)
+ {
+ if (c.state != Channel.STATE_OPEN)
+ throw new IOException("Cannot ping this channel (" + c.getReasonClosed() + ")");
+
+ pctp = new PacketChannelTrileadPing(c.remoteID);
+
+ c.successCounter = c.failedCounter = 0;
+ }
+
+ synchronized (c.channelSendLock)
+ {
+ if (c.closeMessageSent)
+ throw new IOException("Cannot ping this channel (" + c.getReasonClosed() + ")");
+ tm.sendMessage(pctp.getPayload());
+ }
+
+ try
+ {
+ if (waitForChannelRequestResult(c) == true)
+ throw new IOException("Your server is alive - but buggy. "
+ + "It replied with SSH_MSG_SESSION_SUCCESS when it actually should not.");
+
+ }
+ catch (IOException e)
+ {
+ throw (IOException) new IOException("The ping request failed.").initCause(e);
+ }
+ }
+
+ public void requestPTY(Channel c, String term, int term_width_characters, int term_height_characters,
+ int term_width_pixels, int term_height_pixels, byte[] terminal_modes) throws IOException
+ {
+ PacketSessionPtyRequest spr;
+
+ synchronized (c)
+ {
+ if (c.state != Channel.STATE_OPEN)
+ throw new IOException("Cannot request PTY on this channel (" + c.getReasonClosed() + ")");
+
+ spr = new PacketSessionPtyRequest(c.remoteID, true, term, term_width_characters, term_height_characters,
+ term_width_pixels, term_height_pixels, terminal_modes);
+
+ c.successCounter = c.failedCounter = 0;
+ }
+
+ synchronized (c.channelSendLock)
+ {
+ if (c.closeMessageSent)
+ throw new IOException("Cannot request PTY on this channel (" + c.getReasonClosed() + ")");
+ tm.sendMessage(spr.getPayload());
+ }
+
+ try
+ {
+ if (waitForChannelRequestResult(c) == false)
+ throw new IOException("The server denied the request.");
+ }
+ catch (IOException e)
+ {
+ throw (IOException) new IOException("PTY request failed").initCause(e);
+ }
+ }
+
+
+ public void resizePTY(Channel c, int term_width_characters, int term_height_characters,
+ int term_width_pixels, int term_height_pixels) throws IOException {
+ PacketSessionPtyResize spr;
+
+ synchronized (c) {
+ if (c.state != Channel.STATE_OPEN)
+ throw new IOException("Cannot request PTY on this channel ("
+ + c.getReasonClosed() + ")");
+
+ spr = new PacketSessionPtyResize(c.remoteID, term_width_characters, term_height_characters,
+ term_width_pixels, term_height_pixels);
+ c.successCounter = c.failedCounter = 0;
+ }
+
+ synchronized (c.channelSendLock) {
+ if (c.closeMessageSent)
+ throw new IOException("Cannot request PTY on this channel ("
+ + c.getReasonClosed() + ")");
+ tm.sendMessage(spr.getPayload());
+ }
+ }
+
+
+ public void requestX11(Channel c, boolean singleConnection, String x11AuthenticationProtocol,
+ String x11AuthenticationCookie, int x11ScreenNumber) throws IOException
+ {
+ PacketSessionX11Request psr;
+
+ synchronized (c)
+ {
+ if (c.state != Channel.STATE_OPEN)
+ throw new IOException("Cannot request X11 on this channel (" + c.getReasonClosed() + ")");
+
+ psr = new PacketSessionX11Request(c.remoteID, true, singleConnection, x11AuthenticationProtocol,
+ x11AuthenticationCookie, x11ScreenNumber);
+
+ c.successCounter = c.failedCounter = 0;
+ }
+
+ synchronized (c.channelSendLock)
+ {
+ if (c.closeMessageSent)
+ throw new IOException("Cannot request X11 on this channel (" + c.getReasonClosed() + ")");
+ tm.sendMessage(psr.getPayload());
+ }
+
+ if (log.isEnabled())
+ log.log(50, "Requesting X11 forwarding (Channel " + c.localID + "/" + c.remoteID + ")");
+
+ try
+ {
+ if (waitForChannelRequestResult(c) == false)
+ throw new IOException("The server denied the request.");
+ }
+ catch (IOException e)
+ {
+ throw (IOException) new IOException("The X11 request failed.").initCause(e);
+ }
+ }
+
+ public void requestSubSystem(Channel c, String subSystemName) throws IOException
+ {
+ PacketSessionSubsystemRequest ssr;
+
+ synchronized (c)
+ {
+ if (c.state != Channel.STATE_OPEN)
+ throw new IOException("Cannot request subsystem on this channel (" + c.getReasonClosed() + ")");
+
+ ssr = new PacketSessionSubsystemRequest(c.remoteID, true, subSystemName);
+
+ c.successCounter = c.failedCounter = 0;
+ }
+
+ synchronized (c.channelSendLock)
+ {
+ if (c.closeMessageSent)
+ throw new IOException("Cannot request subsystem on this channel (" + c.getReasonClosed() + ")");
+ tm.sendMessage(ssr.getPayload());
+ }
+
+ try
+ {
+ if (waitForChannelRequestResult(c) == false)
+ throw new IOException("The server denied the request.");
+ }
+ catch (IOException e)
+ {
+ throw (IOException) new IOException("The subsystem request failed.").initCause(e);
+ }
+ }
+
+ public void requestExecCommand(Channel c, String cmd) throws IOException
+ {
+ PacketSessionExecCommand sm;
+
+ synchronized (c)
+ {
+ if (c.state != Channel.STATE_OPEN)
+ throw new IOException("Cannot execute command on this channel (" + c.getReasonClosed() + ")");
+
+ sm = new PacketSessionExecCommand(c.remoteID, true, cmd);
+
+ c.successCounter = c.failedCounter = 0;
+ }
+
+ synchronized (c.channelSendLock)
+ {
+ if (c.closeMessageSent)
+ throw new IOException("Cannot execute command on this channel (" + c.getReasonClosed() + ")");
+ tm.sendMessage(sm.getPayload());
+ }
+
+ if (log.isEnabled())
+ log.log(50, "Executing command (channel " + c.localID + ", '" + cmd + "')");
+
+ try
+ {
+ if (waitForChannelRequestResult(c) == false)
+ throw new IOException("The server denied the request.");
+ }
+ catch (IOException e)
+ {
+ throw (IOException) new IOException("The execute request failed.").initCause(e);
+ }
+ }
+
+ public void requestShell(Channel c) throws IOException
+ {
+ PacketSessionStartShell sm;
+
+ synchronized (c)
+ {
+ if (c.state != Channel.STATE_OPEN)
+ throw new IOException("Cannot start shell on this channel (" + c.getReasonClosed() + ")");
+
+ sm = new PacketSessionStartShell(c.remoteID, true);
+
+ c.successCounter = c.failedCounter = 0;
+ }
+
+ synchronized (c.channelSendLock)
+ {
+ if (c.closeMessageSent)
+ throw new IOException("Cannot start shell on this channel (" + c.getReasonClosed() + ")");
+ tm.sendMessage(sm.getPayload());
+ }
+
+ try
+ {
+ if (waitForChannelRequestResult(c) == false)
+ throw new IOException("The server denied the request.");
+ }
+ catch (IOException e)
+ {
+ throw (IOException) new IOException("The shell request failed.").initCause(e);
+ }
+ }
+
+ public void msgChannelExtendedData(byte[] msg, int msglen) throws IOException
+ {
+ if (msglen <= 13)
+ throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong size (" + msglen + ")");
+
+ int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
+ int dataType = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);
+ int len = ((msg[9] & 0xff) << 24) | ((msg[10] & 0xff) << 16) | ((msg[11] & 0xff) << 8) | (msg[12] & 0xff);
+
+ Channel c = getChannel(id);
+
+ if (c == null)
+ throw new IOException("Unexpected SSH_MSG_CHANNEL_EXTENDED_DATA message for non-existent channel " + id);
+
+ if (dataType != Packets.SSH_EXTENDED_DATA_STDERR)
+ throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has unknown type (" + dataType + ")");
+
+ if (len != (msglen - 13))
+ throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong len (calculated " + (msglen - 13)
+ + ", got " + len + ")");
+
+ if (log.isEnabled())
+ log.log(80, "Got SSH_MSG_CHANNEL_EXTENDED_DATA (channel " + id + ", " + len + ")");
+
+ synchronized (c)
+ {
+ if (c.state == Channel.STATE_CLOSED)
+ return; // ignore
+
+ if (c.state != Channel.STATE_OPEN)
+ throw new IOException("Got SSH_MSG_CHANNEL_EXTENDED_DATA, but channel is not in correct state ("
+ + c.state + ")");
+
+ if (c.localWindow < len)
+ throw new IOException("Remote sent too much data, does not fit into window.");
+
+ c.localWindow -= len;
+
+ System.arraycopy(msg, 13, c.stderrBuffer, c.stderrWritepos, len);
+ c.stderrWritepos += len;
+
+ c.notifyAll();
+ }
+ }
+
+ /**
+ * Wait until for a condition.
+ *
+ * @param c
+ * Channel
+ * @param timeout
+ * in ms, 0 means no timeout.
+ * @param condition_mask
+ * minimum event mask
+ * @return all current events
+ *
+ */
+ public int waitForCondition(Channel c, long timeout, int condition_mask)
+ {
+ long end_time = 0;
+ boolean end_time_set = false;
+
+ synchronized (c)
+ {
+ while (true)
+ {
+ int current_cond = 0;
+
+ int stdoutAvail = c.stdoutWritepos - c.stdoutReadpos;
+ int stderrAvail = c.stderrWritepos - c.stderrReadpos;
+
+ if (stdoutAvail > 0)
+ current_cond = current_cond | ChannelCondition.STDOUT_DATA;
+
+ if (stderrAvail > 0)
+ current_cond = current_cond | ChannelCondition.STDERR_DATA;
+
+ if (c.EOF)
+ current_cond = current_cond | ChannelCondition.EOF;
+
+ if (c.getExitStatus() != null)
+ current_cond = current_cond | ChannelCondition.EXIT_STATUS;
+
+ if (c.getExitSignal() != null)
+ current_cond = current_cond | ChannelCondition.EXIT_SIGNAL;
+
+ if (c.state == Channel.STATE_CLOSED)
+ return current_cond | ChannelCondition.CLOSED | ChannelCondition.EOF;
+
+ if ((current_cond & condition_mask) != 0)
+ return current_cond;
+
+ if (timeout > 0)
+ {
+ if (!end_time_set)
+ {
+ end_time = System.currentTimeMillis() + timeout;
+ end_time_set = true;
+ }
+ else
+ {
+ timeout = end_time - System.currentTimeMillis();
+
+ if (timeout <= 0)
+ return current_cond | ChannelCondition.TIMEOUT;
+ }
+ }
+
+ try
+ {
+ if (timeout > 0)
+ c.wait(timeout);
+ else
+ c.wait();
+ }
+ catch (InterruptedException e)
+ {
+ }
+ }
+ }
+ }
+
+ public int getAvailable(Channel c, boolean extended) throws IOException
+ {
+ synchronized (c)
+ {
+ int avail;
+
+ if (extended)
+ avail = c.stderrWritepos - c.stderrReadpos;
+ else
+ avail = c.stdoutWritepos - c.stdoutReadpos;
+
+ return ((avail > 0) ? avail : (c.EOF ? -1 : 0));
+ }
+ }
+
+ public int getChannelData(Channel c, boolean extended, byte[] target, int off, int len) throws IOException
+ {
+ int copylen = 0;
+ int increment = 0;
+ int remoteID = 0;
+ int localID = 0;
+
+ synchronized (c)
+ {
+ int stdoutAvail = 0;
+ int stderrAvail = 0;
+
+ while (true)
+ {
+ /*
+ * Data available? We have to return remaining data even if the
+ * channel is already closed.
+ */
+
+ stdoutAvail = c.stdoutWritepos - c.stdoutReadpos;
+ stderrAvail = c.stderrWritepos - c.stderrReadpos;
+
+ if ((!extended) && (stdoutAvail != 0))
+ break;
+
+ if ((extended) && (stderrAvail != 0))
+ break;
+
+ /* Do not wait if more data will never arrive (EOF or CLOSED) */
+
+ if ((c.EOF) || (c.state != Channel.STATE_OPEN))
+ return -1;
+
+ try
+ {
+ c.wait();
+ }
+ catch (InterruptedException ignore)
+ {
+ }
+ }
+
+ /* OK, there is some data. Return it. */
+
+ if (!extended)
+ {
+ copylen = (stdoutAvail > len) ? len : stdoutAvail;
+ System.arraycopy(c.stdoutBuffer, c.stdoutReadpos, target, off, copylen);
+ c.stdoutReadpos += copylen;
+
+ if (c.stdoutReadpos != c.stdoutWritepos)
+
+ System.arraycopy(c.stdoutBuffer, c.stdoutReadpos, c.stdoutBuffer, 0, c.stdoutWritepos
+ - c.stdoutReadpos);
+
+ c.stdoutWritepos -= c.stdoutReadpos;
+ c.stdoutReadpos = 0;
+ }
+ else
+ {
+ copylen = (stderrAvail > len) ? len : stderrAvail;
+ System.arraycopy(c.stderrBuffer, c.stderrReadpos, target, off, copylen);
+ c.stderrReadpos += copylen;
+
+ if (c.stderrReadpos != c.stderrWritepos)
+
+ System.arraycopy(c.stderrBuffer, c.stderrReadpos, c.stderrBuffer, 0, c.stderrWritepos
+ - c.stderrReadpos);
+
+ c.stderrWritepos -= c.stderrReadpos;
+ c.stderrReadpos = 0;
+ }
+
+ if (c.state != Channel.STATE_OPEN)
+ return copylen;
+
+ if (c.localWindow < ((Channel.CHANNEL_BUFFER_SIZE + 1) / 2))
+ {
+ int minFreeSpace = Math.min(Channel.CHANNEL_BUFFER_SIZE - c.stdoutWritepos, Channel.CHANNEL_BUFFER_SIZE
+ - c.stderrWritepos);
+
+ increment = minFreeSpace - c.localWindow;
+ c.localWindow = minFreeSpace;
+ }
+
+ remoteID = c.remoteID; /* read while holding the lock */
+ localID = c.localID; /* read while holding the lock */
+ }
+
+ /*
+ * If a consumer reads stdout and stdin in parallel, we may end up with
+ * sending two msgWindowAdjust messages. Luckily, it
+ * does not matter in which order they arrive at the server.
+ */
+
+ if (increment > 0)
+ {
+ if (log.isEnabled())
+ log.log(80, "Sending SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + localID + ", " + increment + ")");
+
+ synchronized (c.channelSendLock)
+ {
+ byte[] msg = c.msgWindowAdjust;
+
+ msg[0] = Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST;
+ msg[1] = (byte) (remoteID >> 24);
+ msg[2] = (byte) (remoteID >> 16);
+ msg[3] = (byte) (remoteID >> 8);
+ msg[4] = (byte) (remoteID);
+ msg[5] = (byte) (increment >> 24);
+ msg[6] = (byte) (increment >> 16);
+ msg[7] = (byte) (increment >> 8);
+ msg[8] = (byte) (increment);
+
+ if (c.closeMessageSent == false)
+ tm.sendMessage(msg);
+ }
+ }
+
+ return copylen;
+ }
+
+ public void msgChannelData(byte[] msg, int msglen) throws IOException
+ {
+ if (msglen <= 9)
+ throw new IOException("SSH_MSG_CHANNEL_DATA message has wrong size (" + msglen + ")");
+
+ int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
+ int len = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);
+
+ Channel c = getChannel(id);
+
+ if (c == null)
+ throw new IOException("Unexpected SSH_MSG_CHANNEL_DATA message for non-existent channel " + id);
+
+ if (len != (msglen - 9))
+ throw new IOException("SSH_MSG_CHANNEL_DATA message has wrong len (calculated " + (msglen - 9) + ", got "
+ + len + ")");
+
+ if (log.isEnabled())
+ log.log(80, "Got SSH_MSG_CHANNEL_DATA (channel " + id + ", " + len + ")");
+
+ synchronized (c)
+ {
+ if (c.state == Channel.STATE_CLOSED)
+ return; // ignore
+
+ if (c.state != Channel.STATE_OPEN)
+ throw new IOException("Got SSH_MSG_CHANNEL_DATA, but channel is not in correct state (" + c.state + ")");
+
+ if (c.localWindow < len)
+ throw new IOException("Remote sent too much data, does not fit into window.");
+
+ c.localWindow -= len;
+
+ System.arraycopy(msg, 9, c.stdoutBuffer, c.stdoutWritepos, len);
+ c.stdoutWritepos += len;
+
+ c.notifyAll();
+ }
+ }
+
+ public void msgChannelWindowAdjust(byte[] msg, int msglen) throws IOException
+ {
+ if (msglen != 9)
+ throw new IOException("SSH_MSG_CHANNEL_WINDOW_ADJUST message has wrong size (" + msglen + ")");
+
+ int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
+ int windowChange = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);
+
+ Channel c = getChannel(id);
+
+ if (c == null)
+ throw new IOException("Unexpected SSH_MSG_CHANNEL_WINDOW_ADJUST message for non-existent channel " + id);
+
+ synchronized (c)
+ {
+ final long huge = 0xFFFFffffL; /* 2^32 - 1 */
+
+ c.remoteWindow += (windowChange & huge); /* avoid sign extension */
+
+ /* TODO - is this a good heuristic? */
+
+ if ((c.remoteWindow > huge))
+ c.remoteWindow = huge;
+
+ c.notifyAll();
+ }
+
+ if (log.isEnabled())
+ log.log(80, "Got SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + id + ", " + windowChange + ")");
+ }
+
+ public void msgChannelOpen(byte[] msg, int msglen) throws IOException
+ {
+ TypesReader tr = new TypesReader(msg, 0, msglen);
+
+ tr.readByte(); // skip packet type
+ String channelType = tr.readString();
+ int remoteID = tr.readUINT32(); /* sender channel */
+ int remoteWindow = tr.readUINT32(); /* initial window size */
+ int remoteMaxPacketSize = tr.readUINT32(); /* maximum packet size */
+
+ if ("x11".equals(channelType))
+ {
+ synchronized (x11_magic_cookies)
+ {
+ /* If we did not request X11 forwarding, then simply ignore this bogus request. */
+
+ if (x11_magic_cookies.size() == 0)
+ {
+ PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID,
+ Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED, "X11 forwarding not activated", "");
+
+ tm.sendAsynchronousMessage(pcof.getPayload());
+
+ if (log.isEnabled())
+ log.log(20, "Unexpected X11 request, denying it!");
+
+ return;
+ }
+ }
+
+ String remoteOriginatorAddress = tr.readString();
+ int remoteOriginatorPort = tr.readUINT32();
+
+ Channel c = new Channel(this);
+
+ synchronized (c)
+ {
+ c.remoteID = remoteID;
+ c.remoteWindow = remoteWindow & 0xFFFFffffL; /* properly convert UINT32 to long */
+ c.remoteMaxPacketSize = remoteMaxPacketSize;
+ c.localID = addChannel(c);
+ }
+
+ /*
+ * The open confirmation message will be sent from another thread
+ */
+
+ RemoteX11AcceptThread rxat = new RemoteX11AcceptThread(c, remoteOriginatorAddress, remoteOriginatorPort);
+ rxat.setDaemon(true);
+ rxat.start();
+
+ return;
+ }
+
+ if ("forwarded-tcpip".equals(channelType))
+ {
+ String remoteConnectedAddress = tr.readString(); /* address that was connected */
+ int remoteConnectedPort = tr.readUINT32(); /* port that was connected */
+ String remoteOriginatorAddress = tr.readString(); /* originator IP address */
+ int remoteOriginatorPort = tr.readUINT32(); /* originator port */
+
+ RemoteForwardingData rfd = null;
+
+ synchronized (remoteForwardings)
+ {
+ rfd = remoteForwardings.get(Integer.valueOf(remoteConnectedPort));
+ }
+
+ if (rfd == null)
+ {
+ PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID,
+ Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
+ "No thanks, unknown port in forwarded-tcpip request", "");
+
+ /* Always try to be polite. */
+
+ tm.sendAsynchronousMessage(pcof.getPayload());
+
+ if (log.isEnabled())
+ log.log(20, "Unexpected forwarded-tcpip request, denying it!");
+
+ return;
+ }
+
+ Channel c = new Channel(this);
+
+ synchronized (c)
+ {
+ c.remoteID = remoteID;
+ c.remoteWindow = remoteWindow & 0xFFFFffffL; /* convert UINT32 to long */
+ c.remoteMaxPacketSize = remoteMaxPacketSize;
+ c.localID = addChannel(c);
+ }
+
+ /*
+ * The open confirmation message will be sent from another thread.
+ */
+
+ RemoteAcceptThread rat = new RemoteAcceptThread(c, remoteConnectedAddress, remoteConnectedPort,
+ remoteOriginatorAddress, remoteOriginatorPort, rfd.targetAddress, rfd.targetPort);
+
+ rat.setDaemon(true);
+ rat.start();
+
+ return;
+ }
+
+ if ("auth-agent@openssh.com".equals(channelType)) {
+ Channel c = new Channel(this);
+
+ synchronized (c)
+ {
+ c.remoteID = remoteID;
+ c.remoteWindow = remoteWindow & 0xFFFFffffL; /* properly convert UINT32 to long */
+ c.remoteMaxPacketSize = remoteMaxPacketSize;
+ c.localID = addChannel(c);
+ }
+
+ AuthAgentForwardThread aat = new AuthAgentForwardThread(c, authAgent);
+
+ aat.setDaemon(true);
+ aat.start();
+
+ return;
+ }
+
+ /* Tell the server that we have no idea what it is talking about */
+
+ PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID, Packets.SSH_OPEN_UNKNOWN_CHANNEL_TYPE,
+ "Unknown channel type", "");
+
+ tm.sendAsynchronousMessage(pcof.getPayload());
+
+ if (log.isEnabled())
+ log.log(20, "The peer tried to open an unsupported channel type (" + channelType + ")");
+ }
+
+ public void msgChannelRequest(byte[] msg, int msglen) throws IOException
+ {
+ TypesReader tr = new TypesReader(msg, 0, msglen);
+
+ tr.readByte(); // skip packet type
+ int id = tr.readUINT32();
+
+ Channel c = getChannel(id);
+
+ if (c == null)
+ throw new IOException("Unexpected SSH_MSG_CHANNEL_REQUEST message for non-existent channel " + id);
+
+ String type = tr.readString("US-ASCII");
+ boolean wantReply = tr.readBoolean();
+
+ if (log.isEnabled())
+ log.log(80, "Got SSH_MSG_CHANNEL_REQUEST (channel " + id + ", '" + type + "')");
+
+ if (type.equals("exit-status"))
+ {
+ if (wantReply != false)
+ throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message, 'want reply' is true");
+
+ int exit_status = tr.readUINT32();
+
+ if (tr.remain() != 0)
+ throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
+
+ synchronized (c)
+ {
+ c.exit_status = Integer.valueOf(exit_status);
+ c.notifyAll();
+ }
+
+ if (log.isEnabled())
+ log.log(50, "Got EXIT STATUS (channel " + id + ", status " + exit_status + ")");
+
+ return;
+ }
+
+ if (type.equals("exit-signal"))
+ {
+ if (wantReply != false)
+ throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message, 'want reply' is true");
+
+ String signame = tr.readString("US-ASCII");
+ tr.readBoolean();
+ tr.readString();
+ tr.readString();
+
+ if (tr.remain() != 0)
+ throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
+
+ synchronized (c)
+ {
+ c.exit_signal = signame;
+ c.notifyAll();
+ }
+
+ if (log.isEnabled())
+ log.log(50, "Got EXIT SIGNAL (channel " + id + ", signal " + signame + ")");
+
+ return;
+ }
+
+ /* We simply ignore unknown channel requests, however, if the server wants a reply,
+ * then we signal that we have no idea what it is about.
+ */
+
+ if (wantReply)
+ {
+ byte[] reply = new byte[5];
+
+ reply[0] = Packets.SSH_MSG_CHANNEL_FAILURE;
+ reply[1] = (byte) (c.remoteID >> 24);
+ reply[2] = (byte) (c.remoteID >> 16);
+ reply[3] = (byte) (c.remoteID >> 8);
+ reply[4] = (byte) (c.remoteID);
+
+ tm.sendAsynchronousMessage(reply);
+ }
+
+ if (log.isEnabled())
+ log.log(50, "Channel request '" + type + "' is not known, ignoring it");
+ }
+
+ public void msgChannelEOF(byte[] msg, int msglen) throws IOException
+ {
+ if (msglen != 5)
+ throw new IOException("SSH_MSG_CHANNEL_EOF message has wrong size (" + msglen + ")");
+
+ int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
+
+ Channel c = getChannel(id);
+
+ if (c == null)
+ throw new IOException("Unexpected SSH_MSG_CHANNEL_EOF message for non-existent channel " + id);
+
+ synchronized (c)
+ {
+ c.EOF = true;
+ c.notifyAll();
+ }
+
+ if (log.isEnabled())
+ log.log(50, "Got SSH_MSG_CHANNEL_EOF (channel " + id + ")");
+ }
+
+ public void msgChannelClose(byte[] msg, int msglen) throws IOException
+ {
+ if (msglen != 5)
+ throw new IOException("SSH_MSG_CHANNEL_CLOSE message has wrong size (" + msglen + ")");
+
+ int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
+
+ Channel c = getChannel(id);
+
+ if (c == null)
+ throw new IOException("Unexpected SSH_MSG_CHANNEL_CLOSE message for non-existent channel " + id);
+
+ synchronized (c)
+ {
+ c.EOF = true;
+ c.state = Channel.STATE_CLOSED;
+ c.setReasonClosed("Close requested by remote");
+ c.closeMessageRecv = true;
+
+ removeChannel(c.localID);
+
+ c.notifyAll();
+ }
+
+ if (log.isEnabled())
+ log.log(50, "Got SSH_MSG_CHANNEL_CLOSE (channel " + id + ")");
+ }
+
+ public void msgChannelSuccess(byte[] msg, int msglen) throws IOException
+ {
+ if (msglen != 5)
+ throw new IOException("SSH_MSG_CHANNEL_SUCCESS message has wrong size (" + msglen + ")");
+
+ int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
+
+ Channel c = getChannel(id);
+
+ if (c == null)
+ throw new IOException("Unexpected SSH_MSG_CHANNEL_SUCCESS message for non-existent channel " + id);
+
+ synchronized (c)
+ {
+ c.successCounter++;
+ c.notifyAll();
+ }
+
+ if (log.isEnabled())
+ log.log(80, "Got SSH_MSG_CHANNEL_SUCCESS (channel " + id + ")");
+ }
+
+ public void msgChannelFailure(byte[] msg, int msglen) throws IOException
+ {
+ if (msglen != 5)
+ throw new IOException("SSH_MSG_CHANNEL_FAILURE message has wrong size (" + msglen + ")");
+
+ int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
+
+ Channel c = getChannel(id);
+
+ if (c == null)
+ throw new IOException("Unexpected SSH_MSG_CHANNEL_FAILURE message for non-existent channel " + id);
+
+ synchronized (c)
+ {
+ c.failedCounter++;
+ c.notifyAll();
+ }
+
+ if (log.isEnabled())
+ log.log(50, "Got SSH_MSG_CHANNEL_FAILURE (channel " + id + ")");
+ }
+
+ public void msgChannelOpenConfirmation(byte[] msg, int msglen) throws IOException
+ {
+ PacketChannelOpenConfirmation sm = new PacketChannelOpenConfirmation(msg, 0, msglen);
+
+ Channel c = getChannel(sm.recipientChannelID);
+
+ if (c == null)
+ throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for non-existent channel "
+ + sm.recipientChannelID);
+
+ synchronized (c)
+ {
+ if (c.state != Channel.STATE_OPENING)
+ throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for channel "
+ + sm.recipientChannelID);
+
+ c.remoteID = sm.senderChannelID;
+ c.remoteWindow = sm.initialWindowSize & 0xFFFFffffL; /* convert UINT32 to long */
+ c.remoteMaxPacketSize = sm.maxPacketSize;
+ c.state = Channel.STATE_OPEN;
+ c.notifyAll();
+ }
+
+ if (log.isEnabled())
+ log.log(50, "Got SSH_MSG_CHANNEL_OPEN_CONFIRMATION (channel " + sm.recipientChannelID + " / remote: "
+ + sm.senderChannelID + ")");
+ }
+
+ public void msgChannelOpenFailure(byte[] msg, int msglen) throws IOException
+ {
+ if (msglen < 5)
+ throw new IOException("SSH_MSG_CHANNEL_OPEN_FAILURE message has wrong size (" + msglen + ")");
+
+ TypesReader tr = new TypesReader(msg, 0, msglen);
+
+ tr.readByte(); // skip packet type
+ int id = tr.readUINT32(); /* sender channel */
+
+ Channel c = getChannel(id);
+
+ if (c == null)
+ throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_FAILURE message for non-existent channel " + id);
+
+ int reasonCode = tr.readUINT32();
+ String description = tr.readString("UTF-8");
+
+ String reasonCodeSymbolicName = null;
+
+ switch (reasonCode)
+ {
+ case 1:
+ reasonCodeSymbolicName = "SSH_OPEN_ADMINISTRATIVELY_PROHIBITED";
+ break;
+ case 2:
+ reasonCodeSymbolicName = "SSH_OPEN_CONNECT_FAILED";
+ break;
+ case 3:
+ reasonCodeSymbolicName = "SSH_OPEN_UNKNOWN_CHANNEL_TYPE";
+ break;
+ case 4:
+ reasonCodeSymbolicName = "SSH_OPEN_RESOURCE_SHORTAGE";
+ break;
+ default:
+ reasonCodeSymbolicName = "UNKNOWN REASON CODE (" + reasonCode + ")";
+ }
+
+ StringBuffer descriptionBuffer = new StringBuffer();
+ descriptionBuffer.append(description);
+
+ for (int i = 0; i < descriptionBuffer.length(); i++)
+ {
+ char cc = descriptionBuffer.charAt(i);
+
+ if ((cc >= 32) && (cc <= 126))
+ continue;
+ descriptionBuffer.setCharAt(i, '\uFFFD');
+ }
+
+ synchronized (c)
+ {
+ c.EOF = true;
+ c.state = Channel.STATE_CLOSED;
+ c.setReasonClosed("The server refused to open the channel (" + reasonCodeSymbolicName + ", '"
+ + descriptionBuffer.toString() + "')");
+ c.notifyAll();
+ }
+
+ if (log.isEnabled())
+ log.log(50, "Got SSH_MSG_CHANNEL_OPEN_FAILURE (channel " + id + ")");
+ }
+
+ public void msgGlobalRequest(byte[] msg, int msglen) throws IOException
+ {
+ /* Currently we do not support any kind of global request */
+
+ TypesReader tr = new TypesReader(msg, 0, msglen);
+
+ tr.readByte(); // skip packet type
+ String requestName = tr.readString();
+ boolean wantReply = tr.readBoolean();
+
+ if (wantReply)
+ {
+ byte[] reply_failure = new byte[1];
+ reply_failure[0] = Packets.SSH_MSG_REQUEST_FAILURE;
+
+ tm.sendAsynchronousMessage(reply_failure);
+ }
+
+ /* We do not clean up the requestName String - that is OK for debug */
+
+ if (log.isEnabled())
+ log.log(80, "Got SSH_MSG_GLOBAL_REQUEST (" + requestName + ")");
+ }
+
+ public void msgGlobalSuccess() throws IOException
+ {
+ synchronized (channels)
+ {
+ globalSuccessCounter++;
+ channels.notifyAll();
+ }
+
+ if (log.isEnabled())
+ log.log(80, "Got SSH_MSG_REQUEST_SUCCESS");
+ }
+
+ public void msgGlobalFailure() throws IOException
+ {
+ synchronized (channels)
+ {
+ globalFailedCounter++;
+ channels.notifyAll();
+ }
+
+ if (log.isEnabled())
+ log.log(80, "Got SSH_MSG_REQUEST_FAILURE");
+ }
+
+ public void handleMessage(byte[] msg, int msglen) throws IOException
+ {
+ if (msg == null)
+ {
+ if (log.isEnabled())
+ log.log(50, "HandleMessage: got shutdown");
+
+ synchronized (listenerThreads)
+ {
+ for (int i = 0; i < listenerThreads.size(); i++)
+ {
+ IChannelWorkerThread lat = listenerThreads.elementAt(i);
+ lat.stopWorking();
+ }
+ listenerThreadsAllowed = false;
+ }
+
+ synchronized (channels)
+ {
+ shutdown = true;
+
+ for (int i = 0; i < channels.size(); i++)
+ {
+ Channel c = channels.elementAt(i);
+ synchronized (c)
+ {
+ c.EOF = true;
+ c.state = Channel.STATE_CLOSED;
+ c.setReasonClosed("The connection is being shutdown");
+ c.closeMessageRecv = true; /*
+ * You never know, perhaps
+ * we are waiting for a
+ * pending close message
+ * from the server...
+ */
+ c.notifyAll();
+ }
+ }
+ /* Works with J2ME */
+ channels.setSize(0);
+ channels.trimToSize();
+ channels.notifyAll(); /* Notify global response waiters */
+ return;
+ }
+ }
+
+ switch (msg[0])
+ {
+ case Packets.SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
+ msgChannelOpenConfirmation(msg, msglen);
+ break;
+ case Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST:
+ msgChannelWindowAdjust(msg, msglen);
+ break;
+ case Packets.SSH_MSG_CHANNEL_DATA:
+ msgChannelData(msg, msglen);
+ break;
+ case Packets.SSH_MSG_CHANNEL_EXTENDED_DATA:
+ msgChannelExtendedData(msg, msglen);
+ break;
+ case Packets.SSH_MSG_CHANNEL_REQUEST:
+ msgChannelRequest(msg, msglen);
+ break;
+ case Packets.SSH_MSG_CHANNEL_EOF:
+ msgChannelEOF(msg, msglen);
+ break;
+ case Packets.SSH_MSG_CHANNEL_OPEN:
+ msgChannelOpen(msg, msglen);
+ break;
+ case Packets.SSH_MSG_CHANNEL_CLOSE:
+ msgChannelClose(msg, msglen);
+ break;
+ case Packets.SSH_MSG_CHANNEL_SUCCESS:
+ msgChannelSuccess(msg, msglen);
+ break;
+ case Packets.SSH_MSG_CHANNEL_FAILURE:
+ msgChannelFailure(msg, msglen);
+ break;
+ case Packets.SSH_MSG_CHANNEL_OPEN_FAILURE:
+ msgChannelOpenFailure(msg, msglen);
+ break;
+ case Packets.SSH_MSG_GLOBAL_REQUEST:
+ msgGlobalRequest(msg, msglen);
+ break;
+ case Packets.SSH_MSG_REQUEST_SUCCESS:
+ msgGlobalSuccess();
+ break;
+ case Packets.SSH_MSG_REQUEST_FAILURE:
+ msgGlobalFailure();
+ break;
+ default:
+ throw new IOException("Cannot handle unknown channel message " + (msg[0] & 0xff));
+ }
+ }
+}
diff --git a/src/com/trilead/ssh2/channel/ChannelOutputStream.java b/src/com/trilead/ssh2/channel/ChannelOutputStream.java
index 30e8fd8..c1d56e8 100644
--- a/src/com/trilead/ssh2/channel/ChannelOutputStream.java
+++ b/src/com/trilead/ssh2/channel/ChannelOutputStream.java
@@ -1,71 +1,71 @@
-package com.trilead.ssh2.channel;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * ChannelOutputStream.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: ChannelOutputStream.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public final class ChannelOutputStream extends OutputStream
-{
- Channel c;
-
- private byte[] writeBuffer;
-
- boolean isClosed = false;
-
- ChannelOutputStream(Channel c)
- {
- this.c = c;
- writeBuffer = new byte[1];
- }
-
- public void write(int b) throws IOException
- {
- writeBuffer[0] = (byte) b;
-
- write(writeBuffer, 0, 1);
- }
-
- public void close() throws IOException
- {
- if (isClosed == false)
- {
- isClosed = true;
- c.cm.sendEOF(c);
- }
- }
-
- public void flush() throws IOException
- {
- if (isClosed)
- throw new IOException("This OutputStream is closed.");
-
- /* This is a no-op, since this stream is unbuffered */
- }
-
- public void write(byte[] b, int off, int len) throws IOException
- {
- if (isClosed)
- throw new IOException("This OutputStream is closed.");
-
- if (b == null)
- throw new NullPointerException();
-
- if ((off < 0) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0) || (off > b.length))
- throw new IndexOutOfBoundsException();
-
- if (len == 0)
- return;
-
- c.cm.sendData(c, b, off, len);
- }
-
- public void write(byte[] b) throws IOException
- {
- write(b, 0, b.length);
- }
-}
+package com.trilead.ssh2.channel;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * ChannelOutputStream.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: ChannelOutputStream.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+public final class ChannelOutputStream extends OutputStream
+{
+ Channel c;
+
+ private byte[] writeBuffer;
+
+ boolean isClosed = false;
+
+ ChannelOutputStream(Channel c)
+ {
+ this.c = c;
+ writeBuffer = new byte[1];
+ }
+
+ public void write(int b) throws IOException
+ {
+ writeBuffer[0] = (byte) b;
+
+ write(writeBuffer, 0, 1);
+ }
+
+ public void close() throws IOException
+ {
+ if (isClosed == false)
+ {
+ isClosed = true;
+ c.cm.sendEOF(c);
+ }
+ }
+
+ public void flush() throws IOException
+ {
+ if (isClosed)
+ throw new IOException("This OutputStream is closed.");
+
+ /* This is a no-op, since this stream is unbuffered */
+ }
+
+ public void write(byte[] b, int off, int len) throws IOException
+ {
+ if (isClosed)
+ throw new IOException("This OutputStream is closed.");
+
+ if (b == null)
+ throw new NullPointerException();
+
+ if ((off < 0) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0) || (off > b.length))
+ throw new IndexOutOfBoundsException();
+
+ if (len == 0)
+ return;
+
+ c.cm.sendData(c, b, off, len);
+ }
+
+ public void write(byte[] b) throws IOException
+ {
+ write(b, 0, b.length);
+ }
+}
diff --git a/src/com/trilead/ssh2/channel/DynamicAcceptThread.java b/src/com/trilead/ssh2/channel/DynamicAcceptThread.java
index f3c8b07..ef3a3d0 100644
--- a/src/com/trilead/ssh2/channel/DynamicAcceptThread.java
+++ b/src/com/trilead/ssh2/channel/DynamicAcceptThread.java
@@ -202,7 +202,7 @@ public class DynamicAcceptThread extends Thread implements IChannelWorkerThread
}
try {
- r2l = new StreamForwarder(cn, null, null, cn.stdoutStream, out, "RemoteToLocal");
+ r2l = new StreamForwarder(cn, null, sock, cn.stdoutStream, out, "RemoteToLocal");
l2r = new StreamForwarder(cn, r2l, sock, in, cn.stdinStream, "LocalToRemote");
} catch (IOException e) {
try {
diff --git a/src/com/trilead/ssh2/channel/IChannelWorkerThread.java b/src/com/trilead/ssh2/channel/IChannelWorkerThread.java
index c085421..bce9b1b 100644
--- a/src/com/trilead/ssh2/channel/IChannelWorkerThread.java
+++ b/src/com/trilead/ssh2/channel/IChannelWorkerThread.java
@@ -1,13 +1,13 @@
-
-package com.trilead.ssh2.channel;
-
-/**
- * IChannelWorkerThread.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: IChannelWorkerThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-interface IChannelWorkerThread
-{
- public void stopWorking();
-}
+
+package com.trilead.ssh2.channel;
+
+/**
+ * IChannelWorkerThread.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: IChannelWorkerThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+interface IChannelWorkerThread
+{
+ public void stopWorking();
+}
diff --git a/src/com/trilead/ssh2/channel/LocalAcceptThread.java b/src/com/trilead/ssh2/channel/LocalAcceptThread.java
index 1b08d9c..0d1bb35 100644
--- a/src/com/trilead/ssh2/channel/LocalAcceptThread.java
+++ b/src/com/trilead/ssh2/channel/LocalAcceptThread.java
@@ -1,135 +1,135 @@
-
-package com.trilead.ssh2.channel;
-
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.net.ServerSocket;
-import java.net.Socket;
-
-/**
- * LocalAcceptThread.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: LocalAcceptThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public class LocalAcceptThread extends Thread implements IChannelWorkerThread
-{
- ChannelManager cm;
- String host_to_connect;
- int port_to_connect;
-
- final ServerSocket ss;
-
- public LocalAcceptThread(ChannelManager cm, int local_port, String host_to_connect, int port_to_connect)
- throws IOException
- {
- this.cm = cm;
- this.host_to_connect = host_to_connect;
- this.port_to_connect = port_to_connect;
-
- ss = new ServerSocket(local_port);
- }
-
- public LocalAcceptThread(ChannelManager cm, InetSocketAddress localAddress, String host_to_connect,
- int port_to_connect) throws IOException
- {
- this.cm = cm;
- this.host_to_connect = host_to_connect;
- this.port_to_connect = port_to_connect;
-
- ss = new ServerSocket();
- ss.bind(localAddress);
- }
-
- public void run()
- {
- try
- {
- cm.registerThread(this);
- }
- catch (IOException e)
- {
- stopWorking();
- return;
- }
-
- while (true)
- {
- Socket s = null;
-
- try
- {
- s = ss.accept();
- }
- catch (IOException e)
- {
- stopWorking();
- return;
- }
-
- Channel cn = null;
- StreamForwarder r2l = null;
- StreamForwarder l2r = null;
-
- try
- {
- /* This may fail, e.g., if the remote port is closed (in optimistic terms: not open yet) */
-
- cn = cm.openDirectTCPIPChannel(host_to_connect, port_to_connect, s.getInetAddress().getHostAddress(), s
- .getPort());
-
- }
- catch (IOException e)
- {
- /* Simply close the local socket and wait for the next incoming connection */
-
- try
- {
- s.close();
- }
- catch (IOException ignore)
- {
- }
-
- continue;
- }
-
- try
- {
- r2l = new StreamForwarder(cn, null, null, cn.stdoutStream, s.getOutputStream(), "RemoteToLocal");
- l2r = new StreamForwarder(cn, r2l, s, s.getInputStream(), cn.stdinStream, "LocalToRemote");
- }
- catch (IOException e)
- {
- try
- {
- /* This message is only visible during debugging, since we discard the channel immediatelly */
- cn.cm.closeChannel(cn, "Weird error during creation of StreamForwarder (" + e.getMessage() + ")",
- true);
- }
- catch (IOException ignore)
- {
- }
-
- continue;
- }
-
- r2l.setDaemon(true);
- l2r.setDaemon(true);
- r2l.start();
- l2r.start();
- }
- }
-
- public void stopWorking()
- {
- try
- {
- /* This will lead to an IOException in the ss.accept() call */
- ss.close();
- }
- catch (IOException e)
- {
- }
- }
-}
+
+package com.trilead.ssh2.channel;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+/**
+ * LocalAcceptThread.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: LocalAcceptThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+public class LocalAcceptThread extends Thread implements IChannelWorkerThread
+{
+ ChannelManager cm;
+ String host_to_connect;
+ int port_to_connect;
+
+ final ServerSocket ss;
+
+ public LocalAcceptThread(ChannelManager cm, int local_port, String host_to_connect, int port_to_connect)
+ throws IOException
+ {
+ this.cm = cm;
+ this.host_to_connect = host_to_connect;
+ this.port_to_connect = port_to_connect;
+
+ ss = new ServerSocket(local_port);
+ }
+
+ public LocalAcceptThread(ChannelManager cm, InetSocketAddress localAddress, String host_to_connect,
+ int port_to_connect) throws IOException
+ {
+ this.cm = cm;
+ this.host_to_connect = host_to_connect;
+ this.port_to_connect = port_to_connect;
+
+ ss = new ServerSocket();
+ ss.bind(localAddress);
+ }
+
+ public void run()
+ {
+ try
+ {
+ cm.registerThread(this);
+ }
+ catch (IOException e)
+ {
+ stopWorking();
+ return;
+ }
+
+ while (true)
+ {
+ Socket s = null;
+
+ try
+ {
+ s = ss.accept();
+ }
+ catch (IOException e)
+ {
+ stopWorking();
+ return;
+ }
+
+ Channel cn = null;
+ StreamForwarder r2l = null;
+ StreamForwarder l2r = null;
+
+ try
+ {
+ /* This may fail, e.g., if the remote port is closed (in optimistic terms: not open yet) */
+
+ cn = cm.openDirectTCPIPChannel(host_to_connect, port_to_connect, s.getInetAddress().getHostAddress(), s
+ .getPort());
+
+ }
+ catch (IOException e)
+ {
+ /* Simply close the local socket and wait for the next incoming connection */
+
+ try
+ {
+ s.close();
+ }
+ catch (IOException ignore)
+ {
+ }
+
+ continue;
+ }
+
+ try
+ {
+ r2l = new StreamForwarder(cn, null, s, cn.stdoutStream, s.getOutputStream(), "RemoteToLocal");
+ l2r = new StreamForwarder(cn, r2l, s, s.getInputStream(), cn.stdinStream, "LocalToRemote");
+ }
+ catch (IOException e)
+ {
+ try
+ {
+ /* This message is only visible during debugging, since we discard the channel immediatelly */
+ cn.cm.closeChannel(cn, "Weird error during creation of StreamForwarder (" + e.getMessage() + ")",
+ true);
+ }
+ catch (IOException ignore)
+ {
+ }
+
+ continue;
+ }
+
+ r2l.setDaemon(true);
+ l2r.setDaemon(true);
+ r2l.start();
+ l2r.start();
+ }
+ }
+
+ public void stopWorking()
+ {
+ try
+ {
+ /* This will lead to an IOException in the ss.accept() call */
+ ss.close();
+ }
+ catch (IOException e)
+ {
+ }
+ }
+}
diff --git a/src/com/trilead/ssh2/channel/RemoteAcceptThread.java b/src/com/trilead/ssh2/channel/RemoteAcceptThread.java
index 1ca9d76..29b02b8 100644
--- a/src/com/trilead/ssh2/channel/RemoteAcceptThread.java
+++ b/src/com/trilead/ssh2/channel/RemoteAcceptThread.java
@@ -1,103 +1,103 @@
-
-package com.trilead.ssh2.channel;
-
-import java.io.IOException;
-import java.net.Socket;
-
-import com.trilead.ssh2.log.Logger;
-
-
-/**
- * RemoteAcceptThread.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: RemoteAcceptThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public class RemoteAcceptThread extends Thread
-{
- private static final Logger log = Logger.getLogger(RemoteAcceptThread.class);
-
- Channel c;
-
- String remoteConnectedAddress;
- int remoteConnectedPort;
- String remoteOriginatorAddress;
- int remoteOriginatorPort;
- String targetAddress;
- int targetPort;
-
- Socket s;
-
- public RemoteAcceptThread(Channel c, String remoteConnectedAddress, int remoteConnectedPort,
- String remoteOriginatorAddress, int remoteOriginatorPort, String targetAddress, int targetPort)
- {
- this.c = c;
- this.remoteConnectedAddress = remoteConnectedAddress;
- this.remoteConnectedPort = remoteConnectedPort;
- this.remoteOriginatorAddress = remoteOriginatorAddress;
- this.remoteOriginatorPort = remoteOriginatorPort;
- this.targetAddress = targetAddress;
- this.targetPort = targetPort;
-
- if (log.isEnabled())
- log.log(20, "RemoteAcceptThread: " + remoteConnectedAddress + "/" + remoteConnectedPort + ", R: "
- + remoteOriginatorAddress + "/" + remoteOriginatorPort);
- }
-
- public void run()
- {
- try
- {
- c.cm.sendOpenConfirmation(c);
-
- s = new Socket(targetAddress, targetPort);
-
- StreamForwarder r2l = new StreamForwarder(c, null, null, c.getStdoutStream(), s.getOutputStream(),
- "RemoteToLocal");
- StreamForwarder l2r = new StreamForwarder(c, null, null, s.getInputStream(), c.getStdinStream(),
- "LocalToRemote");
-
- /* No need to start two threads, one can be executed in the current thread */
-
- r2l.setDaemon(true);
- r2l.start();
- l2r.run();
-
- while (r2l.isAlive())
- {
- try
- {
- r2l.join();
- }
- catch (InterruptedException e)
- {
- }
- }
-
- /* If the channel is already closed, then this is a no-op */
-
- c.cm.closeChannel(c, "EOF on both streams reached.", true);
- s.close();
- }
- catch (IOException e)
- {
- log.log(50, "IOException in proxy code: " + e.getMessage());
-
- try
- {
- c.cm.closeChannel(c, "IOException in proxy code (" + e.getMessage() + ")", true);
- }
- catch (IOException e1)
- {
- }
- try
- {
- if (s != null)
- s.close();
- }
- catch (IOException e1)
- {
- }
- }
- }
-}
+
+package com.trilead.ssh2.channel;
+
+import java.io.IOException;
+import java.net.Socket;
+
+import com.trilead.ssh2.log.Logger;
+
+
+/**
+ * RemoteAcceptThread.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: RemoteAcceptThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+public class RemoteAcceptThread extends Thread
+{
+ private static final Logger log = Logger.getLogger(RemoteAcceptThread.class);
+
+ Channel c;
+
+ String remoteConnectedAddress;
+ int remoteConnectedPort;
+ String remoteOriginatorAddress;
+ int remoteOriginatorPort;
+ String targetAddress;
+ int targetPort;
+
+ Socket s;
+
+ public RemoteAcceptThread(Channel c, String remoteConnectedAddress, int remoteConnectedPort,
+ String remoteOriginatorAddress, int remoteOriginatorPort, String targetAddress, int targetPort)
+ {
+ this.c = c;
+ this.remoteConnectedAddress = remoteConnectedAddress;
+ this.remoteConnectedPort = remoteConnectedPort;
+ this.remoteOriginatorAddress = remoteOriginatorAddress;
+ this.remoteOriginatorPort = remoteOriginatorPort;
+ this.targetAddress = targetAddress;
+ this.targetPort = targetPort;
+
+ if (log.isEnabled())
+ log.log(20, "RemoteAcceptThread: " + remoteConnectedAddress + "/" + remoteConnectedPort + ", R: "
+ + remoteOriginatorAddress + "/" + remoteOriginatorPort);
+ }
+
+ public void run()
+ {
+ try
+ {
+ c.cm.sendOpenConfirmation(c);
+
+ s = new Socket(targetAddress, targetPort);
+
+ StreamForwarder r2l = new StreamForwarder(c, null, s, c.getStdoutStream(), s.getOutputStream(),
+ "RemoteToLocal");
+ StreamForwarder l2r = new StreamForwarder(c, null, null, s.getInputStream(), c.getStdinStream(),
+ "LocalToRemote");
+
+ /* No need to start two threads, one can be executed in the current thread */
+
+ r2l.setDaemon(true);
+ r2l.start();
+ l2r.run();
+
+ while (r2l.isAlive())
+ {
+ try
+ {
+ r2l.join();
+ }
+ catch (InterruptedException e)
+ {
+ }
+ }
+
+ /* If the channel is already closed, then this is a no-op */
+
+ c.cm.closeChannel(c, "EOF on both streams reached.", true);
+ s.close();
+ }
+ catch (IOException e)
+ {
+ log.log(50, "IOException in proxy code: " + e.getMessage());
+
+ try
+ {
+ c.cm.closeChannel(c, "IOException in proxy code (" + e.getMessage() + ")", true);
+ }
+ catch (IOException e1)
+ {
+ }
+ try
+ {
+ if (s != null)
+ s.close();
+ }
+ catch (IOException e1)
+ {
+ }
+ }
+ }
+}
diff --git a/src/com/trilead/ssh2/channel/RemoteForwardingData.java b/src/com/trilead/ssh2/channel/RemoteForwardingData.java
index 0bafce2..d05378e 100644
--- a/src/com/trilead/ssh2/channel/RemoteForwardingData.java
+++ b/src/com/trilead/ssh2/channel/RemoteForwardingData.java
@@ -1,17 +1,17 @@
-
-package com.trilead.ssh2.channel;
-
-/**
- * RemoteForwardingData. Data about a requested remote forwarding.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: RemoteForwardingData.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public class RemoteForwardingData
-{
- public String bindAddress;
- public int bindPort;
-
- String targetAddress;
- int targetPort;
-}
+
+package com.trilead.ssh2.channel;
+
+/**
+ * RemoteForwardingData. Data about a requested remote forwarding.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: RemoteForwardingData.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+public class RemoteForwardingData
+{
+ public String bindAddress;
+ public int bindPort;
+
+ String targetAddress;
+ int targetPort;
+}
diff --git a/src/com/trilead/ssh2/channel/RemoteX11AcceptThread.java b/src/com/trilead/ssh2/channel/RemoteX11AcceptThread.java
index 8ee05a2..9f99410 100644
--- a/src/com/trilead/ssh2/channel/RemoteX11AcceptThread.java
+++ b/src/com/trilead/ssh2/channel/RemoteX11AcceptThread.java
@@ -1,240 +1,240 @@
-
-package com.trilead.ssh2.channel;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.Socket;
-
-import com.trilead.ssh2.log.Logger;
-
-
-/**
- * RemoteX11AcceptThread.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: RemoteX11AcceptThread.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
- */
-public class RemoteX11AcceptThread extends Thread
-{
- private static final Logger log = Logger.getLogger(RemoteX11AcceptThread.class);
-
- Channel c;
-
- String remoteOriginatorAddress;
- int remoteOriginatorPort;
-
- Socket s;
-
- public RemoteX11AcceptThread(Channel c, String remoteOriginatorAddress, int remoteOriginatorPort)
- {
- this.c = c;
- this.remoteOriginatorAddress = remoteOriginatorAddress;
- this.remoteOriginatorPort = remoteOriginatorPort;
- }
-
- public void run()
- {
- try
- {
- /* Send Open Confirmation */
-
- c.cm.sendOpenConfirmation(c);
-
- /* Read startup packet from client */
-
- OutputStream remote_os = c.getStdinStream();
- InputStream remote_is = c.getStdoutStream();
-
- /* The following code is based on the protocol description given in:
- * Scheifler/Gettys,
- * X Windows System: Core and Extension Protocols:
- * X Version 11, Releases 6 and 6.1 ISBN 1-55558-148-X
- */
-
- /*
- * Client startup:
- *
- * 1 0X42 MSB first/0x6c lSB first - byteorder
- * 1 - unused
- * 2 card16 - protocol-major-version
- * 2 card16 - protocol-minor-version
- * 2 n - lenght of authorization-protocol-name
- * 2 d - lenght of authorization-protocol-data
- * 2 - unused
- * string8 - authorization-protocol-name
- * p - unused, p=pad(n)
- * string8 - authorization-protocol-data
- * q - unused, q=pad(d)
- *
- * pad(X) = (4 - (X mod 4)) mod 4
- *
- * Server response:
- *
- * 1 (0 failed, 2 authenticate, 1 success)
- * ...
- *
- */
-
- /* Later on we will simply forward the first 6 header bytes to the "real" X11 server */
-
- byte[] header = new byte[6];
-
- if (remote_is.read(header) != 6)
- throw new IOException("Unexpected EOF on X11 startup!");
-
- if ((header[0] != 0x42) && (header[0] != 0x6c)) // 0x42 MSB first, 0x6C LSB first
- throw new IOException("Unknown endian format in X11 message!");
-
- /* Yes, I came up with this myself - shall I file an application for a patent? =) */
-
- int idxMSB = (header[0] == 0x42) ? 0 : 1;
-
- /* Read authorization data header */
-
- byte[] auth_buff = new byte[6];
-
- if (remote_is.read(auth_buff) != 6)
- throw new IOException("Unexpected EOF on X11 startup!");
-
- int authProtocolNameLength = ((auth_buff[idxMSB] & 0xff) << 8) | (auth_buff[1 - idxMSB] & 0xff);
- int authProtocolDataLength = ((auth_buff[2 + idxMSB] & 0xff) << 8) | (auth_buff[3 - idxMSB] & 0xff);
-
- if ((authProtocolNameLength > 256) || (authProtocolDataLength > 256))
- throw new IOException("Buggy X11 authorization data");
-
- int authProtocolNamePadding = ((4 - (authProtocolNameLength % 4)) % 4);
- int authProtocolDataPadding = ((4 - (authProtocolDataLength % 4)) % 4);
-
- byte[] authProtocolName = new byte[authProtocolNameLength];
- byte[] authProtocolData = new byte[authProtocolDataLength];
-
- byte[] paddingBuffer = new byte[4];
-
- if (remote_is.read(authProtocolName) != authProtocolNameLength)
- throw new IOException("Unexpected EOF on X11 startup! (authProtocolName)");
-
- if (remote_is.read(paddingBuffer, 0, authProtocolNamePadding) != authProtocolNamePadding)
- throw new IOException("Unexpected EOF on X11 startup! (authProtocolNamePadding)");
-
- if (remote_is.read(authProtocolData) != authProtocolDataLength)
- throw new IOException("Unexpected EOF on X11 startup! (authProtocolData)");
-
- if (remote_is.read(paddingBuffer, 0, authProtocolDataPadding) != authProtocolDataPadding)
- throw new IOException("Unexpected EOF on X11 startup! (authProtocolDataPadding)");
-
- if ("MIT-MAGIC-COOKIE-1".equals(new String(authProtocolName, "ISO-8859-1")) == false)
- throw new IOException("Unknown X11 authorization protocol!");
-
- if (authProtocolDataLength != 16)
- throw new IOException("Wrong data length for X11 authorization data!");
-
- StringBuffer tmp = new StringBuffer(32);
- for (int i = 0; i < authProtocolData.length; i++)
- {
- String digit2 = Integer.toHexString(authProtocolData[i] & 0xff);
- tmp.append((digit2.length() == 2) ? digit2 : "0" + digit2);
- }
- String hexEncodedFakeCookie = tmp.toString();
-
- /* Order is very important here - it may be that a certain x11 forwarding
- * gets disabled right in the moment when we check and register our connection
- * */
-
- synchronized (c)
- {
- /* Please read the comment in Channel.java */
- c.hexX11FakeCookie = hexEncodedFakeCookie;
- }
-
- /* Now check our fake cookie directory to see if we produced this cookie */
-
- X11ServerData sd = c.cm.checkX11Cookie(hexEncodedFakeCookie);
-
- if (sd == null)
- throw new IOException("Invalid X11 cookie received.");
-
- /* If the session which corresponds to this cookie is closed then we will
- * detect this: the session's close code will close all channels
- * with the session's assigned x11 fake cookie.
- */
-
- s = new Socket(sd.hostname, sd.port);
-
- OutputStream x11_os = s.getOutputStream();
- InputStream x11_is = s.getInputStream();
-
- /* Now we are sending the startup packet to the real X11 server */
-
- x11_os.write(header);
-
- if (sd.x11_magic_cookie == null)
- {
- byte[] emptyAuthData = new byte[6];
- /* empty auth data, hopefully you are connecting to localhost =) */
- x11_os.write(emptyAuthData);
- }
- else
- {
- if (sd.x11_magic_cookie.length != 16)
- throw new IOException("The real X11 cookie has an invalid length!");
-
- /* send X11 cookie specified by client */
- x11_os.write(auth_buff);
- x11_os.write(authProtocolName); /* re-use */
- x11_os.write(paddingBuffer, 0, authProtocolNamePadding);
- x11_os.write(sd.x11_magic_cookie);
- x11_os.write(paddingBuffer, 0, authProtocolDataPadding);
- }
-
- x11_os.flush();
-
- /* Start forwarding traffic */
-
- StreamForwarder r2l = new StreamForwarder(c, null, null, remote_is, x11_os, "RemoteToX11");
- StreamForwarder l2r = new StreamForwarder(c, null, null, x11_is, remote_os, "X11ToRemote");
-
- /* No need to start two threads, one can be executed in the current thread */
-
- r2l.setDaemon(true);
- r2l.start();
- l2r.run();
-
- while (r2l.isAlive())
- {
- try
- {
- r2l.join();
- }
- catch (InterruptedException e)
- {
- }
- }
-
- /* If the channel is already closed, then this is a no-op */
-
- c.cm.closeChannel(c, "EOF on both X11 streams reached.", true);
- s.close();
- }
- catch (IOException e)
- {
- log.log(50, "IOException in X11 proxy code: " + e.getMessage());
-
- try
- {
- c.cm.closeChannel(c, "IOException in X11 proxy code (" + e.getMessage() + ")", true);
- }
- catch (IOException e1)
- {
- }
- try
- {
- if (s != null)
- s.close();
- }
- catch (IOException e1)
- {
- }
- }
- }
-}
+
+package com.trilead.ssh2.channel;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+import com.trilead.ssh2.log.Logger;
+
+
+/**
+ * RemoteX11AcceptThread.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: RemoteX11AcceptThread.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
+ */
+public class RemoteX11AcceptThread extends Thread
+{
+ private static final Logger log = Logger.getLogger(RemoteX11AcceptThread.class);
+
+ Channel c;
+
+ String remoteOriginatorAddress;
+ int remoteOriginatorPort;
+
+ Socket s;
+
+ public RemoteX11AcceptThread(Channel c, String remoteOriginatorAddress, int remoteOriginatorPort)
+ {
+ this.c = c;
+ this.remoteOriginatorAddress = remoteOriginatorAddress;
+ this.remoteOriginatorPort = remoteOriginatorPort;
+ }
+
+ public void run()
+ {
+ try
+ {
+ /* Send Open Confirmation */
+
+ c.cm.sendOpenConfirmation(c);
+
+ /* Read startup packet from client */
+
+ OutputStream remote_os = c.getStdinStream();
+ InputStream remote_is = c.getStdoutStream();
+
+ /* The following code is based on the protocol description given in:
+ * Scheifler/Gettys,
+ * X Windows System: Core and Extension Protocols:
+ * X Version 11, Releases 6 and 6.1 ISBN 1-55558-148-X
+ */
+
+ /*
+ * Client startup:
+ *
+ * 1 0X42 MSB first/0x6c lSB first - byteorder
+ * 1 - unused
+ * 2 card16 - protocol-major-version
+ * 2 card16 - protocol-minor-version
+ * 2 n - lenght of authorization-protocol-name
+ * 2 d - lenght of authorization-protocol-data
+ * 2 - unused
+ * string8 - authorization-protocol-name
+ * p - unused, p=pad(n)
+ * string8 - authorization-protocol-data
+ * q - unused, q=pad(d)
+ *
+ * pad(X) = (4 - (X mod 4)) mod 4
+ *
+ * Server response:
+ *
+ * 1 (0 failed, 2 authenticate, 1 success)
+ * ...
+ *
+ */
+
+ /* Later on we will simply forward the first 6 header bytes to the "real" X11 server */
+
+ byte[] header = new byte[6];
+
+ if (remote_is.read(header) != 6)
+ throw new IOException("Unexpected EOF on X11 startup!");
+
+ if ((header[0] != 0x42) && (header[0] != 0x6c)) // 0x42 MSB first, 0x6C LSB first
+ throw new IOException("Unknown endian format in X11 message!");
+
+ /* Yes, I came up with this myself - shall I file an application for a patent? =) */
+
+ int idxMSB = (header[0] == 0x42) ? 0 : 1;
+
+ /* Read authorization data header */
+
+ byte[] auth_buff = new byte[6];
+
+ if (remote_is.read(auth_buff) != 6)
+ throw new IOException("Unexpected EOF on X11 startup!");
+
+ int authProtocolNameLength = ((auth_buff[idxMSB] & 0xff) << 8) | (auth_buff[1 - idxMSB] & 0xff);
+ int authProtocolDataLength = ((auth_buff[2 + idxMSB] & 0xff) << 8) | (auth_buff[3 - idxMSB] & 0xff);
+
+ if ((authProtocolNameLength > 256) || (authProtocolDataLength > 256))
+ throw new IOException("Buggy X11 authorization data");
+
+ int authProtocolNamePadding = ((4 - (authProtocolNameLength % 4)) % 4);
+ int authProtocolDataPadding = ((4 - (authProtocolDataLength % 4)) % 4);
+
+ byte[] authProtocolName = new byte[authProtocolNameLength];
+ byte[] authProtocolData = new byte[authProtocolDataLength];
+
+ byte[] paddingBuffer = new byte[4];
+
+ if (remote_is.read(authProtocolName) != authProtocolNameLength)
+ throw new IOException("Unexpected EOF on X11 startup! (authProtocolName)");
+
+ if (remote_is.read(paddingBuffer, 0, authProtocolNamePadding) != authProtocolNamePadding)
+ throw new IOException("Unexpected EOF on X11 startup! (authProtocolNamePadding)");
+
+ if (remote_is.read(authProtocolData) != authProtocolDataLength)
+ throw new IOException("Unexpected EOF on X11 startup! (authProtocolData)");
+
+ if (remote_is.read(paddingBuffer, 0, authProtocolDataPadding) != authProtocolDataPadding)
+ throw new IOException("Unexpected EOF on X11 startup! (authProtocolDataPadding)");
+
+ if ("MIT-MAGIC-COOKIE-1".equals(new String(authProtocolName, "ISO-8859-1")) == false)
+ throw new IOException("Unknown X11 authorization protocol!");
+
+ if (authProtocolDataLength != 16)
+ throw new IOException("Wrong data length for X11 authorization data!");
+
+ StringBuffer tmp = new StringBuffer(32);
+ for (int i = 0; i < authProtocolData.length; i++)
+ {
+ String digit2 = Integer.toHexString(authProtocolData[i] & 0xff);
+ tmp.append((digit2.length() == 2) ? digit2 : "0" + digit2);
+ }
+ String hexEncodedFakeCookie = tmp.toString();
+
+ /* Order is very important here - it may be that a certain x11 forwarding
+ * gets disabled right in the moment when we check and register our connection
+ * */
+
+ synchronized (c)
+ {
+ /* Please read the comment in Channel.java */
+ c.hexX11FakeCookie = hexEncodedFakeCookie;
+ }
+
+ /* Now check our fake cookie directory to see if we produced this cookie */
+
+ X11ServerData sd = c.cm.checkX11Cookie(hexEncodedFakeCookie);
+
+ if (sd == null)
+ throw new IOException("Invalid X11 cookie received.");
+
+ /* If the session which corresponds to this cookie is closed then we will
+ * detect this: the session's close code will close all channels
+ * with the session's assigned x11 fake cookie.
+ */
+
+ s = new Socket(sd.hostname, sd.port);
+
+ OutputStream x11_os = s.getOutputStream();
+ InputStream x11_is = s.getInputStream();
+
+ /* Now we are sending the startup packet to the real X11 server */
+
+ x11_os.write(header);
+
+ if (sd.x11_magic_cookie == null)
+ {
+ byte[] emptyAuthData = new byte[6];
+ /* empty auth data, hopefully you are connecting to localhost =) */
+ x11_os.write(emptyAuthData);
+ }
+ else
+ {
+ if (sd.x11_magic_cookie.length != 16)
+ throw new IOException("The real X11 cookie has an invalid length!");
+
+ /* send X11 cookie specified by client */
+ x11_os.write(auth_buff);
+ x11_os.write(authProtocolName); /* re-use */
+ x11_os.write(paddingBuffer, 0, authProtocolNamePadding);
+ x11_os.write(sd.x11_magic_cookie);
+ x11_os.write(paddingBuffer, 0, authProtocolDataPadding);
+ }
+
+ x11_os.flush();
+
+ /* Start forwarding traffic */
+
+ StreamForwarder r2l = new StreamForwarder(c, null, s, remote_is, x11_os, "RemoteToX11");
+ StreamForwarder l2r = new StreamForwarder(c, null, null, x11_is, remote_os, "X11ToRemote");
+
+ /* No need to start two threads, one can be executed in the current thread */
+
+ r2l.setDaemon(true);
+ r2l.start();
+ l2r.run();
+
+ while (r2l.isAlive())
+ {
+ try
+ {
+ r2l.join();
+ }
+ catch (InterruptedException e)
+ {
+ }
+ }
+
+ /* If the channel is already closed, then this is a no-op */
+
+ c.cm.closeChannel(c, "EOF on both X11 streams reached.", true);
+ s.close();
+ }
+ catch (IOException e)
+ {
+ log.log(50, "IOException in X11 proxy code: " + e.getMessage());
+
+ try
+ {
+ c.cm.closeChannel(c, "IOException in X11 proxy code (" + e.getMessage() + ")", true);
+ }
+ catch (IOException e1)
+ {
+ }
+ try
+ {
+ if (s != null)
+ s.close();
+ }
+ catch (IOException e1)
+ {
+ }
+ }
+ }
+}
diff --git a/src/com/trilead/ssh2/channel/StreamForwarder.java b/src/com/trilead/ssh2/channel/StreamForwarder.java
index 376a3a0..e1afee8 100644
--- a/src/com/trilead/ssh2/channel/StreamForwarder.java
+++ b/src/com/trilead/ssh2/channel/StreamForwarder.java
@@ -1,112 +1,113 @@
-
-package com.trilead.ssh2.channel;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.Socket;
-
-/**
- * A StreamForwarder forwards data between two given streams.
- * If two StreamForwarder threads are used (one for each direction)
- * then one can be configured to shutdown the underlying channel/socket
- * if both threads have finished forwarding (EOF).
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: StreamForwarder.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public class StreamForwarder extends Thread
-{
- OutputStream os;
- InputStream is;
- byte[] buffer = new byte[Channel.CHANNEL_BUFFER_SIZE];
- Channel c;
- StreamForwarder sibling;
- Socket s;
- String mode;
-
- StreamForwarder(Channel c, StreamForwarder sibling, Socket s, InputStream is, OutputStream os, String mode)
- throws IOException
- {
- this.is = is;
- this.os = os;
- this.mode = mode;
- this.c = c;
- this.sibling = sibling;
- this.s = s;
- }
-
- public void run()
- {
- try
- {
- while (true)
- {
- int len = is.read(buffer);
- if (len <= 0)
- break;
- os.write(buffer, 0, len);
- os.flush();
- }
- }
- catch (IOException ignore)
- {
- try
- {
- c.cm.closeChannel(c, "Closed due to exception in StreamForwarder (" + mode + "): "
- + ignore.getMessage(), true);
- }
- catch (IOException e)
- {
- }
- }
- finally
- {
- try
- {
- os.close();
- }
- catch (IOException e1)
- {
- }
- try
- {
- is.close();
- }
- catch (IOException e2)
- {
- }
-
- if (sibling != null)
- {
- while (sibling.isAlive())
- {
- try
- {
- sibling.join();
- }
- catch (InterruptedException e)
- {
- }
- }
-
- try
- {
- c.cm.closeChannel(c, "StreamForwarder (" + mode + ") is cleaning up the connection", true);
- }
- catch (IOException e3)
- {
- }
-
- try
- {
- if (s != null)
- s.close();
- }
- catch (IOException e1)
- {
- }
- }
- }
- }
-} \ No newline at end of file
+
+package com.trilead.ssh2.channel;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+/**
+ * A StreamForwarder forwards data between two given streams.
+ * If two StreamForwarder threads are used (one for each direction)
+ * then one can be configured to shutdown the underlying channel/socket
+ * if both threads have finished forwarding (EOF).
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: StreamForwarder.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+public class StreamForwarder extends Thread
+{
+ final OutputStream os;
+ final InputStream is;
+ final byte[] buffer = new byte[Channel.CHANNEL_BUFFER_SIZE];
+ final Channel c;
+ final StreamForwarder sibling;
+ final Socket s;
+ final String mode;
+
+ StreamForwarder(Channel c, StreamForwarder sibling, Socket s, InputStream is, OutputStream os, String mode)
+ throws IOException
+ {
+ this.is = is;
+ this.os = os;
+ this.mode = mode;
+ this.c = c;
+ this.sibling = sibling;
+ this.s = s;
+ }
+
+ public void run()
+ {
+ try
+ {
+ while (true)
+ {
+ int len = is.read(buffer);
+ if (len <= 0)
+ break;
+ os.write(buffer, 0, len);
+ os.flush();
+ }
+ }
+ catch (IOException ignore)
+ {
+ try
+ {
+ c.cm.closeChannel(c, "Closed due to exception in StreamForwarder (" + mode + "): "
+ + ignore.getMessage(), true);
+ }
+ catch (IOException e)
+ {
+ }
+ }
+ finally
+ {
+ try
+ {
+ os.close();
+ }
+ catch (IOException e1)
+ {
+ }
+ try
+ {
+ is.close();
+ }
+ catch (IOException e2)
+ {
+ }
+
+ if (sibling != null)
+ {
+ while (sibling.isAlive())
+ {
+ try
+ {
+ sibling.join();
+ }
+ catch (InterruptedException e)
+ {
+ }
+ }
+
+ try
+ {
+ c.cm.closeChannel(c, "StreamForwarder (" + mode + ") is cleaning up the connection", true);
+ }
+ catch (IOException e3)
+ {
+ }
+ }
+
+ if (s != null) {
+ try
+ {
+ s.close();
+ }
+ catch (IOException e1)
+ {
+ }
+ }
+ }
+ }
+}
diff --git a/src/com/trilead/ssh2/channel/X11ServerData.java b/src/com/trilead/ssh2/channel/X11ServerData.java
index 0840376..041f9cb 100644
--- a/src/com/trilead/ssh2/channel/X11ServerData.java
+++ b/src/com/trilead/ssh2/channel/X11ServerData.java
@@ -1,16 +1,16 @@
-
-package com.trilead.ssh2.channel;
-
-/**
- * X11ServerData. Data regarding an x11 forwarding target.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: X11ServerData.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- *
- */
-public class X11ServerData
-{
- public String hostname;
- public int port;
- public byte[] x11_magic_cookie; /* not the remote (fake) one, the local (real) one */
-}
+
+package com.trilead.ssh2.channel;
+
+/**
+ * X11ServerData. Data regarding an x11 forwarding target.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: X11ServerData.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ *
+ */
+public class X11ServerData
+{
+ public String hostname;
+ public int port;
+ public byte[] x11_magic_cookie; /* not the remote (fake) one, the local (real) one */
+}
diff --git a/src/com/trilead/ssh2/compression/CompressionFactory.java b/src/com/trilead/ssh2/compression/CompressionFactory.java
index a846f58..9f8d7ef 100644
--- a/src/com/trilead/ssh2/compression/CompressionFactory.java
+++ b/src/com/trilead/ssh2/compression/CompressionFactory.java
@@ -43,7 +43,7 @@ public class CompressionFactory {
/* Higher Priority First */
compressors.addElement(new CompressorEntry("zlib", "com.trilead.ssh2.compression.Zlib"));
- compressors.addElement(new CompressorEntry("zlib@openssh.com", "com.trilead.ssh2.compression.Zlib"));
+ compressors.addElement(new CompressorEntry("zlib@openssh.com", "com.trilead.ssh2.compression.ZlibOpenSSH"));
compressors.addElement(new CompressorEntry("none", ""));
}
diff --git a/src/com/trilead/ssh2/compression/ICompressor.java b/src/com/trilead/ssh2/compression/ICompressor.java
index 3f76326..0b435b9 100644
--- a/src/com/trilead/ssh2/compression/ICompressor.java
+++ b/src/com/trilead/ssh2/compression/ICompressor.java
@@ -27,4 +27,6 @@ public interface ICompressor {
int compress(byte[] buf, int start, int len, byte[] output);
byte[] uncompress(byte[] buf, int start, int[] len);
+
+ boolean canCompressPreauth();
}
diff --git a/src/com/trilead/ssh2/compression/Zlib.java b/src/com/trilead/ssh2/compression/Zlib.java
index e9cc017..c1203a3 100644
--- a/src/com/trilead/ssh2/compression/Zlib.java
+++ b/src/com/trilead/ssh2/compression/Zlib.java
@@ -47,6 +47,10 @@ public class Zlib implements ICompressor {
inflated_buf = new byte[DEFAULT_BUF_SIZE];
}
+ public boolean canCompressPreauth() {
+ return true;
+ }
+
public int getBufferSize() {
return DEFAULT_BUF_SIZE;
}
diff --git a/src/com/trilead/ssh2/compression/ZlibOpenSSH.java b/src/com/trilead/ssh2/compression/ZlibOpenSSH.java
new file mode 100644
index 0000000..266fff9
--- /dev/null
+++ b/src/com/trilead/ssh2/compression/ZlibOpenSSH.java
@@ -0,0 +1,35 @@
+/*
+ * ConnectBot: simple, powerful, open-source SSH client for Android
+ * Copyright 2007 Kenny Root, Jeffrey Sharkey
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.trilead.ssh2.compression;
+
+/**
+ * Defines how zlib@openssh.org compression works.
+ * See
+ * http://www.openssh.org/txt/draft-miller-secsh-compression-delayed-00.txt
+ * compression is disabled until userauth has occurred.
+ *
+ * @author Matt Johnston
+ *
+ */
+public class ZlibOpenSSH extends Zlib {
+
+ public boolean canCompressPreauth() {
+ return false;
+ }
+
+}
diff --git a/src/com/trilead/ssh2/crypto/Base64.java b/src/com/trilead/ssh2/crypto/Base64.java
index 18a524f..93770ac 100644
--- a/src/com/trilead/ssh2/crypto/Base64.java
+++ b/src/com/trilead/ssh2/crypto/Base64.java
@@ -1,148 +1,148 @@
-
-package com.trilead.ssh2.crypto;
-
-import java.io.CharArrayWriter;
-import java.io.IOException;
-
-/**
- * Basic Base64 Support.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: Base64.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public class Base64
-{
- static final char[] alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
-
- public static char[] encode(byte[] content)
- {
- CharArrayWriter cw = new CharArrayWriter((4 * content.length) / 3);
-
- int idx = 0;
-
- int x = 0;
-
- for (int i = 0; i < content.length; i++)
- {
- if (idx == 0)
- x = (content[i] & 0xff) << 16;
- else if (idx == 1)
- x = x | ((content[i] & 0xff) << 8);
- else
- x = x | (content[i] & 0xff);
-
- idx++;
-
- if (idx == 3)
- {
- cw.write(alphabet[x >> 18]);
- cw.write(alphabet[(x >> 12) & 0x3f]);
- cw.write(alphabet[(x >> 6) & 0x3f]);
- cw.write(alphabet[x & 0x3f]);
-
- idx = 0;
- }
- }
-
- if (idx == 1)
- {
- cw.write(alphabet[x >> 18]);
- cw.write(alphabet[(x >> 12) & 0x3f]);
- cw.write('=');
- cw.write('=');
- }
-
- if (idx == 2)
- {
- cw.write(alphabet[x >> 18]);
- cw.write(alphabet[(x >> 12) & 0x3f]);
- cw.write(alphabet[(x >> 6) & 0x3f]);
- cw.write('=');
- }
-
- return cw.toCharArray();
- }
-
- public static byte[] decode(char[] message) throws IOException
- {
- byte buff[] = new byte[4];
- byte dest[] = new byte[message.length];
-
- int bpos = 0;
- int destpos = 0;
-
- for (int i = 0; i < message.length; i++)
- {
- int c = message[i];
-
- if ((c == '\n') || (c == '\r') || (c == ' ') || (c == '\t'))
- continue;
-
- if ((c >= 'A') && (c <= 'Z'))
- {
- buff[bpos++] = (byte) (c - 'A');
- }
- else if ((c >= 'a') && (c <= 'z'))
- {
- buff[bpos++] = (byte) ((c - 'a') + 26);
- }
- else if ((c >= '0') && (c <= '9'))
- {
- buff[bpos++] = (byte) ((c - '0') + 52);
- }
- else if (c == '+')
- {
- buff[bpos++] = 62;
- }
- else if (c == '/')
- {
- buff[bpos++] = 63;
- }
- else if (c == '=')
- {
- buff[bpos++] = 64;
- }
- else
- {
- throw new IOException("Illegal char in base64 code.");
- }
-
- if (bpos == 4)
- {
- bpos = 0;
-
- if (buff[0] == 64)
- break;
-
- if (buff[1] == 64)
- throw new IOException("Unexpected '=' in base64 code.");
-
- if (buff[2] == 64)
- {
- int v = (((buff[0] & 0x3f) << 6) | ((buff[1] & 0x3f)));
- dest[destpos++] = (byte) (v >> 4);
- break;
- }
- else if (buff[3] == 64)
- {
- int v = (((buff[0] & 0x3f) << 12) | ((buff[1] & 0x3f) << 6) | ((buff[2] & 0x3f)));
- dest[destpos++] = (byte) (v >> 10);
- dest[destpos++] = (byte) (v >> 2);
- break;
- }
- else
- {
- int v = (((buff[0] & 0x3f) << 18) | ((buff[1] & 0x3f) << 12) | ((buff[2] & 0x3f) << 6) | ((buff[3] & 0x3f)));
- dest[destpos++] = (byte) (v >> 16);
- dest[destpos++] = (byte) (v >> 8);
- dest[destpos++] = (byte) (v);
- }
- }
- }
-
- byte[] res = new byte[destpos];
- System.arraycopy(dest, 0, res, 0, destpos);
-
- return res;
- }
-}
+
+package com.trilead.ssh2.crypto;
+
+import java.io.CharArrayWriter;
+import java.io.IOException;
+
+/**
+ * Basic Base64 Support.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: Base64.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+public class Base64
+{
+ static final char[] alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
+
+ public static char[] encode(byte[] content)
+ {
+ CharArrayWriter cw = new CharArrayWriter((4 * content.length) / 3);
+
+ int idx = 0;
+
+ int x = 0;
+
+ for (int i = 0; i < content.length; i++)
+ {
+ if (idx == 0)
+ x = (content[i] & 0xff) << 16;
+ else if (idx == 1)
+ x = x | ((content[i] & 0xff) << 8);
+ else
+ x = x | (content[i] & 0xff);
+
+ idx++;
+
+ if (idx == 3)
+ {
+ cw.write(alphabet[x >> 18]);
+ cw.write(alphabet[(x >> 12) & 0x3f]);
+ cw.write(alphabet[(x >> 6) & 0x3f]);
+ cw.write(alphabet[x & 0x3f]);
+
+ idx = 0;
+ }
+ }
+
+ if (idx == 1)
+ {
+ cw.write(alphabet[x >> 18]);
+ cw.write(alphabet[(x >> 12) & 0x3f]);
+ cw.write('=');
+ cw.write('=');
+ }
+
+ if (idx == 2)
+ {
+ cw.write(alphabet[x >> 18]);
+ cw.write(alphabet[(x >> 12) & 0x3f]);
+ cw.write(alphabet[(x >> 6) & 0x3f]);
+ cw.write('=');
+ }
+
+ return cw.toCharArray();
+ }
+
+ public static byte[] decode(char[] message) throws IOException
+ {
+ byte buff[] = new byte[4];
+ byte dest[] = new byte[message.length];
+
+ int bpos = 0;
+ int destpos = 0;
+
+ for (int i = 0; i < message.length; i++)
+ {
+ int c = message[i];
+
+ if ((c == '\n') || (c == '\r') || (c == ' ') || (c == '\t'))
+ continue;
+
+ if ((c >= 'A') && (c <= 'Z'))
+ {
+ buff[bpos++] = (byte) (c - 'A');
+ }
+ else if ((c >= 'a') && (c <= 'z'))
+ {
+ buff[bpos++] = (byte) ((c - 'a') + 26);
+ }
+ else if ((c >= '0') && (c <= '9'))
+ {
+ buff[bpos++] = (byte) ((c - '0') + 52);
+ }
+ else if (c == '+')
+ {
+ buff[bpos++] = 62;
+ }
+ else if (c == '/')
+ {
+ buff[bpos++] = 63;
+ }
+ else if (c == '=')
+ {
+ buff[bpos++] = 64;
+ }
+ else
+ {
+ throw new IOException("Illegal char in base64 code.");
+ }
+
+ if (bpos == 4)
+ {
+ bpos = 0;
+
+ if (buff[0] == 64)
+ break;
+
+ if (buff[1] == 64)
+ throw new IOException("Unexpected '=' in base64 code.");
+
+ if (buff[2] == 64)
+ {
+ int v = (((buff[0] & 0x3f) << 6) | ((buff[1] & 0x3f)));
+ dest[destpos++] = (byte) (v >> 4);
+ break;
+ }
+ else if (buff[3] == 64)
+ {
+ int v = (((buff[0] & 0x3f) << 12) | ((buff[1] & 0x3f) << 6) | ((buff[2] & 0x3f)));
+ dest[destpos++] = (byte) (v >> 10);
+ dest[destpos++] = (byte) (v >> 2);
+ break;
+ }
+ else
+ {
+ int v = (((buff[0] & 0x3f) << 18) | ((buff[1] & 0x3f) << 12) | ((buff[2] & 0x3f) << 6) | ((buff[3] & 0x3f)));
+ dest[destpos++] = (byte) (v >> 16);
+ dest[destpos++] = (byte) (v >> 8);
+ dest[destpos++] = (byte) (v);
+ }
+ }
+ }
+
+ byte[] res = new byte[destpos];
+ System.arraycopy(dest, 0, res, 0, destpos);
+
+ return res;
+ }
+}
diff --git a/src/com/trilead/ssh2/crypto/CryptoWishList.java b/src/com/trilead/ssh2/crypto/CryptoWishList.java
index 8fc64e5..86959e7 100644
--- a/src/com/trilead/ssh2/crypto/CryptoWishList.java
+++ b/src/com/trilead/ssh2/crypto/CryptoWishList.java
@@ -1,26 +1,26 @@
-
-package com.trilead.ssh2.crypto;
-
-import com.trilead.ssh2.compression.CompressionFactory;
-import com.trilead.ssh2.crypto.cipher.BlockCipherFactory;
-import com.trilead.ssh2.crypto.digest.MAC;
-import com.trilead.ssh2.transport.KexManager;
-
-
-/**
- * CryptoWishList.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: CryptoWishList.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public class CryptoWishList
-{
- public String[] kexAlgorithms = KexManager.getDefaultKexAlgorithmList();
- public String[] serverHostKeyAlgorithms = KexManager.getDefaultServerHostkeyAlgorithmList();
- public String[] c2s_enc_algos = BlockCipherFactory.getDefaultCipherList();
- public String[] s2c_enc_algos = BlockCipherFactory.getDefaultCipherList();
- public String[] c2s_mac_algos = MAC.getMacList();
- public String[] s2c_mac_algos = MAC.getMacList();
- public String[] c2s_comp_algos = CompressionFactory.getDefaultCompressorList();
- public String[] s2c_comp_algos = CompressionFactory.getDefaultCompressorList();
-}
+
+package com.trilead.ssh2.crypto;
+
+import com.trilead.ssh2.compression.CompressionFactory;
+import com.trilead.ssh2.crypto.cipher.BlockCipherFactory;
+import com.trilead.ssh2.crypto.digest.MAC;
+import com.trilead.ssh2.transport.KexManager;
+
+
+/**
+ * CryptoWishList.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: CryptoWishList.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+public class CryptoWishList
+{
+ public String[] kexAlgorithms = KexManager.getDefaultKexAlgorithmList();
+ public String[] serverHostKeyAlgorithms = KexManager.getDefaultServerHostkeyAlgorithmList();
+ public String[] c2s_enc_algos = BlockCipherFactory.getDefaultCipherList();
+ public String[] s2c_enc_algos = BlockCipherFactory.getDefaultCipherList();
+ public String[] c2s_mac_algos = MAC.getMacList();
+ public String[] s2c_mac_algos = MAC.getMacList();
+ public String[] c2s_comp_algos = CompressionFactory.getDefaultCompressorList();
+ public String[] s2c_comp_algos = CompressionFactory.getDefaultCompressorList();
+}
diff --git a/src/com/trilead/ssh2/crypto/KeyMaterial.java b/src/com/trilead/ssh2/crypto/KeyMaterial.java
index 5dfb55e..1dbd6c7 100644
--- a/src/com/trilead/ssh2/crypto/KeyMaterial.java
+++ b/src/com/trilead/ssh2/crypto/KeyMaterial.java
@@ -1,91 +1,91 @@
-
-package com.trilead.ssh2.crypto;
-
-
-import java.math.BigInteger;
-
-import com.trilead.ssh2.crypto.digest.HashForSSH2Types;
-
-/**
- * Establishes key material for iv/key/mac (both directions).
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: KeyMaterial.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public class KeyMaterial
-{
- public byte[] initial_iv_client_to_server;
- public byte[] initial_iv_server_to_client;
- public byte[] enc_key_client_to_server;
- public byte[] enc_key_server_to_client;
- public byte[] integrity_key_client_to_server;
- public byte[] integrity_key_server_to_client;
-
- private static byte[] calculateKey(HashForSSH2Types sh, BigInteger K, byte[] H, byte type, byte[] SessionID,
- int keyLength)
- {
- byte[] res = new byte[keyLength];
-
- int dglen = sh.getDigestLength();
- int numRounds = (keyLength + dglen - 1) / dglen;
-
- byte[][] tmp = new byte[numRounds][];
-
- sh.reset();
- sh.updateBigInt(K);
- sh.updateBytes(H);
- sh.updateByte(type);
- sh.updateBytes(SessionID);
-
- tmp[0] = sh.getDigest();
-
- int off = 0;
- int produced = Math.min(dglen, keyLength);
-
- System.arraycopy(tmp[0], 0, res, off, produced);
-
- keyLength -= produced;
- off += produced;
-
- for (int i = 1; i < numRounds; i++)
- {
- sh.updateBigInt(K);
- sh.updateBytes(H);
-
- for (int j = 0; j < i; j++)
- sh.updateBytes(tmp[j]);
-
- tmp[i] = sh.getDigest();
-
- produced = Math.min(dglen, keyLength);
- System.arraycopy(tmp[i], 0, res, off, produced);
- keyLength -= produced;
- off += produced;
- }
-
- return res;
- }
-
- public static KeyMaterial create(String hashType, byte[] H, BigInteger K, byte[] SessionID, int keyLengthCS,
- int blockSizeCS, int macLengthCS, int keyLengthSC, int blockSizeSC, int macLengthSC)
- throws IllegalArgumentException
- {
- KeyMaterial km = new KeyMaterial();
-
- HashForSSH2Types sh = new HashForSSH2Types(hashType);
-
- km.initial_iv_client_to_server = calculateKey(sh, K, H, (byte) 'A', SessionID, blockSizeCS);
-
- km.initial_iv_server_to_client = calculateKey(sh, K, H, (byte) 'B', SessionID, blockSizeSC);
-
- km.enc_key_client_to_server = calculateKey(sh, K, H, (byte) 'C', SessionID, keyLengthCS);
-
- km.enc_key_server_to_client = calculateKey(sh, K, H, (byte) 'D', SessionID, keyLengthSC);
-
- km.integrity_key_client_to_server = calculateKey(sh, K, H, (byte) 'E', SessionID, macLengthCS);
-
- km.integrity_key_server_to_client = calculateKey(sh, K, H, (byte) 'F', SessionID, macLengthSC);
-
- return km;
- }
-}
+
+package com.trilead.ssh2.crypto;
+
+
+import java.math.BigInteger;
+
+import com.trilead.ssh2.crypto.digest.HashForSSH2Types;
+
+/**
+ * Establishes key material for iv/key/mac (both directions).
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: KeyMaterial.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+public class KeyMaterial
+{
+ public byte[] initial_iv_client_to_server;
+ public byte[] initial_iv_server_to_client;
+ public byte[] enc_key_client_to_server;
+ public byte[] enc_key_server_to_client;
+ public byte[] integrity_key_client_to_server;
+ public byte[] integrity_key_server_to_client;
+
+ private static byte[] calculateKey(HashForSSH2Types sh, BigInteger K, byte[] H, byte type, byte[] SessionID,
+ int keyLength)
+ {
+ byte[] res = new byte[keyLength];
+
+ int dglen = sh.getDigestLength();
+ int numRounds = (keyLength + dglen - 1) / dglen;
+
+ byte[][] tmp = new byte[numRounds][];
+
+ sh.reset();
+ sh.updateBigInt(K);
+ sh.updateBytes(H);
+ sh.updateByte(type);
+ sh.updateBytes(SessionID);
+
+ tmp[0] = sh.getDigest();
+
+ int off = 0;
+ int produced = Math.min(dglen, keyLength);
+
+ System.arraycopy(tmp[0], 0, res, off, produced);
+
+ keyLength -= produced;
+ off += produced;
+
+ for (int i = 1; i < numRounds; i++)
+ {
+ sh.updateBigInt(K);
+ sh.updateBytes(H);
+
+ for (int j = 0; j < i; j++)
+ sh.updateBytes(tmp[j]);
+
+ tmp[i] = sh.getDigest();
+
+ produced = Math.min(dglen, keyLength);
+ System.arraycopy(tmp[i], 0, res, off, produced);
+ keyLength -= produced;
+ off += produced;
+ }
+
+ return res;
+ }
+
+ public static KeyMaterial create(String hashAlgo, byte[] H, BigInteger K, byte[] SessionID, int keyLengthCS,
+ int blockSizeCS, int macLengthCS, int keyLengthSC, int blockSizeSC, int macLengthSC)
+ throws IllegalArgumentException
+ {
+ KeyMaterial km = new KeyMaterial();
+
+ HashForSSH2Types sh = new HashForSSH2Types(hashAlgo);
+
+ km.initial_iv_client_to_server = calculateKey(sh, K, H, (byte) 'A', SessionID, blockSizeCS);
+
+ km.initial_iv_server_to_client = calculateKey(sh, K, H, (byte) 'B', SessionID, blockSizeSC);
+
+ km.enc_key_client_to_server = calculateKey(sh, K, H, (byte) 'C', SessionID, keyLengthCS);
+
+ km.enc_key_server_to_client = calculateKey(sh, K, H, (byte) 'D', SessionID, keyLengthSC);
+
+ km.integrity_key_client_to_server = calculateKey(sh, K, H, (byte) 'E', SessionID, macLengthCS);
+
+ km.integrity_key_server_to_client = calculateKey(sh, K, H, (byte) 'F', SessionID, macLengthSC);
+
+ return km;
+ }
+}
diff --git a/src/com/trilead/ssh2/crypto/PEMDecoder.java b/src/com/trilead/ssh2/crypto/PEMDecoder.java
index 7d0a015..5c0c2fd 100644
--- a/src/com/trilead/ssh2/crypto/PEMDecoder.java
+++ b/src/com/trilead/ssh2/crypto/PEMDecoder.java
@@ -1,377 +1,494 @@
-
-package com.trilead.ssh2.crypto;
-
-import java.io.BufferedReader;
-import java.io.CharArrayReader;
-import java.io.IOException;
-import java.math.BigInteger;
-
-import com.trilead.ssh2.crypto.cipher.AES;
-import com.trilead.ssh2.crypto.cipher.BlockCipher;
-import com.trilead.ssh2.crypto.cipher.CBCMode;
-import com.trilead.ssh2.crypto.cipher.DES;
-import com.trilead.ssh2.crypto.cipher.DESede;
-import com.trilead.ssh2.crypto.digest.MD5;
-import com.trilead.ssh2.signature.DSAPrivateKey;
-import com.trilead.ssh2.signature.RSAPrivateKey;
-
-/**
- * PEM Support.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PEMDecoder.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
- */
-public class PEMDecoder
-{
- public static final int PEM_RSA_PRIVATE_KEY = 1;
- public static final int PEM_DSA_PRIVATE_KEY = 2;
-
- private static final int hexToInt(char c)
- {
- if ((c >= 'a') && (c <= 'f'))
- {
- return (c - 'a') + 10;
- }
-
- if ((c >= 'A') && (c <= 'F'))
- {
- return (c - 'A') + 10;
- }
-
- if ((c >= '0') && (c <= '9'))
- {
- return (c - '0');
- }
-
- throw new IllegalArgumentException("Need hex char");
- }
-
- private static byte[] hexToByteArray(String hex)
- {
- if (hex == null)
- throw new IllegalArgumentException("null argument");
-
- if ((hex.length() % 2) != 0)
- throw new IllegalArgumentException("Uneven string length in hex encoding.");
-
- byte decoded[] = new byte[hex.length() / 2];
-
- for (int i = 0; i < decoded.length; i++)
- {
- int hi = hexToInt(hex.charAt(i * 2));
- int lo = hexToInt(hex.charAt((i * 2) + 1));
-
- decoded[i] = (byte) (hi * 16 + lo);
- }
-
- return decoded;
- }
-
- private static byte[] generateKeyFromPasswordSaltWithMD5(byte[] password, byte[] salt, int keyLen)
- throws IOException
- {
- if (salt.length < 8)
- throw new IllegalArgumentException("Salt needs to be at least 8 bytes for key generation.");
-
- MD5 md5 = new MD5();
-
- byte[] key = new byte[keyLen];
- byte[] tmp = new byte[md5.getDigestLength()];
-
- while (true)
- {
- md5.update(password, 0, password.length);
- md5.update(salt, 0, 8); // ARGH we only use the first 8 bytes of the
- // salt in this step.
- // This took me two hours until I got AES-xxx running.
-
- int copy = (keyLen < tmp.length) ? keyLen : tmp.length;
-
- md5.digest(tmp, 0);
-
- System.arraycopy(tmp, 0, key, key.length - keyLen, copy);
-
- keyLen -= copy;
-
- if (keyLen == 0)
- return key;
-
- md5.update(tmp, 0, tmp.length);
- }
- }
-
- private static byte[] removePadding(byte[] buff, int blockSize) throws IOException
- {
- /* Removes RFC 1423/PKCS #7 padding */
-
- int rfc_1423_padding = buff[buff.length - 1] & 0xff;
-
- if ((rfc_1423_padding < 1) || (rfc_1423_padding > blockSize))
- throw new IOException("Decrypted PEM has wrong padding, did you specify the correct password?");
-
- for (int i = 2; i <= rfc_1423_padding; i++)
- {
- if (buff[buff.length - i] != rfc_1423_padding)
- throw new IOException("Decrypted PEM has wrong padding, did you specify the correct password?");
- }
-
- byte[] tmp = new byte[buff.length - rfc_1423_padding];
- System.arraycopy(buff, 0, tmp, 0, buff.length - rfc_1423_padding);
- return tmp;
- }
-
- public static final PEMStructure parsePEM(char[] pem) throws IOException
- {
- PEMStructure ps = new PEMStructure();
-
- String line = null;
-
- BufferedReader br = new BufferedReader(new CharArrayReader(pem));
-
- String endLine = null;
-
- while (true)
- {
- line = br.readLine();
-
- if (line == null)
- throw new IOException("Invalid PEM structure, '-----BEGIN...' missing");
-
- line = line.trim();
-
- if (line.startsWith("-----BEGIN DSA PRIVATE KEY-----"))
- {
- endLine = "-----END DSA PRIVATE KEY-----";
- ps.pemType = PEM_DSA_PRIVATE_KEY;
- break;
- }
-
- if (line.startsWith("-----BEGIN RSA PRIVATE KEY-----"))
- {
- endLine = "-----END RSA PRIVATE KEY-----";
- ps.pemType = PEM_RSA_PRIVATE_KEY;
- break;
- }
- }
-
- while (true)
- {
- line = br.readLine();
-
- if (line == null)
- throw new IOException("Invalid PEM structure, " + endLine + " missing");
-
- line = line.trim();
-
- int sem_idx = line.indexOf(':');
-
- if (sem_idx == -1)
- break;
-
- String name = line.substring(0, sem_idx + 1);
- String value = line.substring(sem_idx + 1);
-
- String values[] = value.split(",");
-
- for (int i = 0; i < values.length; i++)
- values[i] = values[i].trim();
-
- // Proc-Type: 4,ENCRYPTED
- // DEK-Info: DES-EDE3-CBC,579B6BE3E5C60483
-
- if ("Proc-Type:".equals(name))
- {
- ps.procType = values;
- continue;
- }
-
- if ("DEK-Info:".equals(name))
- {
- ps.dekInfo = values;
- continue;
- }
- /* Ignore line */
- }
-
- StringBuffer keyData = new StringBuffer();
-
- while (true)
- {
- if (line == null)
- throw new IOException("Invalid PEM structure, " + endLine + " missing");
-
- line = line.trim();
-
- if (line.startsWith(endLine))
- break;
-
- keyData.append(line);
-
- line = br.readLine();
- }
-
- char[] pem_chars = new char[keyData.length()];
- keyData.getChars(0, pem_chars.length, pem_chars, 0);
-
- ps.data = Base64.decode(pem_chars);
-
- if (ps.data.length == 0)
- throw new IOException("Invalid PEM structure, no data available");
-
- return ps;
- }
-
- private static final void decryptPEM(PEMStructure ps, byte[] pw) throws IOException
- {
- if (ps.dekInfo == null)
- throw new IOException("Broken PEM, no mode and salt given, but encryption enabled");
-
- if (ps.dekInfo.length != 2)
- throw new IOException("Broken PEM, DEK-Info is incomplete!");
-
- String algo = ps.dekInfo[0];
- byte[] salt = hexToByteArray(ps.dekInfo[1]);
-
- BlockCipher bc = null;
-
- if (algo.equals("DES-EDE3-CBC"))
- {
- DESede des3 = new DESede();
- des3.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 24));
- bc = new CBCMode(des3, salt, false);
- }
- else if (algo.equals("DES-CBC"))
- {
- DES des = new DES();
- des.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 8));
- bc = new CBCMode(des, salt, false);
- }
- else if (algo.equals("AES-128-CBC"))
- {
- AES aes = new AES();
- aes.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 16));
- bc = new CBCMode(aes, salt, false);
- }
- else if (algo.equals("AES-192-CBC"))
- {
- AES aes = new AES();
- aes.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 24));
- bc = new CBCMode(aes, salt, false);
- }
- else if (algo.equals("AES-256-CBC"))
- {
- AES aes = new AES();
- aes.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 32));
- bc = new CBCMode(aes, salt, false);
- }
- else
- {
- throw new IOException("Cannot decrypt PEM structure, unknown cipher " + algo);
- }
-
- if ((ps.data.length % bc.getBlockSize()) != 0)
- throw new IOException("Invalid PEM structure, size of encrypted block is not a multiple of "
- + bc.getBlockSize());
-
- /* Now decrypt the content */
-
- byte[] dz = new byte[ps.data.length];
-
- for (int i = 0; i < ps.data.length / bc.getBlockSize(); i++)
- {
- bc.transformBlock(ps.data, i * bc.getBlockSize(), dz, i * bc.getBlockSize());
- }
-
- /* Now check and remove RFC 1423/PKCS #7 padding */
-
- dz = removePadding(dz, bc.getBlockSize());
-
- ps.data = dz;
- ps.dekInfo = null;
- ps.procType = null;
- }
-
- public static final boolean isPEMEncrypted(PEMStructure ps) throws IOException
- {
- if (ps.procType == null)
- return false;
-
- if (ps.procType.length != 2)
- throw new IOException("Unknown Proc-Type field.");
-
- if ("4".equals(ps.procType[0]) == false)
- throw new IOException("Unknown Proc-Type field (" + ps.procType[0] + ")");
-
- if ("ENCRYPTED".equals(ps.procType[1]))
- return true;
-
- return false;
- }
-
- public static Object decode(char[] pem, String password) throws IOException
- {
- PEMStructure ps = parsePEM(pem);
-
- if (isPEMEncrypted(ps))
- {
- if (password == null)
- throw new IOException("PEM is encrypted, but no password was specified");
-
- decryptPEM(ps, password.getBytes("ISO-8859-1"));
- }
-
- if (ps.pemType == PEM_DSA_PRIVATE_KEY)
- {
- SimpleDERReader dr = new SimpleDERReader(ps.data);
-
- byte[] seq = dr.readSequenceAsByteArray();
-
- if (dr.available() != 0)
- throw new IOException("Padding in DSA PRIVATE KEY DER stream.");
-
- dr.resetInput(seq);
-
- BigInteger version = dr.readInt();
-
- if (version.compareTo(BigInteger.ZERO) != 0)
- throw new IOException("Wrong version (" + version + ") in DSA PRIVATE KEY DER stream.");
-
- BigInteger p = dr.readInt();
- BigInteger q = dr.readInt();
- BigInteger g = dr.readInt();
- BigInteger y = dr.readInt();
- BigInteger x = dr.readInt();
-
- if (dr.available() != 0)
- throw new IOException("Padding in DSA PRIVATE KEY DER stream.");
-
- return new DSAPrivateKey(p, q, g, y, x);
- }
-
- if (ps.pemType == PEM_RSA_PRIVATE_KEY)
- {
- SimpleDERReader dr = new SimpleDERReader(ps.data);
-
- byte[] seq = dr.readSequenceAsByteArray();
-
- if (dr.available() != 0)
- throw new IOException("Padding in RSA PRIVATE KEY DER stream.");
-
- dr.resetInput(seq);
-
- BigInteger version = dr.readInt();
-
- if ((version.compareTo(BigInteger.ZERO) != 0) && (version.compareTo(BigInteger.ONE) != 0))
- throw new IOException("Wrong version (" + version + ") in RSA PRIVATE KEY DER stream.");
-
- BigInteger n = dr.readInt();
- BigInteger e = dr.readInt();
- BigInteger d = dr.readInt();
-
- return new RSAPrivateKey(d, e, n);
- }
-
- throw new IOException("PEM problem: it is of unknown type");
- }
-
-}
+
+package com.trilead.ssh2.crypto;
+
+import java.io.BufferedReader;
+import java.io.CharArrayReader;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.DigestException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.DSAPrivateKeySpec;
+import java.security.spec.DSAPublicKeySpec;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPrivateKeySpec;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.security.spec.RSAPrivateCrtKeySpec;
+import java.security.spec.RSAPrivateKeySpec;
+import java.security.spec.RSAPublicKeySpec;
+
+import com.trilead.ssh2.crypto.cipher.AES;
+import com.trilead.ssh2.crypto.cipher.BlockCipher;
+import com.trilead.ssh2.crypto.cipher.CBCMode;
+import com.trilead.ssh2.crypto.cipher.DES;
+import com.trilead.ssh2.crypto.cipher.DESede;
+import com.trilead.ssh2.signature.ECDSASHA2Verify;
+
+/**
+ * PEM Support.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PEMDecoder.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
+ */
+public class PEMDecoder
+{
+ public static final int PEM_RSA_PRIVATE_KEY = 1;
+ public static final int PEM_DSA_PRIVATE_KEY = 2;
+ public static final int PEM_EC_PRIVATE_KEY = 3;
+
+ private static final int hexToInt(char c)
+ {
+ if ((c >= 'a') && (c <= 'f'))
+ {
+ return (c - 'a') + 10;
+ }
+
+ if ((c >= 'A') && (c <= 'F'))
+ {
+ return (c - 'A') + 10;
+ }
+
+ if ((c >= '0') && (c <= '9'))
+ {
+ return (c - '0');
+ }
+
+ throw new IllegalArgumentException("Need hex char");
+ }
+
+ private static byte[] hexToByteArray(String hex)
+ {
+ if (hex == null)
+ throw new IllegalArgumentException("null argument");
+
+ if ((hex.length() % 2) != 0)
+ throw new IllegalArgumentException("Uneven string length in hex encoding.");
+
+ byte decoded[] = new byte[hex.length() / 2];
+
+ for (int i = 0; i < decoded.length; i++)
+ {
+ int hi = hexToInt(hex.charAt(i * 2));
+ int lo = hexToInt(hex.charAt((i * 2) + 1));
+
+ decoded[i] = (byte) (hi * 16 + lo);
+ }
+
+ return decoded;
+ }
+
+ private static byte[] generateKeyFromPasswordSaltWithMD5(byte[] password, byte[] salt, int keyLen)
+ throws IOException
+ {
+ if (salt.length < 8)
+ throw new IllegalArgumentException("Salt needs to be at least 8 bytes for key generation.");
+
+ MessageDigest md5;
+ try {
+ md5 = MessageDigest.getInstance("MD5");
+ } catch (NoSuchAlgorithmException e) {
+ throw new IllegalArgumentException("VM does not support MD5", e);
+ }
+
+ byte[] key = new byte[keyLen];
+ byte[] tmp = new byte[md5.getDigestLength()];
+
+ while (true)
+ {
+ md5.update(password, 0, password.length);
+ md5.update(salt, 0, 8); // ARGH we only use the first 8 bytes of the
+ // salt in this step.
+ // This took me two hours until I got AES-xxx running.
+
+ int copy = (keyLen < tmp.length) ? keyLen : tmp.length;
+
+ try {
+ md5.digest(tmp, 0, tmp.length);
+ } catch (DigestException e) {
+ IOException ex = new IOException("could not digest password");
+ ex.initCause(e);
+ throw ex;
+ }
+
+ System.arraycopy(tmp, 0, key, key.length - keyLen, copy);
+
+ keyLen -= copy;
+
+ if (keyLen == 0)
+ return key;
+
+ md5.update(tmp, 0, tmp.length);
+ }
+ }
+
+ private static byte[] removePadding(byte[] buff, int blockSize) throws IOException
+ {
+ /* Removes RFC 1423/PKCS #7 padding */
+
+ int rfc_1423_padding = buff[buff.length - 1] & 0xff;
+
+ if ((rfc_1423_padding < 1) || (rfc_1423_padding > blockSize))
+ throw new IOException("Decrypted PEM has wrong padding, did you specify the correct password?");
+
+ for (int i = 2; i <= rfc_1423_padding; i++)
+ {
+ if (buff[buff.length - i] != rfc_1423_padding)
+ throw new IOException("Decrypted PEM has wrong padding, did you specify the correct password?");
+ }
+
+ byte[] tmp = new byte[buff.length - rfc_1423_padding];
+ System.arraycopy(buff, 0, tmp, 0, buff.length - rfc_1423_padding);
+ return tmp;
+ }
+
+ public static final PEMStructure parsePEM(char[] pem) throws IOException
+ {
+ PEMStructure ps = new PEMStructure();
+
+ String line = null;
+
+ BufferedReader br = new BufferedReader(new CharArrayReader(pem));
+
+ String endLine = null;
+
+ while (true)
+ {
+ line = br.readLine();
+
+ if (line == null)
+ throw new IOException("Invalid PEM structure, '-----BEGIN...' missing");
+
+ line = line.trim();
+
+ if (line.startsWith("-----BEGIN DSA PRIVATE KEY-----"))
+ {
+ endLine = "-----END DSA PRIVATE KEY-----";
+ ps.pemType = PEM_DSA_PRIVATE_KEY;
+ break;
+ }
+
+ if (line.startsWith("-----BEGIN RSA PRIVATE KEY-----"))
+ {
+ endLine = "-----END RSA PRIVATE KEY-----";
+ ps.pemType = PEM_RSA_PRIVATE_KEY;
+ break;
+ }
+
+ if (line.startsWith("-----BEGIN EC PRIVATE KEY-----")) {
+ endLine = "-----END EC PRIVATE KEY-----";
+ ps.pemType = PEM_EC_PRIVATE_KEY;
+ break;
+ }
+ }
+
+ while (true)
+ {
+ line = br.readLine();
+
+ if (line == null)
+ throw new IOException("Invalid PEM structure, " + endLine + " missing");
+
+ line = line.trim();
+
+ int sem_idx = line.indexOf(':');
+
+ if (sem_idx == -1)
+ break;
+
+ String name = line.substring(0, sem_idx + 1);
+ String value = line.substring(sem_idx + 1);
+
+ String values[] = value.split(",");
+
+ for (int i = 0; i < values.length; i++)
+ values[i] = values[i].trim();
+
+ // Proc-Type: 4,ENCRYPTED
+ // DEK-Info: DES-EDE3-CBC,579B6BE3E5C60483
+
+ if ("Proc-Type:".equals(name))
+ {
+ ps.procType = values;
+ continue;
+ }
+
+ if ("DEK-Info:".equals(name))
+ {
+ ps.dekInfo = values;
+ continue;
+ }
+ /* Ignore line */
+ }
+
+ StringBuffer keyData = new StringBuffer();
+
+ while (true)
+ {
+ if (line == null)
+ throw new IOException("Invalid PEM structure, " + endLine + " missing");
+
+ line = line.trim();
+
+ if (line.startsWith(endLine))
+ break;
+
+ keyData.append(line);
+
+ line = br.readLine();
+ }
+
+ char[] pem_chars = new char[keyData.length()];
+ keyData.getChars(0, pem_chars.length, pem_chars, 0);
+
+ ps.data = Base64.decode(pem_chars);
+
+ if (ps.data.length == 0)
+ throw new IOException("Invalid PEM structure, no data available");
+
+ return ps;
+ }
+
+ private static final void decryptPEM(PEMStructure ps, byte[] pw) throws IOException
+ {
+ if (ps.dekInfo == null)
+ throw new IOException("Broken PEM, no mode and salt given, but encryption enabled");
+
+ if (ps.dekInfo.length != 2)
+ throw new IOException("Broken PEM, DEK-Info is incomplete!");
+
+ String algo = ps.dekInfo[0];
+ byte[] salt = hexToByteArray(ps.dekInfo[1]);
+
+ BlockCipher bc = null;
+
+ if (algo.equals("DES-EDE3-CBC"))
+ {
+ DESede des3 = new DESede();
+ des3.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 24));
+ bc = new CBCMode(des3, salt, false);
+ }
+ else if (algo.equals("DES-CBC"))
+ {
+ DES des = new DES();
+ des.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 8));
+ bc = new CBCMode(des, salt, false);
+ }
+ else if (algo.equals("AES-128-CBC"))
+ {
+ AES aes = new AES();
+ aes.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 16));
+ bc = new CBCMode(aes, salt, false);
+ }
+ else if (algo.equals("AES-192-CBC"))
+ {
+ AES aes = new AES();
+ aes.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 24));
+ bc = new CBCMode(aes, salt, false);
+ }
+ else if (algo.equals("AES-256-CBC"))
+ {
+ AES aes = new AES();
+ aes.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 32));
+ bc = new CBCMode(aes, salt, false);
+ }
+ else
+ {
+ throw new IOException("Cannot decrypt PEM structure, unknown cipher " + algo);
+ }
+
+ if ((ps.data.length % bc.getBlockSize()) != 0)
+ throw new IOException("Invalid PEM structure, size of encrypted block is not a multiple of "
+ + bc.getBlockSize());
+
+ /* Now decrypt the content */
+
+ byte[] dz = new byte[ps.data.length];
+
+ for (int i = 0; i < ps.data.length / bc.getBlockSize(); i++)
+ {
+ bc.transformBlock(ps.data, i * bc.getBlockSize(), dz, i * bc.getBlockSize());
+ }
+
+ /* Now check and remove RFC 1423/PKCS #7 padding */
+
+ dz = removePadding(dz, bc.getBlockSize());
+
+ ps.data = dz;
+ ps.dekInfo = null;
+ ps.procType = null;
+ }
+
+ public static final boolean isPEMEncrypted(PEMStructure ps) throws IOException
+ {
+ if (ps.procType == null)
+ return false;
+
+ if (ps.procType.length != 2)
+ throw new IOException("Unknown Proc-Type field.");
+
+ if ("4".equals(ps.procType[0]) == false)
+ throw new IOException("Unknown Proc-Type field (" + ps.procType[0] + ")");
+
+ if ("ENCRYPTED".equals(ps.procType[1]))
+ return true;
+
+ return false;
+ }
+
+ public static KeyPair decode(char[] pem, String password) throws IOException
+ {
+ PEMStructure ps = parsePEM(pem);
+ return decode(ps, password);
+ }
+
+ public static KeyPair decode(PEMStructure ps, String password) throws IOException
+ {
+ if (isPEMEncrypted(ps))
+ {
+ if (password == null)
+ throw new IOException("PEM is encrypted, but no password was specified");
+
+ decryptPEM(ps, password.getBytes("ISO-8859-1"));
+ }
+
+ if (ps.pemType == PEM_DSA_PRIVATE_KEY)
+ {
+ SimpleDERReader dr = new SimpleDERReader(ps.data);
+
+ byte[] seq = dr.readSequenceAsByteArray();
+
+ if (dr.available() != 0)
+ throw new IOException("Padding in DSA PRIVATE KEY DER stream.");
+
+ dr.resetInput(seq);
+
+ BigInteger version = dr.readInt();
+
+ if (version.compareTo(BigInteger.ZERO) != 0)
+ throw new IOException("Wrong version (" + version + ") in DSA PRIVATE KEY DER stream.");
+
+ BigInteger p = dr.readInt();
+ BigInteger q = dr.readInt();
+ BigInteger g = dr.readInt();
+ BigInteger y = dr.readInt();
+ BigInteger x = dr.readInt();
+
+ if (dr.available() != 0)
+ throw new IOException("Padding in DSA PRIVATE KEY DER stream.");
+
+ DSAPrivateKeySpec privSpec = new DSAPrivateKeySpec(x, p, q, g);
+ DSAPublicKeySpec pubSpec = new DSAPublicKeySpec(y, p, q, g);
+
+ return generateKeyPair("DSA", privSpec, pubSpec);
+ }
+
+ if (ps.pemType == PEM_RSA_PRIVATE_KEY)
+ {
+ SimpleDERReader dr = new SimpleDERReader(ps.data);
+
+ byte[] seq = dr.readSequenceAsByteArray();
+
+ if (dr.available() != 0)
+ throw new IOException("Padding in RSA PRIVATE KEY DER stream.");
+
+ dr.resetInput(seq);
+
+ BigInteger version = dr.readInt();
+
+ if ((version.compareTo(BigInteger.ZERO) != 0) && (version.compareTo(BigInteger.ONE) != 0))
+ throw new IOException("Wrong version (" + version + ") in RSA PRIVATE KEY DER stream.");
+
+ BigInteger n = dr.readInt();
+ BigInteger e = dr.readInt();
+ BigInteger d = dr.readInt();
+ // TODO: is this right?
+ BigInteger primeP = dr.readInt();
+ BigInteger primeQ = dr.readInt();
+ BigInteger expP = dr.readInt();
+ BigInteger expQ = dr.readInt();
+ BigInteger coeff = dr.readInt();
+
+ RSAPrivateKeySpec privSpec = new RSAPrivateCrtKeySpec(n, e, d, primeP, primeQ, expP, expQ, coeff);
+ RSAPublicKeySpec pubSpec = new RSAPublicKeySpec(n, e);
+
+ return generateKeyPair("RSA", privSpec, pubSpec);
+ }
+
+ if (ps.pemType == PEM_EC_PRIVATE_KEY) {
+ SimpleDERReader dr = new SimpleDERReader(ps.data);
+
+ byte[] seq = dr.readSequenceAsByteArray();
+
+ if (dr.available() != 0)
+ throw new IOException("Padding in EC PRIVATE KEY DER stream.");
+
+ dr.resetInput(seq);
+
+ BigInteger version = dr.readInt();
+
+ if ((version.compareTo(BigInteger.ONE) != 0))
+ throw new IOException("Wrong version (" + version + ") in EC PRIVATE KEY DER stream.");
+
+ byte[] privateBytes = dr.readOctetString();
+
+ String curveOid = null;
+ byte[] publicBytes = null;
+ while (dr.available() > 0) {
+ int type = dr.readConstructedType();
+ SimpleDERReader cr = dr.readConstructed();
+ switch (type) {
+ case 0:
+ curveOid = cr.readOid();
+ break;
+ case 1:
+ publicBytes = cr.readOctetString();
+ break;
+ }
+ }
+
+ ECParameterSpec params = ECDSASHA2Verify.getCurveForOID(curveOid);
+ if (params == null)
+ throw new IOException("invalid OID");
+
+ BigInteger s = new BigInteger(privateBytes);
+ byte[] publicBytesSlice = new byte[publicBytes.length - 1];
+ System.arraycopy(publicBytes, 1, publicBytesSlice, 0, publicBytesSlice.length);
+ ECPoint w = ECDSASHA2Verify.decodeECPoint(publicBytesSlice, params.getCurve());
+
+ ECPrivateKeySpec privSpec = new ECPrivateKeySpec(s, params);
+ ECPublicKeySpec pubSpec = new ECPublicKeySpec(w, params);
+
+ return generateKeyPair("EC", privSpec, pubSpec);
+ }
+
+ throw new IOException("PEM problem: it is of unknown type");
+ }
+
+ /**
+ * Generate a {@code KeyPair} given an {@code algorithm} and {@code KeySpec}.
+ */
+ private static KeyPair generateKeyPair(String algorithm, KeySpec privSpec, KeySpec pubSpec)
+ throws IOException {
+ try {
+ final KeyFactory kf = KeyFactory.getInstance(algorithm);
+ final PublicKey pubKey = kf.generatePublic(pubSpec);
+ final PrivateKey privKey = kf.generatePrivate(privSpec);
+ return new KeyPair(pubKey, privKey);
+ } catch (NoSuchAlgorithmException ex) {
+ IOException ioex = new IOException();
+ ioex.initCause(ex);
+ throw ioex;
+ } catch (InvalidKeySpecException ex) {
+ IOException ioex = new IOException("invalid keyspec");
+ ioex.initCause(ex);
+ throw ioex;
+ }
+ }
+}
diff --git a/src/com/trilead/ssh2/crypto/PEMStructure.java b/src/com/trilead/ssh2/crypto/PEMStructure.java
index 6b657e8..83fb799 100644
--- a/src/com/trilead/ssh2/crypto/PEMStructure.java
+++ b/src/com/trilead/ssh2/crypto/PEMStructure.java
@@ -1,17 +1,17 @@
-
-package com.trilead.ssh2.crypto;
-
-/**
- * Parsed PEM structure.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PEMStructure.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-
-public class PEMStructure
-{
- public int pemType;
- String dekInfo[];
- String procType[];
- public byte[] data;
+
+package com.trilead.ssh2.crypto;
+
+/**
+ * Parsed PEM structure.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PEMStructure.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+
+public class PEMStructure
+{
+ public int pemType;
+ String dekInfo[];
+ String procType[];
+ public byte[] data;
} \ No newline at end of file
diff --git a/src/com/trilead/ssh2/crypto/SimpleDERReader.java b/src/com/trilead/ssh2/crypto/SimpleDERReader.java
index 55c6c6a..ff8112a 100644
--- a/src/com/trilead/ssh2/crypto/SimpleDERReader.java
+++ b/src/com/trilead/ssh2/crypto/SimpleDERReader.java
@@ -1,160 +1,229 @@
-package com.trilead.ssh2.crypto;
-
-import java.io.IOException;
-
-import java.math.BigInteger;
-
-/**
- * SimpleDERReader.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: SimpleDERReader.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public class SimpleDERReader
-{
- byte[] buffer;
- int pos;
- int count;
-
- public SimpleDERReader(byte[] b)
- {
- resetInput(b);
- }
-
- public SimpleDERReader(byte[] b, int off, int len)
- {
- resetInput(b, off, len);
- }
-
- public void resetInput(byte[] b)
- {
- resetInput(b, 0, b.length);
- }
-
- public void resetInput(byte[] b, int off, int len)
- {
- buffer = b;
- pos = off;
- count = len;
- }
-
- private byte readByte() throws IOException
- {
- if (count <= 0)
- throw new IOException("DER byte array: out of data");
- count--;
- return buffer[pos++];
- }
-
- private byte[] readBytes(int len) throws IOException
- {
- if (len > count)
- throw new IOException("DER byte array: out of data");
-
- byte[] b = new byte[len];
-
- System.arraycopy(buffer, pos, b, 0, len);
-
- pos += len;
- count -= len;
-
- return b;
- }
-
- public int available()
- {
- return count;
- }
-
- private int readLength() throws IOException
- {
- int len = readByte() & 0xff;
-
- if ((len & 0x80) == 0)
- return len;
-
- int remain = len & 0x7F;
-
- if (remain == 0)
- return -1;
-
- len = 0;
-
- while (remain > 0)
- {
- len = len << 8;
- len = len | (readByte() & 0xff);
- remain--;
- }
-
- return len;
- }
-
- public int ignoreNextObject() throws IOException
- {
- int type = readByte() & 0xff;
-
- int len = readLength();
-
- if ((len < 0) || len > available())
- throw new IOException("Illegal len in DER object (" + len + ")");
-
- readBytes(len);
-
- return type;
- }
-
- public BigInteger readInt() throws IOException
- {
- int type = readByte() & 0xff;
-
- if (type != 0x02)
- throw new IOException("Expected DER Integer, but found type " + type);
-
- int len = readLength();
-
- if ((len < 0) || len > available())
- throw new IOException("Illegal len in DER object (" + len + ")");
-
- byte[] b = readBytes(len);
-
- BigInteger bi = new BigInteger(b);
-
- return bi;
- }
-
- public byte[] readSequenceAsByteArray() throws IOException
- {
- int type = readByte() & 0xff;
-
- if (type != 0x30)
- throw new IOException("Expected DER Sequence, but found type " + type);
-
- int len = readLength();
-
- if ((len < 0) || len > available())
- throw new IOException("Illegal len in DER object (" + len + ")");
-
- byte[] b = readBytes(len);
-
- return b;
- }
-
- public byte[] readOctetString() throws IOException
- {
- int type = readByte() & 0xff;
-
- if (type != 0x04)
- throw new IOException("Expected DER Octetstring, but found type " + type);
-
- int len = readLength();
-
- if ((len < 0) || len > available())
- throw new IOException("Illegal len in DER object (" + len + ")");
-
- byte[] b = readBytes(len);
-
- return b;
- }
-
-}
+package com.trilead.ssh2.crypto;
+
+import java.io.IOException;
+
+import java.math.BigInteger;
+
+/**
+ * SimpleDERReader.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: SimpleDERReader.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+public class SimpleDERReader
+{
+ private static final int CONSTRUCTED = 0x20;
+
+ byte[] buffer;
+ int pos;
+ int count;
+
+ public SimpleDERReader(byte[] b)
+ {
+ resetInput(b);
+ }
+
+ public SimpleDERReader(byte[] b, int off, int len)
+ {
+ resetInput(b, off, len);
+ }
+
+ public void resetInput(byte[] b)
+ {
+ resetInput(b, 0, b.length);
+ }
+
+ public void resetInput(byte[] b, int off, int len)
+ {
+ buffer = b;
+ pos = off;
+ count = len;
+ }
+
+ private byte readByte() throws IOException
+ {
+ if (count <= 0)
+ throw new IOException("DER byte array: out of data");
+ count--;
+ return buffer[pos++];
+ }
+
+ private byte[] readBytes(int len) throws IOException
+ {
+ if (len > count)
+ throw new IOException("DER byte array: out of data");
+
+ byte[] b = new byte[len];
+
+ System.arraycopy(buffer, pos, b, 0, len);
+
+ pos += len;
+ count -= len;
+
+ return b;
+ }
+
+ public int available()
+ {
+ return count;
+ }
+
+ private int readLength() throws IOException
+ {
+ int len = readByte() & 0xff;
+
+ if ((len & 0x80) == 0)
+ return len;
+
+ int remain = len & 0x7F;
+
+ if (remain == 0)
+ return -1;
+
+ len = 0;
+
+ while (remain > 0)
+ {
+ len = len << 8;
+ len = len | (readByte() & 0xff);
+ remain--;
+ }
+
+ return len;
+ }
+
+ public int ignoreNextObject() throws IOException
+ {
+ int type = readByte() & 0xff;
+
+ int len = readLength();
+
+ if ((len < 0) || len > available())
+ throw new IOException("Illegal len in DER object (" + len + ")");
+
+ readBytes(len);
+
+ return type;
+ }
+
+ public BigInteger readInt() throws IOException
+ {
+ int type = readByte() & 0xff;
+
+ if (type != 0x02)
+ throw new IOException("Expected DER Integer, but found type " + type);
+
+ int len = readLength();
+
+ if ((len < 0) || len > available())
+ throw new IOException("Illegal len in DER object (" + len + ")");
+
+ byte[] b = readBytes(len);
+
+ BigInteger bi = new BigInteger(b);
+
+ return bi;
+ }
+
+ public int readConstructedType() throws IOException {
+ int type = readByte() & 0xff;
+
+ if ((type & CONSTRUCTED) != CONSTRUCTED)
+ throw new IOException("Expected constructed type, but was " + type);
+
+ return type & 0x1f;
+ }
+
+ public SimpleDERReader readConstructed() throws IOException
+ {
+ int len = readLength();
+
+ if ((len < 0) || len > available())
+ throw new IOException("Illegal len in DER object (" + len + ")");
+
+ SimpleDERReader cr = new SimpleDERReader(buffer, pos, len);
+
+ pos += len;
+ count -= len;
+
+ return cr;
+ }
+
+ public byte[] readSequenceAsByteArray() throws IOException
+ {
+ int type = readByte() & 0xff;
+
+ if (type != 0x30)
+ throw new IOException("Expected DER Sequence, but found type " + type);
+
+ int len = readLength();
+
+ if ((len < 0) || len > available())
+ throw new IOException("Illegal len in DER object (" + len + ")");
+
+ byte[] b = readBytes(len);
+
+ return b;
+ }
+
+ public String readOid() throws IOException
+ {
+ int type = readByte() & 0xff;
+
+ if (type != 0x06)
+ throw new IOException("Expected DER OID, but found type " + type);
+
+ int len = readLength();
+
+ if ((len < 1) || len > available())
+ throw new IOException("Illegal len in DER object (" + len + ")");
+
+ byte[] b = readBytes(len);
+
+ long value = 0;
+
+ StringBuilder sb = new StringBuilder(64);
+ switch(b[0] / 40) {
+ case 0:
+ sb.append('0');
+ break;
+ case 1:
+ sb.append('1');
+ b[0] -= 40;
+ break;
+ default:
+ sb.append('2');
+ b[0] -= 80;
+ break;
+ }
+
+ for (int i = 0; i < len; i++) {
+ value = (value << 7) + (b[i] & 0x7F);
+ if ((b[i] & 0x80) == 0) {
+ sb.append('.');
+ sb.append(value);
+ value = 0;
+ }
+ }
+
+ return sb.toString();
+ }
+
+ public byte[] readOctetString() throws IOException
+ {
+ int type = readByte() & 0xff;
+
+ if (type != 0x04 && type != 0x03)
+ throw new IOException("Expected DER Octetstring, but found type " + type);
+
+ int len = readLength();
+
+ if ((len < 0) || len > available())
+ throw new IOException("Illegal len in DER object (" + len + ")");
+
+ byte[] b = readBytes(len);
+
+ return b;
+ }
+
+}
diff --git a/src/com/trilead/ssh2/crypto/cipher/AES.java b/src/com/trilead/ssh2/crypto/cipher/AES.java
index 15fb410..e89e4a6 100644
--- a/src/com/trilead/ssh2/crypto/cipher/AES.java
+++ b/src/com/trilead/ssh2/crypto/cipher/AES.java
@@ -1,698 +1,698 @@
-
-package com.trilead.ssh2.crypto.cipher;
-
-/*
- This file was shamelessly taken from the Bouncy Castle Crypto package.
- Their licence file states the following:
-
- Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle
- (http://www.bouncycastle.org)
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
- */
-
-/**
- * An implementation of the AES (Rijndael), from FIPS-197.
- * <p>
- * For further details see: <a
- * href="http://csrc.nist.gov/encryption/aes/">http://csrc.nist.gov/encryption/aes/
- * </a>.
- *
- * This implementation is based on optimizations from Dr. Brian Gladman's paper
- * and C code at <a
- * href="http://fp.gladman.plus.com/cryptography_technology/rijndael/">http://fp.gladman.plus.com/cryptography_technology/rijndael/
- * </a>
- *
- * There are three levels of tradeoff of speed vs memory Because java has no
- * preprocessor, they are written as three separate classes from which to choose
- *
- * The fastest uses 8Kbytes of static tables to precompute round calculations, 4
- * 256 word tables for encryption and 4 for decryption.
- *
- * The middle performance version uses only one 256 word table for each, for a
- * total of 2Kbytes, adding 12 rotate operations per round to compute the values
- * contained in the other tables from the contents of the first
- *
- * The slowest version uses no static tables at all and computes the values in
- * each round
- * <p>
- * This file contains the fast version with 8Kbytes of static tables for round
- * precomputation
- *
- * @author See comments in the source file
- * @version $Id: AES.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class AES implements BlockCipher
-{
- // The S box
- private static final byte[] S = { (byte) 99, (byte) 124, (byte) 119, (byte) 123, (byte) 242, (byte) 107,
- (byte) 111, (byte) 197, (byte) 48, (byte) 1, (byte) 103, (byte) 43, (byte) 254, (byte) 215, (byte) 171,
- (byte) 118, (byte) 202, (byte) 130, (byte) 201, (byte) 125, (byte) 250, (byte) 89, (byte) 71, (byte) 240,
- (byte) 173, (byte) 212, (byte) 162, (byte) 175, (byte) 156, (byte) 164, (byte) 114, (byte) 192, (byte) 183,
- (byte) 253, (byte) 147, (byte) 38, (byte) 54, (byte) 63, (byte) 247, (byte) 204, (byte) 52, (byte) 165,
- (byte) 229, (byte) 241, (byte) 113, (byte) 216, (byte) 49, (byte) 21, (byte) 4, (byte) 199, (byte) 35,
- (byte) 195, (byte) 24, (byte) 150, (byte) 5, (byte) 154, (byte) 7, (byte) 18, (byte) 128, (byte) 226,
- (byte) 235, (byte) 39, (byte) 178, (byte) 117, (byte) 9, (byte) 131, (byte) 44, (byte) 26, (byte) 27,
- (byte) 110, (byte) 90, (byte) 160, (byte) 82, (byte) 59, (byte) 214, (byte) 179, (byte) 41, (byte) 227,
- (byte) 47, (byte) 132, (byte) 83, (byte) 209, (byte) 0, (byte) 237, (byte) 32, (byte) 252, (byte) 177,
- (byte) 91, (byte) 106, (byte) 203, (byte) 190, (byte) 57, (byte) 74, (byte) 76, (byte) 88, (byte) 207,
- (byte) 208, (byte) 239, (byte) 170, (byte) 251, (byte) 67, (byte) 77, (byte) 51, (byte) 133, (byte) 69,
- (byte) 249, (byte) 2, (byte) 127, (byte) 80, (byte) 60, (byte) 159, (byte) 168, (byte) 81, (byte) 163,
- (byte) 64, (byte) 143, (byte) 146, (byte) 157, (byte) 56, (byte) 245, (byte) 188, (byte) 182, (byte) 218,
- (byte) 33, (byte) 16, (byte) 255, (byte) 243, (byte) 210, (byte) 205, (byte) 12, (byte) 19, (byte) 236,
- (byte) 95, (byte) 151, (byte) 68, (byte) 23, (byte) 196, (byte) 167, (byte) 126, (byte) 61, (byte) 100,
- (byte) 93, (byte) 25, (byte) 115, (byte) 96, (byte) 129, (byte) 79, (byte) 220, (byte) 34, (byte) 42,
- (byte) 144, (byte) 136, (byte) 70, (byte) 238, (byte) 184, (byte) 20, (byte) 222, (byte) 94, (byte) 11,
- (byte) 219, (byte) 224, (byte) 50, (byte) 58, (byte) 10, (byte) 73, (byte) 6, (byte) 36, (byte) 92,
- (byte) 194, (byte) 211, (byte) 172, (byte) 98, (byte) 145, (byte) 149, (byte) 228, (byte) 121, (byte) 231,
- (byte) 200, (byte) 55, (byte) 109, (byte) 141, (byte) 213, (byte) 78, (byte) 169, (byte) 108, (byte) 86,
- (byte) 244, (byte) 234, (byte) 101, (byte) 122, (byte) 174, (byte) 8, (byte) 186, (byte) 120, (byte) 37,
- (byte) 46, (byte) 28, (byte) 166, (byte) 180, (byte) 198, (byte) 232, (byte) 221, (byte) 116, (byte) 31,
- (byte) 75, (byte) 189, (byte) 139, (byte) 138, (byte) 112, (byte) 62, (byte) 181, (byte) 102, (byte) 72,
- (byte) 3, (byte) 246, (byte) 14, (byte) 97, (byte) 53, (byte) 87, (byte) 185, (byte) 134, (byte) 193,
- (byte) 29, (byte) 158, (byte) 225, (byte) 248, (byte) 152, (byte) 17, (byte) 105, (byte) 217, (byte) 142,
- (byte) 148, (byte) 155, (byte) 30, (byte) 135, (byte) 233, (byte) 206, (byte) 85, (byte) 40, (byte) 223,
- (byte) 140, (byte) 161, (byte) 137, (byte) 13, (byte) 191, (byte) 230, (byte) 66, (byte) 104, (byte) 65,
- (byte) 153, (byte) 45, (byte) 15, (byte) 176, (byte) 84, (byte) 187, (byte) 22, };
-
- // The inverse S-box
- private static final byte[] Si = { (byte) 82, (byte) 9, (byte) 106, (byte) 213, (byte) 48, (byte) 54, (byte) 165,
- (byte) 56, (byte) 191, (byte) 64, (byte) 163, (byte) 158, (byte) 129, (byte) 243, (byte) 215, (byte) 251,
- (byte) 124, (byte) 227, (byte) 57, (byte) 130, (byte) 155, (byte) 47, (byte) 255, (byte) 135, (byte) 52,
- (byte) 142, (byte) 67, (byte) 68, (byte) 196, (byte) 222, (byte) 233, (byte) 203, (byte) 84, (byte) 123,
- (byte) 148, (byte) 50, (byte) 166, (byte) 194, (byte) 35, (byte) 61, (byte) 238, (byte) 76, (byte) 149,
- (byte) 11, (byte) 66, (byte) 250, (byte) 195, (byte) 78, (byte) 8, (byte) 46, (byte) 161, (byte) 102,
- (byte) 40, (byte) 217, (byte) 36, (byte) 178, (byte) 118, (byte) 91, (byte) 162, (byte) 73, (byte) 109,
- (byte) 139, (byte) 209, (byte) 37, (byte) 114, (byte) 248, (byte) 246, (byte) 100, (byte) 134, (byte) 104,
- (byte) 152, (byte) 22, (byte) 212, (byte) 164, (byte) 92, (byte) 204, (byte) 93, (byte) 101, (byte) 182,
- (byte) 146, (byte) 108, (byte) 112, (byte) 72, (byte) 80, (byte) 253, (byte) 237, (byte) 185, (byte) 218,
- (byte) 94, (byte) 21, (byte) 70, (byte) 87, (byte) 167, (byte) 141, (byte) 157, (byte) 132, (byte) 144,
- (byte) 216, (byte) 171, (byte) 0, (byte) 140, (byte) 188, (byte) 211, (byte) 10, (byte) 247, (byte) 228,
- (byte) 88, (byte) 5, (byte) 184, (byte) 179, (byte) 69, (byte) 6, (byte) 208, (byte) 44, (byte) 30,
- (byte) 143, (byte) 202, (byte) 63, (byte) 15, (byte) 2, (byte) 193, (byte) 175, (byte) 189, (byte) 3,
- (byte) 1, (byte) 19, (byte) 138, (byte) 107, (byte) 58, (byte) 145, (byte) 17, (byte) 65, (byte) 79,
- (byte) 103, (byte) 220, (byte) 234, (byte) 151, (byte) 242, (byte) 207, (byte) 206, (byte) 240, (byte) 180,
- (byte) 230, (byte) 115, (byte) 150, (byte) 172, (byte) 116, (byte) 34, (byte) 231, (byte) 173, (byte) 53,
- (byte) 133, (byte) 226, (byte) 249, (byte) 55, (byte) 232, (byte) 28, (byte) 117, (byte) 223, (byte) 110,
- (byte) 71, (byte) 241, (byte) 26, (byte) 113, (byte) 29, (byte) 41, (byte) 197, (byte) 137, (byte) 111,
- (byte) 183, (byte) 98, (byte) 14, (byte) 170, (byte) 24, (byte) 190, (byte) 27, (byte) 252, (byte) 86,
- (byte) 62, (byte) 75, (byte) 198, (byte) 210, (byte) 121, (byte) 32, (byte) 154, (byte) 219, (byte) 192,
- (byte) 254, (byte) 120, (byte) 205, (byte) 90, (byte) 244, (byte) 31, (byte) 221, (byte) 168, (byte) 51,
- (byte) 136, (byte) 7, (byte) 199, (byte) 49, (byte) 177, (byte) 18, (byte) 16, (byte) 89, (byte) 39,
- (byte) 128, (byte) 236, (byte) 95, (byte) 96, (byte) 81, (byte) 127, (byte) 169, (byte) 25, (byte) 181,
- (byte) 74, (byte) 13, (byte) 45, (byte) 229, (byte) 122, (byte) 159, (byte) 147, (byte) 201, (byte) 156,
- (byte) 239, (byte) 160, (byte) 224, (byte) 59, (byte) 77, (byte) 174, (byte) 42, (byte) 245, (byte) 176,
- (byte) 200, (byte) 235, (byte) 187, (byte) 60, (byte) 131, (byte) 83, (byte) 153, (byte) 97, (byte) 23,
- (byte) 43, (byte) 4, (byte) 126, (byte) 186, (byte) 119, (byte) 214, (byte) 38, (byte) 225, (byte) 105,
- (byte) 20, (byte) 99, (byte) 85, (byte) 33, (byte) 12, (byte) 125, };
-
- // vector used in calculating key schedule (powers of x in GF(256))
- private static final int[] rcon = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab,
- 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 };
-
- // precomputation tables of calculations for rounds
- private static final int[] T0 = { 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff, 0xbd6b6bd6,
- 0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102, 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d,
- 0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb,
- 0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, 0x967272e4, 0x5bc0c09b, 0xc2b7b775,
- 0x1cfdfde1, 0xae93933d, 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83, 0x5c343468, 0xf4a5a551,
- 0x34e5e5d1, 0x08f1f1f9, 0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a, 0x0c040408, 0x52c7c795, 0x65232346,
- 0x5ec3c39d, 0x28181830, 0xa1969637, 0x0f05050a, 0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df,
- 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912, 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36,
- 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d, 0x7b292952, 0x3ee3e3dd,
- 0x712f2f5e, 0x97848413, 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, 0x60202040, 0x1ffcfce3, 0xc8b1b179,
- 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d, 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85,
- 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a,
- 0x10f9f9e9, 0x06020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d,
- 0xc0404080, 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1, 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf,
- 0x63212142, 0x30101020, 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3,
- 0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, 0xac6464c8,
- 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54,
- 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16,
- 0x76dbdbad, 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, 0x0a06060c, 0x6c242448, 0xe45c5cb8,
- 0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3, 0x8b7979f2, 0x32e7e7d5,
- 0x43c8c88b, 0x5937376e, 0xb76d6dda, 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8, 0xfa5656ac,
- 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a,
- 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697, 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e,
- 0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c, 0xc4b5b571, 0xaa6666cc, 0xd8484890,
- 0x05030306, 0x01f6f6f7, 0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, 0x91868617, 0x58c1c199,
- 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9, 0x898e8e07,
- 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715, 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5,
- 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, 0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182,
- 0xb0999929, 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c };
-
- private static final int[] T1 = { 0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d, 0x6b6bd6bd,
- 0x6f6fdeb1, 0xc5c59154, 0x30306050, 0x01010203, 0x6767cea9, 0x2b2b567d, 0xfefee719, 0xd7d7b562, 0xabab4de6,
- 0x7676ec9a, 0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87, 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b,
- 0xadad41ec, 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7, 0x7272e496, 0xc0c09b5b, 0xb7b775c2,
- 0xfdfde11c, 0x93933dae, 0x26264c6a, 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f, 0x3434685c, 0xa5a551f4,
- 0xe5e5d134, 0xf1f1f908, 0x7171e293, 0xd8d8ab73, 0x31316253, 0x15152a3f, 0x0404080c, 0xc7c79552, 0x23234665,
- 0xc3c39d5e, 0x18183028, 0x969637a1, 0x05050a0f, 0x9a9a2fb5, 0x07070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d,
- 0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, 0x0909121b, 0x83831d9e, 0x2c2c5874, 0x1a1a342e, 0x1b1b362d,
- 0x6e6edcb2, 0x5a5ab4ee, 0xa0a05bfb, 0x5252a4f6, 0x3b3b764d, 0xd6d6b761, 0xb3b37dce, 0x2929527b, 0xe3e3dd3e,
- 0x2f2f5e71, 0x84841397, 0x5353a6f5, 0xd1d1b968, 0x00000000, 0xededc12c, 0x20204060, 0xfcfce31f, 0xb1b179c8,
- 0x5b5bb6ed, 0x6a6ad4be, 0xcbcb8d46, 0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, 0xcfcf854a,
- 0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16, 0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194, 0x45458acf,
- 0xf9f9e910, 0x02020406, 0x7f7ffe81, 0x5050a0f0, 0x3c3c7844, 0x9f9f25ba, 0xa8a84be3, 0x5151a2f3, 0xa3a35dfe,
- 0x404080c0, 0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104, 0xbcbc63df, 0xb6b677c1, 0xdadaaf75,
- 0x21214263, 0x10102030, 0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d, 0xcdcd814c, 0x0c0c1814, 0x13132635, 0xececc32f,
- 0x5f5fbee1, 0x979735a2, 0x444488cc, 0x17172e39, 0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47, 0x6464c8ac,
- 0x5d5dbae7, 0x1919322b, 0x7373e695, 0x6060c0a0, 0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e,
- 0x90903bab, 0x88880b83, 0x46468cca, 0xeeeec729, 0xb8b86bd3, 0x1414283c, 0xdedea779, 0x5e5ebce2, 0x0b0b161d,
- 0xdbdbad76, 0xe0e0db3b, 0x32326456, 0x3a3a744e, 0x0a0a141e, 0x494992db, 0x06060c0a, 0x2424486c, 0x5c5cb8e4,
- 0xc2c29f5d, 0xd3d3bd6e, 0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4, 0xe4e4d337, 0x7979f28b, 0xe7e7d532,
- 0xc8c88b43, 0x37376e59, 0x6d6ddab7, 0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, 0x6c6cd8b4, 0x5656acfa,
- 0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e, 0xaeae47e9, 0x08081018, 0xbaba6fd5, 0x7878f088, 0x25254a6f,
- 0x2e2e5c72, 0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751, 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21,
- 0x4b4b96dd, 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, 0x7070e090, 0x3e3e7c42, 0xb5b571c4, 0x6666ccaa, 0x484890d8,
- 0x03030605, 0xf6f6f701, 0x0e0e1c12, 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0, 0x86861791, 0xc1c19958,
- 0x1d1d3a27, 0x9e9e27b9, 0xe1e1d938, 0xf8f8eb13, 0x98982bb3, 0x11112233, 0x6969d2bb, 0xd9d9a970, 0x8e8e0789,
- 0x949433a7, 0x9b9b2db6, 0x1e1e3c22, 0x87871592, 0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a,
- 0x8c8c038f, 0xa1a159f8, 0x89890980, 0x0d0d1a17, 0xbfbf65da, 0xe6e6d731, 0x424284c6, 0x6868d0b8, 0x414182c3,
- 0x999929b0, 0x2d2d5a77, 0x0f0f1e11, 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, 0x16162c3a };
-
- private static final int[] T2 = { 0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2, 0x6bd6bd6b,
- 0x6fdeb16f, 0xc59154c5, 0x30605030, 0x01020301, 0x67cea967, 0x2b567d2b, 0xfee719fe, 0xd7b562d7, 0xab4de6ab,
- 0x76ec9a76, 0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d, 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0,
- 0xad41ecad, 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4, 0x72e49672, 0xc09b5bc0, 0xb775c2b7,
- 0xfde11cfd, 0x933dae93, 0x264c6a26, 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc, 0x34685c34, 0xa551f4a5,
- 0xe5d134e5, 0xf1f908f1, 0x71e29371, 0xd8ab73d8, 0x31625331, 0x152a3f15, 0x04080c04, 0xc79552c7, 0x23466523,
- 0xc39d5ec3, 0x18302818, 0x9637a196, 0x050a0f05, 0x9a2fb59a, 0x070e0907, 0x12243612, 0x801b9b80, 0xe2df3de2,
- 0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, 0x09121b09, 0x831d9e83, 0x2c58742c, 0x1a342e1a, 0x1b362d1b,
- 0x6edcb26e, 0x5ab4ee5a, 0xa05bfba0, 0x52a4f652, 0x3b764d3b, 0xd6b761d6, 0xb37dceb3, 0x29527b29, 0xe3dd3ee3,
- 0x2f5e712f, 0x84139784, 0x53a6f553, 0xd1b968d1, 0x00000000, 0xedc12ced, 0x20406020, 0xfce31ffc, 0xb179c8b1,
- 0x5bb6ed5b, 0x6ad4be6a, 0xcb8d46cb, 0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858, 0xcf854acf,
- 0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb, 0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485, 0x458acf45,
- 0xf9e910f9, 0x02040602, 0x7ffe817f, 0x50a0f050, 0x3c78443c, 0x9f25ba9f, 0xa84be3a8, 0x51a2f351, 0xa35dfea3,
- 0x4080c040, 0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5, 0xbc63dfbc, 0xb677c1b6, 0xdaaf75da,
- 0x21426321, 0x10203010, 0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2, 0xcd814ccd, 0x0c18140c, 0x13263513, 0xecc32fec,
- 0x5fbee15f, 0x9735a297, 0x4488cc44, 0x172e3917, 0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d, 0x64c8ac64,
- 0x5dbae75d, 0x19322b19, 0x73e69573, 0x60c0a060, 0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a,
- 0x903bab90, 0x880b8388, 0x468cca46, 0xeec729ee, 0xb86bd3b8, 0x14283c14, 0xdea779de, 0x5ebce25e, 0x0b161d0b,
- 0xdbad76db, 0xe0db3be0, 0x32645632, 0x3a744e3a, 0x0a141e0a, 0x4992db49, 0x060c0a06, 0x24486c24, 0x5cb8e45c,
- 0xc29f5dc2, 0xd3bd6ed3, 0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495, 0xe4d337e4, 0x79f28b79, 0xe7d532e7,
- 0xc88b43c8, 0x376e5937, 0x6ddab76d, 0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, 0x6cd8b46c, 0x56acfa56,
- 0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a, 0xae47e9ae, 0x08101808, 0xba6fd5ba, 0x78f08878, 0x254a6f25,
- 0x2e5c722e, 0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6, 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f,
- 0x4b96dd4b, 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, 0x70e09070, 0x3e7c423e, 0xb571c4b5, 0x66ccaa66, 0x4890d848,
- 0x03060503, 0xf6f701f6, 0x0e1c120e, 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9, 0x86179186, 0xc19958c1,
- 0x1d3a271d, 0x9e27b99e, 0xe1d938e1, 0xf8eb13f8, 0x982bb398, 0x11223311, 0x69d2bb69, 0xd9a970d9, 0x8e07898e,
- 0x9433a794, 0x9b2db69b, 0x1e3c221e, 0x87159287, 0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf,
- 0x8c038f8c, 0xa159f8a1, 0x89098089, 0x0d1a170d, 0xbf65dabf, 0xe6d731e6, 0x4284c642, 0x68d0b868, 0x4182c341,
- 0x9929b099, 0x2d5a772d, 0x0f1e110f, 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, 0x162c3a16 };
-
- private static final int[] T3 = { 0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2, 0xd6bd6b6b,
- 0xdeb16f6f, 0x9154c5c5, 0x60503030, 0x02030101, 0xcea96767, 0x567d2b2b, 0xe719fefe, 0xb562d7d7, 0x4de6abab,
- 0xec9a7676, 0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d, 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0,
- 0x41ecadad, 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4, 0xe4967272, 0x9b5bc0c0, 0x75c2b7b7,
- 0xe11cfdfd, 0x3dae9393, 0x4c6a2626, 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc, 0x685c3434, 0x51f4a5a5,
- 0xd134e5e5, 0xf908f1f1, 0xe2937171, 0xab73d8d8, 0x62533131, 0x2a3f1515, 0x080c0404, 0x9552c7c7, 0x46652323,
- 0x9d5ec3c3, 0x30281818, 0x37a19696, 0x0a0f0505, 0x2fb59a9a, 0x0e090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2,
- 0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, 0x121b0909, 0x1d9e8383, 0x58742c2c, 0x342e1a1a, 0x362d1b1b,
- 0xdcb26e6e, 0xb4ee5a5a, 0x5bfba0a0, 0xa4f65252, 0x764d3b3b, 0xb761d6d6, 0x7dceb3b3, 0x527b2929, 0xdd3ee3e3,
- 0x5e712f2f, 0x13978484, 0xa6f55353, 0xb968d1d1, 0x00000000, 0xc12ceded, 0x40602020, 0xe31ffcfc, 0x79c8b1b1,
- 0xb6ed5b5b, 0xd4be6a6a, 0x8d46cbcb, 0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858, 0x854acfcf,
- 0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb, 0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585, 0x8acf4545,
- 0xe910f9f9, 0x04060202, 0xfe817f7f, 0xa0f05050, 0x78443c3c, 0x25ba9f9f, 0x4be3a8a8, 0xa2f35151, 0x5dfea3a3,
- 0x80c04040, 0x058a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5, 0x63dfbcbc, 0x77c1b6b6, 0xaf75dada,
- 0x42632121, 0x20301010, 0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2, 0x814ccdcd, 0x18140c0c, 0x26351313, 0xc32fecec,
- 0xbee15f5f, 0x35a29797, 0x88cc4444, 0x2e391717, 0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d, 0xc8ac6464,
- 0xbae75d5d, 0x322b1919, 0xe6957373, 0xc0a06060, 0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a,
- 0x3bab9090, 0x0b838888, 0x8cca4646, 0xc729eeee, 0x6bd3b8b8, 0x283c1414, 0xa779dede, 0xbce25e5e, 0x161d0b0b,
- 0xad76dbdb, 0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a, 0x92db4949, 0x0c0a0606, 0x486c2424, 0xb8e45c5c,
- 0x9f5dc2c2, 0xbd6ed3d3, 0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595, 0xd337e4e4, 0xf28b7979, 0xd532e7e7,
- 0x8b43c8c8, 0x6e593737, 0xdab76d6d, 0x018c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, 0xd8b46c6c, 0xacfa5656,
- 0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a, 0x47e9aeae, 0x10180808, 0x6fd5baba, 0xf0887878, 0x4a6f2525,
- 0x5c722e2e, 0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6, 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f,
- 0x96dd4b4b, 0x61dcbdbd, 0x0d868b8b, 0x0f858a8a, 0xe0907070, 0x7c423e3e, 0x71c4b5b5, 0xccaa6666, 0x90d84848,
- 0x06050303, 0xf701f6f6, 0x1c120e0e, 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9, 0x17918686, 0x9958c1c1,
- 0x3a271d1d, 0x27b99e9e, 0xd938e1e1, 0xeb13f8f8, 0x2bb39898, 0x22331111, 0xd2bb6969, 0xa970d9d9, 0x07898e8e,
- 0x33a79494, 0x2db69b9b, 0x3c221e1e, 0x15928787, 0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf,
- 0x038f8c8c, 0x59f8a1a1, 0x09808989, 0x1a170d0d, 0x65dabfbf, 0xd731e6e6, 0x84c64242, 0xd0b86868, 0x82c34141,
- 0x29b09999, 0x5a772d2d, 0x1e110f0f, 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, 0x2c3a1616 };
-
- private static final int[] Tinv0 = { 0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b, 0xf1459d1f,
- 0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad, 0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526,
- 0x8fa362b5, 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d, 0x02752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b,
- 0xe75f8f03, 0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458, 0x2969e049, 0x44c8c98e, 0x6a89c275,
- 0x78798ef4, 0x6b3e5899, 0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d, 0x184adf63, 0x82311ae5,
- 0x60335197, 0x457f5362, 0xe07764b1, 0x84ae6bbb, 0x1ca081fe, 0x942b08f9, 0x58684870, 0x19fd458f, 0x876cde94,
- 0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3, 0x2aab5566, 0x0728ebb2, 0x03c2b52f, 0x9a7bc586, 0xa50837d3,
- 0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, 0x2b1ccf8a, 0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65,
- 0xd5be0506, 0x1f6234d1, 0x8afea6c4, 0x9d532e34, 0xa055f3a2, 0x32e18a05, 0x75ebf6a4, 0x39ec830b, 0xaaef6040,
- 0x069f715e, 0x51106ebd, 0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491, 0x055dc471, 0x6fd40604,
- 0xff155060, 0x24fb9819, 0x97e9bdd6, 0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7, 0xdbeec879,
- 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x00000000, 0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, 0xfbff0efd,
- 0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68, 0xd1545b9b, 0x3a2e3624, 0xb1670a0c, 0x0fe75793,
- 0xd296eeb4, 0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c, 0x0aba93e2, 0xe52aa0c0, 0x43e0223c,
- 0x1d171b12, 0x0b0d090e, 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, 0x8519f157, 0x4c0775af, 0xbbdd99ee, 0xfd607fa3,
- 0x9f2601f7, 0xbcf5725c, 0xc53b6644, 0x347efb5b, 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8, 0xcadc31d7,
- 0x10856342, 0x40229713, 0x2011c684, 0x7d244a85, 0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc,
- 0xec52860d, 0xd0e3c177, 0x6c16b32b, 0x99b970a9, 0xfa489411, 0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56,
- 0xef903322, 0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6, 0x28de7aa5, 0x268eb7da, 0xa4bfad3f,
- 0xe49d3a2c, 0x0d927850, 0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e, 0xf5afc382, 0xbe805d9f,
- 0x7c93d069, 0xa92dd56f, 0xb31225cf, 0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, 0x097826cd, 0xf418596e,
- 0x01b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa, 0x08cfbc21, 0xe6e815ef, 0xd99be7ba, 0xce366f4a, 0xd4099fea,
- 0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235, 0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733,
- 0x4a9804f1, 0xf7daec41, 0x0e50cd7f, 0x2ff69117, 0x8dd64d76, 0x4db0ef43, 0x544daacc, 0xdf0496e4, 0xe3b5d19e,
- 0x1b886a4c, 0xb81f2cc1, 0x7f516546, 0x04ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb, 0x5a1d67b3, 0x52d2db92,
- 0x335610e9, 0x1347d66d, 0x8c61d79a, 0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7, 0xede51ce1,
- 0x3cb1477a, 0x59dfd29c, 0x3f73f255, 0x79ce1418, 0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478,
- 0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16, 0x0c25e2bc, 0x8b493c28, 0x41950dff, 0x7101a839,
- 0xdeb30c08, 0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48, 0x4257b8d0 };
-
- private static final int[] Tinv1 = { 0xa7f45150, 0x65417e53, 0xa4171ac3, 0x5e273a96, 0x6bab3bcb, 0x459d1ff1,
- 0x58faacab, 0x03e34b93, 0xfa302055, 0x6d76adf6, 0x76cc8891, 0x4c02f525, 0xd7e54ffc, 0xcb2ac5d7, 0x44352680,
- 0xa362b58f, 0x5ab1de49, 0x1bba2567, 0x0eea4598, 0xc0fe5de1, 0x752fc302, 0xf04c8112, 0x97468da3, 0xf9d36bc6,
- 0x5f8f03e7, 0x9c921595, 0x7a6dbfeb, 0x595295da, 0x83bed42d, 0x217458d3, 0x69e04929, 0xc8c98e44, 0x89c2756a,
- 0x798ef478, 0x3e58996b, 0x71b927dd, 0x4fe1beb6, 0xad88f017, 0xac20c966, 0x3ace7db4, 0x4adf6318, 0x311ae582,
- 0x33519760, 0x7f536245, 0x7764b1e0, 0xae6bbb84, 0xa081fe1c, 0x2b08f994, 0x68487058, 0xfd458f19, 0x6cde9487,
- 0xf87b52b7, 0xd373ab23, 0x024b72e2, 0x8f1fe357, 0xab55662a, 0x28ebb207, 0xc2b52f03, 0x7bc5869a, 0x0837d3a5,
- 0x872830f2, 0xa5bf23b2, 0x6a0302ba, 0x8216ed5c, 0x1ccf8a2b, 0xb479a792, 0xf207f3f0, 0xe2694ea1, 0xf4da65cd,
- 0xbe0506d5, 0x6234d11f, 0xfea6c48a, 0x532e349d, 0x55f3a2a0, 0xe18a0532, 0xebf6a475, 0xec830b39, 0xef6040aa,
- 0x9f715e06, 0x106ebd51, 0x8a213ef9, 0x06dd963d, 0x053eddae, 0xbde64d46, 0x8d5491b5, 0x5dc47105, 0xd406046f,
- 0x155060ff, 0xfb981924, 0xe9bdd697, 0x434089cc, 0x9ed96777, 0x42e8b0bd, 0x8b890788, 0x5b19e738, 0xeec879db,
- 0x0a7ca147, 0x0f427ce9, 0x1e84f8c9, 0x00000000, 0x86800983, 0xed2b3248, 0x70111eac, 0x725a6c4e, 0xff0efdfb,
- 0x38850f56, 0xd5ae3d1e, 0x392d3627, 0xd90f0a64, 0xa65c6821, 0x545b9bd1, 0x2e36243a, 0x670a0cb1, 0xe757930f,
- 0x96eeb4d2, 0x919b1b9e, 0xc5c0804f, 0x20dc61a2, 0x4b775a69, 0x1a121c16, 0xba93e20a, 0x2aa0c0e5, 0xe0223c43,
- 0x171b121d, 0x0d090e0b, 0xc78bf2ad, 0xa8b62db9, 0xa91e14c8, 0x19f15785, 0x0775af4c, 0xdd99eebb, 0x607fa3fd,
- 0x2601f79f, 0xf5725cbc, 0x3b6644c5, 0x7efb5b34, 0x29438b76, 0xc623cbdc, 0xfcedb668, 0xf1e4b863, 0xdc31d7ca,
- 0x85634210, 0x22971340, 0x11c68420, 0x244a857d, 0x3dbbd2f8, 0x32f9ae11, 0xa129c76d, 0x2f9e1d4b, 0x30b2dcf3,
- 0x52860dec, 0xe3c177d0, 0x16b32b6c, 0xb970a999, 0x489411fa, 0x64e94722, 0x8cfca8c4, 0x3ff0a01a, 0x2c7d56d8,
- 0x903322ef, 0x4e4987c7, 0xd138d9c1, 0xa2ca8cfe, 0x0bd49836, 0x81f5a6cf, 0xde7aa528, 0x8eb7da26, 0xbfad3fa4,
- 0x9d3a2ce4, 0x9278500d, 0xcc5f6a9b, 0x467e5462, 0x138df6c2, 0xb8d890e8, 0xf7392e5e, 0xafc382f5, 0x805d9fbe,
- 0x93d0697c, 0x2dd56fa9, 0x1225cfb3, 0x99acc83b, 0x7d1810a7, 0x639ce86e, 0xbb3bdb7b, 0x7826cd09, 0x18596ef4,
- 0xb79aec01, 0x9a4f83a8, 0x6e95e665, 0xe6ffaa7e, 0xcfbc2108, 0xe815efe6, 0x9be7bad9, 0x366f4ace, 0x099fead4,
- 0x7cb029d6, 0xb2a431af, 0x233f2a31, 0x94a5c630, 0x66a235c0, 0xbc4e7437, 0xca82fca6, 0xd090e0b0, 0xd8a73315,
- 0x9804f14a, 0xdaec41f7, 0x50cd7f0e, 0xf691172f, 0xd64d768d, 0xb0ef434d, 0x4daacc54, 0x0496e4df, 0xb5d19ee3,
- 0x886a4c1b, 0x1f2cc1b8, 0x5165467f, 0xea5e9d04, 0x358c015d, 0x7487fa73, 0x410bfb2e, 0x1d67b35a, 0xd2db9252,
- 0x5610e933, 0x47d66d13, 0x61d79a8c, 0x0ca1377a, 0x14f8598e, 0x3c13eb89, 0x27a9ceee, 0xc961b735, 0xe51ce1ed,
- 0xb1477a3c, 0xdfd29c59, 0x73f2553f, 0xce141879, 0x37c773bf, 0xcdf753ea, 0xaafd5f5b, 0x6f3ddf14, 0xdb447886,
- 0xf3afca81, 0xc468b93e, 0x3424382c, 0x40a3c25f, 0xc31d1672, 0x25e2bc0c, 0x493c288b, 0x950dff41, 0x01a83971,
- 0xb30c08de, 0xe4b4d89c, 0xc1566490, 0x84cb7b61, 0xb632d570, 0x5c6c4874, 0x57b8d042 };
-
- private static final int[] Tinv2 = { 0xf45150a7, 0x417e5365, 0x171ac3a4, 0x273a965e, 0xab3bcb6b, 0x9d1ff145,
- 0xfaacab58, 0xe34b9303, 0x302055fa, 0x76adf66d, 0xcc889176, 0x02f5254c, 0xe54ffcd7, 0x2ac5d7cb, 0x35268044,
- 0x62b58fa3, 0xb1de495a, 0xba25671b, 0xea45980e, 0xfe5de1c0, 0x2fc30275, 0x4c8112f0, 0x468da397, 0xd36bc6f9,
- 0x8f03e75f, 0x9215959c, 0x6dbfeb7a, 0x5295da59, 0xbed42d83, 0x7458d321, 0xe0492969, 0xc98e44c8, 0xc2756a89,
- 0x8ef47879, 0x58996b3e, 0xb927dd71, 0xe1beb64f, 0x88f017ad, 0x20c966ac, 0xce7db43a, 0xdf63184a, 0x1ae58231,
- 0x51976033, 0x5362457f, 0x64b1e077, 0x6bbb84ae, 0x81fe1ca0, 0x08f9942b, 0x48705868, 0x458f19fd, 0xde94876c,
- 0x7b52b7f8, 0x73ab23d3, 0x4b72e202, 0x1fe3578f, 0x55662aab, 0xebb20728, 0xb52f03c2, 0xc5869a7b, 0x37d3a508,
- 0x2830f287, 0xbf23b2a5, 0x0302ba6a, 0x16ed5c82, 0xcf8a2b1c, 0x79a792b4, 0x07f3f0f2, 0x694ea1e2, 0xda65cdf4,
- 0x0506d5be, 0x34d11f62, 0xa6c48afe, 0x2e349d53, 0xf3a2a055, 0x8a0532e1, 0xf6a475eb, 0x830b39ec, 0x6040aaef,
- 0x715e069f, 0x6ebd5110, 0x213ef98a, 0xdd963d06, 0x3eddae05, 0xe64d46bd, 0x5491b58d, 0xc471055d, 0x06046fd4,
- 0x5060ff15, 0x981924fb, 0xbdd697e9, 0x4089cc43, 0xd967779e, 0xe8b0bd42, 0x8907888b, 0x19e7385b, 0xc879dbee,
- 0x7ca1470a, 0x427ce90f, 0x84f8c91e, 0x00000000, 0x80098386, 0x2b3248ed, 0x111eac70, 0x5a6c4e72, 0x0efdfbff,
- 0x850f5638, 0xae3d1ed5, 0x2d362739, 0x0f0a64d9, 0x5c6821a6, 0x5b9bd154, 0x36243a2e, 0x0a0cb167, 0x57930fe7,
- 0xeeb4d296, 0x9b1b9e91, 0xc0804fc5, 0xdc61a220, 0x775a694b, 0x121c161a, 0x93e20aba, 0xa0c0e52a, 0x223c43e0,
- 0x1b121d17, 0x090e0b0d, 0x8bf2adc7, 0xb62db9a8, 0x1e14c8a9, 0xf1578519, 0x75af4c07, 0x99eebbdd, 0x7fa3fd60,
- 0x01f79f26, 0x725cbcf5, 0x6644c53b, 0xfb5b347e, 0x438b7629, 0x23cbdcc6, 0xedb668fc, 0xe4b863f1, 0x31d7cadc,
- 0x63421085, 0x97134022, 0xc6842011, 0x4a857d24, 0xbbd2f83d, 0xf9ae1132, 0x29c76da1, 0x9e1d4b2f, 0xb2dcf330,
- 0x860dec52, 0xc177d0e3, 0xb32b6c16, 0x70a999b9, 0x9411fa48, 0xe9472264, 0xfca8c48c, 0xf0a01a3f, 0x7d56d82c,
- 0x3322ef90, 0x4987c74e, 0x38d9c1d1, 0xca8cfea2, 0xd498360b, 0xf5a6cf81, 0x7aa528de, 0xb7da268e, 0xad3fa4bf,
- 0x3a2ce49d, 0x78500d92, 0x5f6a9bcc, 0x7e546246, 0x8df6c213, 0xd890e8b8, 0x392e5ef7, 0xc382f5af, 0x5d9fbe80,
- 0xd0697c93, 0xd56fa92d, 0x25cfb312, 0xacc83b99, 0x1810a77d, 0x9ce86e63, 0x3bdb7bbb, 0x26cd0978, 0x596ef418,
- 0x9aec01b7, 0x4f83a89a, 0x95e6656e, 0xffaa7ee6, 0xbc2108cf, 0x15efe6e8, 0xe7bad99b, 0x6f4ace36, 0x9fead409,
- 0xb029d67c, 0xa431afb2, 0x3f2a3123, 0xa5c63094, 0xa235c066, 0x4e7437bc, 0x82fca6ca, 0x90e0b0d0, 0xa73315d8,
- 0x04f14a98, 0xec41f7da, 0xcd7f0e50, 0x91172ff6, 0x4d768dd6, 0xef434db0, 0xaacc544d, 0x96e4df04, 0xd19ee3b5,
- 0x6a4c1b88, 0x2cc1b81f, 0x65467f51, 0x5e9d04ea, 0x8c015d35, 0x87fa7374, 0x0bfb2e41, 0x67b35a1d, 0xdb9252d2,
- 0x10e93356, 0xd66d1347, 0xd79a8c61, 0xa1377a0c, 0xf8598e14, 0x13eb893c, 0xa9ceee27, 0x61b735c9, 0x1ce1ede5,
- 0x477a3cb1, 0xd29c59df, 0xf2553f73, 0x141879ce, 0xc773bf37, 0xf753eacd, 0xfd5f5baa, 0x3ddf146f, 0x447886db,
- 0xafca81f3, 0x68b93ec4, 0x24382c34, 0xa3c25f40, 0x1d1672c3, 0xe2bc0c25, 0x3c288b49, 0x0dff4195, 0xa8397101,
- 0x0c08deb3, 0xb4d89ce4, 0x566490c1, 0xcb7b6184, 0x32d570b6, 0x6c48745c, 0xb8d04257 };
-
- private static final int[] Tinv3 = { 0x5150a7f4, 0x7e536541, 0x1ac3a417, 0x3a965e27, 0x3bcb6bab, 0x1ff1459d,
- 0xacab58fa, 0x4b9303e3, 0x2055fa30, 0xadf66d76, 0x889176cc, 0xf5254c02, 0x4ffcd7e5, 0xc5d7cb2a, 0x26804435,
- 0xb58fa362, 0xde495ab1, 0x25671bba, 0x45980eea, 0x5de1c0fe, 0xc302752f, 0x8112f04c, 0x8da39746, 0x6bc6f9d3,
- 0x03e75f8f, 0x15959c92, 0xbfeb7a6d, 0x95da5952, 0xd42d83be, 0x58d32174, 0x492969e0, 0x8e44c8c9, 0x756a89c2,
- 0xf478798e, 0x996b3e58, 0x27dd71b9, 0xbeb64fe1, 0xf017ad88, 0xc966ac20, 0x7db43ace, 0x63184adf, 0xe582311a,
- 0x97603351, 0x62457f53, 0xb1e07764, 0xbb84ae6b, 0xfe1ca081, 0xf9942b08, 0x70586848, 0x8f19fd45, 0x94876cde,
- 0x52b7f87b, 0xab23d373, 0x72e2024b, 0xe3578f1f, 0x662aab55, 0xb20728eb, 0x2f03c2b5, 0x869a7bc5, 0xd3a50837,
- 0x30f28728, 0x23b2a5bf, 0x02ba6a03, 0xed5c8216, 0x8a2b1ccf, 0xa792b479, 0xf3f0f207, 0x4ea1e269, 0x65cdf4da,
- 0x06d5be05, 0xd11f6234, 0xc48afea6, 0x349d532e, 0xa2a055f3, 0x0532e18a, 0xa475ebf6, 0x0b39ec83, 0x40aaef60,
- 0x5e069f71, 0xbd51106e, 0x3ef98a21, 0x963d06dd, 0xddae053e, 0x4d46bde6, 0x91b58d54, 0x71055dc4, 0x046fd406,
- 0x60ff1550, 0x1924fb98, 0xd697e9bd, 0x89cc4340, 0x67779ed9, 0xb0bd42e8, 0x07888b89, 0xe7385b19, 0x79dbeec8,
- 0xa1470a7c, 0x7ce90f42, 0xf8c91e84, 0x00000000, 0x09838680, 0x3248ed2b, 0x1eac7011, 0x6c4e725a, 0xfdfbff0e,
- 0x0f563885, 0x3d1ed5ae, 0x3627392d, 0x0a64d90f, 0x6821a65c, 0x9bd1545b, 0x243a2e36, 0x0cb1670a, 0x930fe757,
- 0xb4d296ee, 0x1b9e919b, 0x804fc5c0, 0x61a220dc, 0x5a694b77, 0x1c161a12, 0xe20aba93, 0xc0e52aa0, 0x3c43e022,
- 0x121d171b, 0x0e0b0d09, 0xf2adc78b, 0x2db9a8b6, 0x14c8a91e, 0x578519f1, 0xaf4c0775, 0xeebbdd99, 0xa3fd607f,
- 0xf79f2601, 0x5cbcf572, 0x44c53b66, 0x5b347efb, 0x8b762943, 0xcbdcc623, 0xb668fced, 0xb863f1e4, 0xd7cadc31,
- 0x42108563, 0x13402297, 0x842011c6, 0x857d244a, 0xd2f83dbb, 0xae1132f9, 0xc76da129, 0x1d4b2f9e, 0xdcf330b2,
- 0x0dec5286, 0x77d0e3c1, 0x2b6c16b3, 0xa999b970, 0x11fa4894, 0x472264e9, 0xa8c48cfc, 0xa01a3ff0, 0x56d82c7d,
- 0x22ef9033, 0x87c74e49, 0xd9c1d138, 0x8cfea2ca, 0x98360bd4, 0xa6cf81f5, 0xa528de7a, 0xda268eb7, 0x3fa4bfad,
- 0x2ce49d3a, 0x500d9278, 0x6a9bcc5f, 0x5462467e, 0xf6c2138d, 0x90e8b8d8, 0x2e5ef739, 0x82f5afc3, 0x9fbe805d,
- 0x697c93d0, 0x6fa92dd5, 0xcfb31225, 0xc83b99ac, 0x10a77d18, 0xe86e639c, 0xdb7bbb3b, 0xcd097826, 0x6ef41859,
- 0xec01b79a, 0x83a89a4f, 0xe6656e95, 0xaa7ee6ff, 0x2108cfbc, 0xefe6e815, 0xbad99be7, 0x4ace366f, 0xead4099f,
- 0x29d67cb0, 0x31afb2a4, 0x2a31233f, 0xc63094a5, 0x35c066a2, 0x7437bc4e, 0xfca6ca82, 0xe0b0d090, 0x3315d8a7,
- 0xf14a9804, 0x41f7daec, 0x7f0e50cd, 0x172ff691, 0x768dd64d, 0x434db0ef, 0xcc544daa, 0xe4df0496, 0x9ee3b5d1,
- 0x4c1b886a, 0xc1b81f2c, 0x467f5165, 0x9d04ea5e, 0x015d358c, 0xfa737487, 0xfb2e410b, 0xb35a1d67, 0x9252d2db,
- 0xe9335610, 0x6d1347d6, 0x9a8c61d7, 0x377a0ca1, 0x598e14f8, 0xeb893c13, 0xceee27a9, 0xb735c961, 0xe1ede51c,
- 0x7a3cb147, 0x9c59dfd2, 0x553f73f2, 0x1879ce14, 0x73bf37c7, 0x53eacdf7, 0x5f5baafd, 0xdf146f3d, 0x7886db44,
- 0xca81f3af, 0xb93ec468, 0x382c3424, 0xc25f40a3, 0x1672c31d, 0xbc0c25e2, 0x288b493c, 0xff41950d, 0x397101a8,
- 0x08deb30c, 0xd89ce4b4, 0x6490c156, 0x7b6184cb, 0xd570b632, 0x48745c6c, 0xd04257b8 };
-
- private final int shift(int r, int shift)
- {
- return (((r >>> shift) | (r << (32 - shift))));
- }
-
- /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */
-
- private static final int m1 = 0x80808080;
- private static final int m2 = 0x7f7f7f7f;
- private static final int m3 = 0x0000001b;
-
- private final int FFmulX(int x)
- {
- return (((x & m2) << 1) ^ (((x & m1) >>> 7) * m3));
- }
-
- /*
- * The following defines provide alternative definitions of FFmulX that
- * might give improved performance if a fast 32-bit multiply is not
- * available.
- *
- * private int FFmulX(int x) { int u = x & m1; u |= (u >> 1); return ((x &
- * m2) < < 1) ^ ((u >>> 3) | (u >>> 6)); } private static final int m4 =
- * 0x1b1b1b1b; private int FFmulX(int x) { int u = x & m1; return ((x & m2) < <
- * 1) ^ ((u - (u >>> 7)) & m4); }
- *
- */
-
- private final int inv_mcol(int x)
- {
- int f2 = FFmulX(x);
- int f4 = FFmulX(f2);
- int f8 = FFmulX(f4);
- int f9 = x ^ f8;
-
- return f2 ^ f4 ^ f8 ^ shift(f2 ^ f9, 8) ^ shift(f4 ^ f9, 16) ^ shift(f9, 24);
- }
-
- private final int subWord(int x)
- {
- return (S[x & 255] & 255 | ((S[(x >> 8) & 255] & 255) << 8) | ((S[(x >> 16) & 255] & 255) << 16) | S[(x >> 24) & 255] << 24);
- }
-
- /**
- * Calculate the necessary round keys The number of calculations depends on
- * key size and block size AES specified a fixed block size of 128 bits and
- * key sizes 128/192/256 bits This code is written assuming those are the
- * only possible values
- */
- private final int[][] generateWorkingKey(byte[] key, boolean forEncryption)
- {
- int KC = key.length / 4; // key length in words
- int t;
-
- if (((KC != 4) && (KC != 6) && (KC != 8)) || ((KC * 4) != key.length))
- {
- throw new IllegalArgumentException("Key length not 128/192/256 bits.");
- }
-
- ROUNDS = KC + 6; // This is not always true for the generalized
- // Rijndael that allows larger block sizes
- int[][] W = new int[ROUNDS + 1][4]; // 4 words in a block
-
- //
- // copy the key into the round key array
- //
-
- t = 0;
- for (int i = 0; i < key.length; t++)
- {
- W[t >> 2][t & 3] = (key[i] & 0xff) | ((key[i + 1] & 0xff) << 8) | ((key[i + 2] & 0xff) << 16)
- | (key[i + 3] << 24);
- i += 4;
- }
-
- //
- // while not enough round key material calculated
- // calculate new values
- //
- int k = (ROUNDS + 1) << 2;
- for (int i = KC; (i < k); i++)
- {
- int temp = W[(i - 1) >> 2][(i - 1) & 3];
- if ((i % KC) == 0)
- {
- temp = subWord(shift(temp, 8)) ^ rcon[(i / KC) - 1];
- }
- else if ((KC > 6) && ((i % KC) == 4))
- {
- temp = subWord(temp);
- }
-
- W[i >> 2][i & 3] = W[(i - KC) >> 2][(i - KC) & 3] ^ temp;
- }
-
- if (!forEncryption)
- {
- for (int j = 1; j < ROUNDS; j++)
- {
- for (int i = 0; i < 4; i++)
- {
- W[j][i] = inv_mcol(W[j][i]);
- }
- }
- }
-
- return W;
- }
-
- private int ROUNDS;
- private int[][] WorkingKey = null;
- private int C0, C1, C2, C3;
- private boolean doEncrypt;
-
- private static final int BLOCK_SIZE = 16;
-
- /**
- * default constructor - 128 bit block size.
- */
- public AES()
- {
- }
-
- /**
- * initialise an AES cipher.
- *
- * @param forEncryption
- * whether or not we are for encryption.
- * @param key
- * the key required to set up the cipher.
- * @exception IllegalArgumentException
- * if the params argument is inappropriate.
- */
-
- public final void init(boolean forEncryption, byte[] key)
- {
- WorkingKey = generateWorkingKey(key, forEncryption);
- this.doEncrypt = forEncryption;
- }
-
- public final String getAlgorithmName()
- {
- return "AES";
- }
-
- public final int getBlockSize()
- {
- return BLOCK_SIZE;
- }
-
- public final int processBlock(byte[] in, int inOff, byte[] out, int outOff)
- {
- if (WorkingKey == null)
- {
- throw new IllegalStateException("AES engine not initialised");
- }
-
- if ((inOff + (32 / 2)) > in.length)
- {
- throw new IllegalArgumentException("input buffer too short");
- }
-
- if ((outOff + (32 / 2)) > out.length)
- {
- throw new IllegalArgumentException("output buffer too short");
- }
-
- if (doEncrypt)
- {
- unpackBlock(in, inOff);
- encryptBlock(WorkingKey);
- packBlock(out, outOff);
- }
- else
- {
- unpackBlock(in, inOff);
- decryptBlock(WorkingKey);
- packBlock(out, outOff);
- }
-
- return BLOCK_SIZE;
- }
-
- public final void reset()
- {
- }
-
- private final void unpackBlock(byte[] bytes, int off)
- {
- int index = off;
-
- C0 = (bytes[index++] & 0xff);
- C0 |= (bytes[index++] & 0xff) << 8;
- C0 |= (bytes[index++] & 0xff) << 16;
- C0 |= bytes[index++] << 24;
-
- C1 = (bytes[index++] & 0xff);
- C1 |= (bytes[index++] & 0xff) << 8;
- C1 |= (bytes[index++] & 0xff) << 16;
- C1 |= bytes[index++] << 24;
-
- C2 = (bytes[index++] & 0xff);
- C2 |= (bytes[index++] & 0xff) << 8;
- C2 |= (bytes[index++] & 0xff) << 16;
- C2 |= bytes[index++] << 24;
-
- C3 = (bytes[index++] & 0xff);
- C3 |= (bytes[index++] & 0xff) << 8;
- C3 |= (bytes[index++] & 0xff) << 16;
- C3 |= bytes[index++] << 24;
- }
-
- private final void packBlock(byte[] bytes, int off)
- {
- int index = off;
-
- bytes[index++] = (byte) C0;
- bytes[index++] = (byte) (C0 >> 8);
- bytes[index++] = (byte) (C0 >> 16);
- bytes[index++] = (byte) (C0 >> 24);
-
- bytes[index++] = (byte) C1;
- bytes[index++] = (byte) (C1 >> 8);
- bytes[index++] = (byte) (C1 >> 16);
- bytes[index++] = (byte) (C1 >> 24);
-
- bytes[index++] = (byte) C2;
- bytes[index++] = (byte) (C2 >> 8);
- bytes[index++] = (byte) (C2 >> 16);
- bytes[index++] = (byte) (C2 >> 24);
-
- bytes[index++] = (byte) C3;
- bytes[index++] = (byte) (C3 >> 8);
- bytes[index++] = (byte) (C3 >> 16);
- bytes[index++] = (byte) (C3 >> 24);
- }
-
- private final void encryptBlock(int[][] KW)
- {
- int r, r0, r1, r2, r3;
-
- C0 ^= KW[0][0];
- C1 ^= KW[0][1];
- C2 ^= KW[0][2];
- C3 ^= KW[0][3];
-
- for (r = 1; r < ROUNDS - 1;)
- {
- r0 = T0[C0 & 255] ^ T1[(C1 >> 8) & 255] ^ T2[(C2 >> 16) & 255] ^ T3[(C3 >> 24) & 255] ^ KW[r][0];
- r1 = T0[C1 & 255] ^ T1[(C2 >> 8) & 255] ^ T2[(C3 >> 16) & 255] ^ T3[(C0 >> 24) & 255] ^ KW[r][1];
- r2 = T0[C2 & 255] ^ T1[(C3 >> 8) & 255] ^ T2[(C0 >> 16) & 255] ^ T3[(C1 >> 24) & 255] ^ KW[r][2];
- r3 = T0[C3 & 255] ^ T1[(C0 >> 8) & 255] ^ T2[(C1 >> 16) & 255] ^ T3[(C2 >> 24) & 255] ^ KW[r++][3];
- C0 = T0[r0 & 255] ^ T1[(r1 >> 8) & 255] ^ T2[(r2 >> 16) & 255] ^ T3[(r3 >> 24) & 255] ^ KW[r][0];
- C1 = T0[r1 & 255] ^ T1[(r2 >> 8) & 255] ^ T2[(r3 >> 16) & 255] ^ T3[(r0 >> 24) & 255] ^ KW[r][1];
- C2 = T0[r2 & 255] ^ T1[(r3 >> 8) & 255] ^ T2[(r0 >> 16) & 255] ^ T3[(r1 >> 24) & 255] ^ KW[r][2];
- C3 = T0[r3 & 255] ^ T1[(r0 >> 8) & 255] ^ T2[(r1 >> 16) & 255] ^ T3[(r2 >> 24) & 255] ^ KW[r++][3];
- }
-
- r0 = T0[C0 & 255] ^ T1[(C1 >> 8) & 255] ^ T2[(C2 >> 16) & 255] ^ T3[(C3 >> 24) & 255] ^ KW[r][0];
- r1 = T0[C1 & 255] ^ T1[(C2 >> 8) & 255] ^ T2[(C3 >> 16) & 255] ^ T3[(C0 >> 24) & 255] ^ KW[r][1];
- r2 = T0[C2 & 255] ^ T1[(C3 >> 8) & 255] ^ T2[(C0 >> 16) & 255] ^ T3[(C1 >> 24) & 255] ^ KW[r][2];
- r3 = T0[C3 & 255] ^ T1[(C0 >> 8) & 255] ^ T2[(C1 >> 16) & 255] ^ T3[(C2 >> 24) & 255] ^ KW[r++][3];
-
- // the final round's table is a simple function of S so we don't use a
- // whole other four tables for it
-
- C0 = (S[r0 & 255] & 255) ^ ((S[(r1 >> 8) & 255] & 255) << 8) ^ ((S[(r2 >> 16) & 255] & 255) << 16)
- ^ (S[(r3 >> 24) & 255] << 24) ^ KW[r][0];
- C1 = (S[r1 & 255] & 255) ^ ((S[(r2 >> 8) & 255] & 255) << 8) ^ ((S[(r3 >> 16) & 255] & 255) << 16)
- ^ (S[(r0 >> 24) & 255] << 24) ^ KW[r][1];
- C2 = (S[r2 & 255] & 255) ^ ((S[(r3 >> 8) & 255] & 255) << 8) ^ ((S[(r0 >> 16) & 255] & 255) << 16)
- ^ (S[(r1 >> 24) & 255] << 24) ^ KW[r][2];
- C3 = (S[r3 & 255] & 255) ^ ((S[(r0 >> 8) & 255] & 255) << 8) ^ ((S[(r1 >> 16) & 255] & 255) << 16)
- ^ (S[(r2 >> 24) & 255] << 24) ^ KW[r][3];
-
- }
-
- private final void decryptBlock(int[][] KW)
- {
- int r, r0, r1, r2, r3;
-
- C0 ^= KW[ROUNDS][0];
- C1 ^= KW[ROUNDS][1];
- C2 ^= KW[ROUNDS][2];
- C3 ^= KW[ROUNDS][3];
-
- for (r = ROUNDS - 1; r > 1;)
- {
- r0 = Tinv0[C0 & 255] ^ Tinv1[(C3 >> 8) & 255] ^ Tinv2[(C2 >> 16) & 255] ^ Tinv3[(C1 >> 24) & 255]
- ^ KW[r][0];
- r1 = Tinv0[C1 & 255] ^ Tinv1[(C0 >> 8) & 255] ^ Tinv2[(C3 >> 16) & 255] ^ Tinv3[(C2 >> 24) & 255]
- ^ KW[r][1];
- r2 = Tinv0[C2 & 255] ^ Tinv1[(C1 >> 8) & 255] ^ Tinv2[(C0 >> 16) & 255] ^ Tinv3[(C3 >> 24) & 255]
- ^ KW[r][2];
- r3 = Tinv0[C3 & 255] ^ Tinv1[(C2 >> 8) & 255] ^ Tinv2[(C1 >> 16) & 255] ^ Tinv3[(C0 >> 24) & 255]
- ^ KW[r--][3];
- C0 = Tinv0[r0 & 255] ^ Tinv1[(r3 >> 8) & 255] ^ Tinv2[(r2 >> 16) & 255] ^ Tinv3[(r1 >> 24) & 255]
- ^ KW[r][0];
- C1 = Tinv0[r1 & 255] ^ Tinv1[(r0 >> 8) & 255] ^ Tinv2[(r3 >> 16) & 255] ^ Tinv3[(r2 >> 24) & 255]
- ^ KW[r][1];
- C2 = Tinv0[r2 & 255] ^ Tinv1[(r1 >> 8) & 255] ^ Tinv2[(r0 >> 16) & 255] ^ Tinv3[(r3 >> 24) & 255]
- ^ KW[r][2];
- C3 = Tinv0[r3 & 255] ^ Tinv1[(r2 >> 8) & 255] ^ Tinv2[(r1 >> 16) & 255] ^ Tinv3[(r0 >> 24) & 255]
- ^ KW[r--][3];
- }
-
- r0 = Tinv0[C0 & 255] ^ Tinv1[(C3 >> 8) & 255] ^ Tinv2[(C2 >> 16) & 255] ^ Tinv3[(C1 >> 24) & 255] ^ KW[r][0];
- r1 = Tinv0[C1 & 255] ^ Tinv1[(C0 >> 8) & 255] ^ Tinv2[(C3 >> 16) & 255] ^ Tinv3[(C2 >> 24) & 255] ^ KW[r][1];
- r2 = Tinv0[C2 & 255] ^ Tinv1[(C1 >> 8) & 255] ^ Tinv2[(C0 >> 16) & 255] ^ Tinv3[(C3 >> 24) & 255] ^ KW[r][2];
- r3 = Tinv0[C3 & 255] ^ Tinv1[(C2 >> 8) & 255] ^ Tinv2[(C1 >> 16) & 255] ^ Tinv3[(C0 >> 24) & 255] ^ KW[r--][3];
-
- // the final round's table is a simple function of Si so we don't use a
- // whole other four tables for it
-
- C0 = (Si[r0 & 255] & 255) ^ ((Si[(r3 >> 8) & 255] & 255) << 8) ^ ((Si[(r2 >> 16) & 255] & 255) << 16)
- ^ (Si[(r1 >> 24) & 255] << 24) ^ KW[0][0];
- C1 = (Si[r1 & 255] & 255) ^ ((Si[(r0 >> 8) & 255] & 255) << 8) ^ ((Si[(r3 >> 16) & 255] & 255) << 16)
- ^ (Si[(r2 >> 24) & 255] << 24) ^ KW[0][1];
- C2 = (Si[r2 & 255] & 255) ^ ((Si[(r1 >> 8) & 255] & 255) << 8) ^ ((Si[(r0 >> 16) & 255] & 255) << 16)
- ^ (Si[(r3 >> 24) & 255] << 24) ^ KW[0][2];
- C3 = (Si[r3 & 255] & 255) ^ ((Si[(r2 >> 8) & 255] & 255) << 8) ^ ((Si[(r1 >> 16) & 255] & 255) << 16)
- ^ (Si[(r0 >> 24) & 255] << 24) ^ KW[0][3];
- }
-
- public void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff)
- {
- processBlock(src, srcoff, dst, dstoff);
- }
-}
+
+package com.trilead.ssh2.crypto.cipher;
+
+/*
+ This file was shamelessly taken from the Bouncy Castle Crypto package.
+ Their licence file states the following:
+
+ Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle
+ (http://www.bouncycastle.org)
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ */
+
+/**
+ * An implementation of the AES (Rijndael), from FIPS-197.
+ * <p>
+ * For further details see: <a
+ * href="http://csrc.nist.gov/encryption/aes/">http://csrc.nist.gov/encryption/aes/
+ * </a>.
+ *
+ * This implementation is based on optimizations from Dr. Brian Gladman's paper
+ * and C code at <a
+ * href="http://fp.gladman.plus.com/cryptography_technology/rijndael/">http://fp.gladman.plus.com/cryptography_technology/rijndael/
+ * </a>
+ *
+ * There are three levels of tradeoff of speed vs memory Because java has no
+ * preprocessor, they are written as three separate classes from which to choose
+ *
+ * The fastest uses 8Kbytes of static tables to precompute round calculations, 4
+ * 256 word tables for encryption and 4 for decryption.
+ *
+ * The middle performance version uses only one 256 word table for each, for a
+ * total of 2Kbytes, adding 12 rotate operations per round to compute the values
+ * contained in the other tables from the contents of the first
+ *
+ * The slowest version uses no static tables at all and computes the values in
+ * each round
+ * <p>
+ * This file contains the fast version with 8Kbytes of static tables for round
+ * precomputation
+ *
+ * @author See comments in the source file
+ * @version $Id: AES.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class AES implements BlockCipher
+{
+ // The S box
+ private static final byte[] S = { (byte) 99, (byte) 124, (byte) 119, (byte) 123, (byte) 242, (byte) 107,
+ (byte) 111, (byte) 197, (byte) 48, (byte) 1, (byte) 103, (byte) 43, (byte) 254, (byte) 215, (byte) 171,
+ (byte) 118, (byte) 202, (byte) 130, (byte) 201, (byte) 125, (byte) 250, (byte) 89, (byte) 71, (byte) 240,
+ (byte) 173, (byte) 212, (byte) 162, (byte) 175, (byte) 156, (byte) 164, (byte) 114, (byte) 192, (byte) 183,
+ (byte) 253, (byte) 147, (byte) 38, (byte) 54, (byte) 63, (byte) 247, (byte) 204, (byte) 52, (byte) 165,
+ (byte) 229, (byte) 241, (byte) 113, (byte) 216, (byte) 49, (byte) 21, (byte) 4, (byte) 199, (byte) 35,
+ (byte) 195, (byte) 24, (byte) 150, (byte) 5, (byte) 154, (byte) 7, (byte) 18, (byte) 128, (byte) 226,
+ (byte) 235, (byte) 39, (byte) 178, (byte) 117, (byte) 9, (byte) 131, (byte) 44, (byte) 26, (byte) 27,
+ (byte) 110, (byte) 90, (byte) 160, (byte) 82, (byte) 59, (byte) 214, (byte) 179, (byte) 41, (byte) 227,
+ (byte) 47, (byte) 132, (byte) 83, (byte) 209, (byte) 0, (byte) 237, (byte) 32, (byte) 252, (byte) 177,
+ (byte) 91, (byte) 106, (byte) 203, (byte) 190, (byte) 57, (byte) 74, (byte) 76, (byte) 88, (byte) 207,
+ (byte) 208, (byte) 239, (byte) 170, (byte) 251, (byte) 67, (byte) 77, (byte) 51, (byte) 133, (byte) 69,
+ (byte) 249, (byte) 2, (byte) 127, (byte) 80, (byte) 60, (byte) 159, (byte) 168, (byte) 81, (byte) 163,
+ (byte) 64, (byte) 143, (byte) 146, (byte) 157, (byte) 56, (byte) 245, (byte) 188, (byte) 182, (byte) 218,
+ (byte) 33, (byte) 16, (byte) 255, (byte) 243, (byte) 210, (byte) 205, (byte) 12, (byte) 19, (byte) 236,
+ (byte) 95, (byte) 151, (byte) 68, (byte) 23, (byte) 196, (byte) 167, (byte) 126, (byte) 61, (byte) 100,
+ (byte) 93, (byte) 25, (byte) 115, (byte) 96, (byte) 129, (byte) 79, (byte) 220, (byte) 34, (byte) 42,
+ (byte) 144, (byte) 136, (byte) 70, (byte) 238, (byte) 184, (byte) 20, (byte) 222, (byte) 94, (byte) 11,
+ (byte) 219, (byte) 224, (byte) 50, (byte) 58, (byte) 10, (byte) 73, (byte) 6, (byte) 36, (byte) 92,
+ (byte) 194, (byte) 211, (byte) 172, (byte) 98, (byte) 145, (byte) 149, (byte) 228, (byte) 121, (byte) 231,
+ (byte) 200, (byte) 55, (byte) 109, (byte) 141, (byte) 213, (byte) 78, (byte) 169, (byte) 108, (byte) 86,
+ (byte) 244, (byte) 234, (byte) 101, (byte) 122, (byte) 174, (byte) 8, (byte) 186, (byte) 120, (byte) 37,
+ (byte) 46, (byte) 28, (byte) 166, (byte) 180, (byte) 198, (byte) 232, (byte) 221, (byte) 116, (byte) 31,
+ (byte) 75, (byte) 189, (byte) 139, (byte) 138, (byte) 112, (byte) 62, (byte) 181, (byte) 102, (byte) 72,
+ (byte) 3, (byte) 246, (byte) 14, (byte) 97, (byte) 53, (byte) 87, (byte) 185, (byte) 134, (byte) 193,
+ (byte) 29, (byte) 158, (byte) 225, (byte) 248, (byte) 152, (byte) 17, (byte) 105, (byte) 217, (byte) 142,
+ (byte) 148, (byte) 155, (byte) 30, (byte) 135, (byte) 233, (byte) 206, (byte) 85, (byte) 40, (byte) 223,
+ (byte) 140, (byte) 161, (byte) 137, (byte) 13, (byte) 191, (byte) 230, (byte) 66, (byte) 104, (byte) 65,
+ (byte) 153, (byte) 45, (byte) 15, (byte) 176, (byte) 84, (byte) 187, (byte) 22, };
+
+ // The inverse S-box
+ private static final byte[] Si = { (byte) 82, (byte) 9, (byte) 106, (byte) 213, (byte) 48, (byte) 54, (byte) 165,
+ (byte) 56, (byte) 191, (byte) 64, (byte) 163, (byte) 158, (byte) 129, (byte) 243, (byte) 215, (byte) 251,
+ (byte) 124, (byte) 227, (byte) 57, (byte) 130, (byte) 155, (byte) 47, (byte) 255, (byte) 135, (byte) 52,
+ (byte) 142, (byte) 67, (byte) 68, (byte) 196, (byte) 222, (byte) 233, (byte) 203, (byte) 84, (byte) 123,
+ (byte) 148, (byte) 50, (byte) 166, (byte) 194, (byte) 35, (byte) 61, (byte) 238, (byte) 76, (byte) 149,
+ (byte) 11, (byte) 66, (byte) 250, (byte) 195, (byte) 78, (byte) 8, (byte) 46, (byte) 161, (byte) 102,
+ (byte) 40, (byte) 217, (byte) 36, (byte) 178, (byte) 118, (byte) 91, (byte) 162, (byte) 73, (byte) 109,
+ (byte) 139, (byte) 209, (byte) 37, (byte) 114, (byte) 248, (byte) 246, (byte) 100, (byte) 134, (byte) 104,
+ (byte) 152, (byte) 22, (byte) 212, (byte) 164, (byte) 92, (byte) 204, (byte) 93, (byte) 101, (byte) 182,
+ (byte) 146, (byte) 108, (byte) 112, (byte) 72, (byte) 80, (byte) 253, (byte) 237, (byte) 185, (byte) 218,
+ (byte) 94, (byte) 21, (byte) 70, (byte) 87, (byte) 167, (byte) 141, (byte) 157, (byte) 132, (byte) 144,
+ (byte) 216, (byte) 171, (byte) 0, (byte) 140, (byte) 188, (byte) 211, (byte) 10, (byte) 247, (byte) 228,
+ (byte) 88, (byte) 5, (byte) 184, (byte) 179, (byte) 69, (byte) 6, (byte) 208, (byte) 44, (byte) 30,
+ (byte) 143, (byte) 202, (byte) 63, (byte) 15, (byte) 2, (byte) 193, (byte) 175, (byte) 189, (byte) 3,
+ (byte) 1, (byte) 19, (byte) 138, (byte) 107, (byte) 58, (byte) 145, (byte) 17, (byte) 65, (byte) 79,
+ (byte) 103, (byte) 220, (byte) 234, (byte) 151, (byte) 242, (byte) 207, (byte) 206, (byte) 240, (byte) 180,
+ (byte) 230, (byte) 115, (byte) 150, (byte) 172, (byte) 116, (byte) 34, (byte) 231, (byte) 173, (byte) 53,
+ (byte) 133, (byte) 226, (byte) 249, (byte) 55, (byte) 232, (byte) 28, (byte) 117, (byte) 223, (byte) 110,
+ (byte) 71, (byte) 241, (byte) 26, (byte) 113, (byte) 29, (byte) 41, (byte) 197, (byte) 137, (byte) 111,
+ (byte) 183, (byte) 98, (byte) 14, (byte) 170, (byte) 24, (byte) 190, (byte) 27, (byte) 252, (byte) 86,
+ (byte) 62, (byte) 75, (byte) 198, (byte) 210, (byte) 121, (byte) 32, (byte) 154, (byte) 219, (byte) 192,
+ (byte) 254, (byte) 120, (byte) 205, (byte) 90, (byte) 244, (byte) 31, (byte) 221, (byte) 168, (byte) 51,
+ (byte) 136, (byte) 7, (byte) 199, (byte) 49, (byte) 177, (byte) 18, (byte) 16, (byte) 89, (byte) 39,
+ (byte) 128, (byte) 236, (byte) 95, (byte) 96, (byte) 81, (byte) 127, (byte) 169, (byte) 25, (byte) 181,
+ (byte) 74, (byte) 13, (byte) 45, (byte) 229, (byte) 122, (byte) 159, (byte) 147, (byte) 201, (byte) 156,
+ (byte) 239, (byte) 160, (byte) 224, (byte) 59, (byte) 77, (byte) 174, (byte) 42, (byte) 245, (byte) 176,
+ (byte) 200, (byte) 235, (byte) 187, (byte) 60, (byte) 131, (byte) 83, (byte) 153, (byte) 97, (byte) 23,
+ (byte) 43, (byte) 4, (byte) 126, (byte) 186, (byte) 119, (byte) 214, (byte) 38, (byte) 225, (byte) 105,
+ (byte) 20, (byte) 99, (byte) 85, (byte) 33, (byte) 12, (byte) 125, };
+
+ // vector used in calculating key schedule (powers of x in GF(256))
+ private static final int[] rcon = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab,
+ 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 };
+
+ // precomputation tables of calculations for rounds
+ private static final int[] T0 = { 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff, 0xbd6b6bd6,
+ 0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102, 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d,
+ 0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb,
+ 0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, 0x967272e4, 0x5bc0c09b, 0xc2b7b775,
+ 0x1cfdfde1, 0xae93933d, 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83, 0x5c343468, 0xf4a5a551,
+ 0x34e5e5d1, 0x08f1f1f9, 0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a, 0x0c040408, 0x52c7c795, 0x65232346,
+ 0x5ec3c39d, 0x28181830, 0xa1969637, 0x0f05050a, 0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df,
+ 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912, 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36,
+ 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d, 0x7b292952, 0x3ee3e3dd,
+ 0x712f2f5e, 0x97848413, 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, 0x60202040, 0x1ffcfce3, 0xc8b1b179,
+ 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d, 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85,
+ 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a,
+ 0x10f9f9e9, 0x06020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d,
+ 0xc0404080, 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1, 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf,
+ 0x63212142, 0x30101020, 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3,
+ 0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, 0xac6464c8,
+ 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54,
+ 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16,
+ 0x76dbdbad, 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, 0x0a06060c, 0x6c242448, 0xe45c5cb8,
+ 0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3, 0x8b7979f2, 0x32e7e7d5,
+ 0x43c8c88b, 0x5937376e, 0xb76d6dda, 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8, 0xfa5656ac,
+ 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a,
+ 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697, 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e,
+ 0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c, 0xc4b5b571, 0xaa6666cc, 0xd8484890,
+ 0x05030306, 0x01f6f6f7, 0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, 0x91868617, 0x58c1c199,
+ 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9, 0x898e8e07,
+ 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715, 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5,
+ 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, 0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182,
+ 0xb0999929, 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c };
+
+ private static final int[] T1 = { 0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d, 0x6b6bd6bd,
+ 0x6f6fdeb1, 0xc5c59154, 0x30306050, 0x01010203, 0x6767cea9, 0x2b2b567d, 0xfefee719, 0xd7d7b562, 0xabab4de6,
+ 0x7676ec9a, 0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87, 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b,
+ 0xadad41ec, 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7, 0x7272e496, 0xc0c09b5b, 0xb7b775c2,
+ 0xfdfde11c, 0x93933dae, 0x26264c6a, 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f, 0x3434685c, 0xa5a551f4,
+ 0xe5e5d134, 0xf1f1f908, 0x7171e293, 0xd8d8ab73, 0x31316253, 0x15152a3f, 0x0404080c, 0xc7c79552, 0x23234665,
+ 0xc3c39d5e, 0x18183028, 0x969637a1, 0x05050a0f, 0x9a9a2fb5, 0x07070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d,
+ 0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, 0x0909121b, 0x83831d9e, 0x2c2c5874, 0x1a1a342e, 0x1b1b362d,
+ 0x6e6edcb2, 0x5a5ab4ee, 0xa0a05bfb, 0x5252a4f6, 0x3b3b764d, 0xd6d6b761, 0xb3b37dce, 0x2929527b, 0xe3e3dd3e,
+ 0x2f2f5e71, 0x84841397, 0x5353a6f5, 0xd1d1b968, 0x00000000, 0xededc12c, 0x20204060, 0xfcfce31f, 0xb1b179c8,
+ 0x5b5bb6ed, 0x6a6ad4be, 0xcbcb8d46, 0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, 0xcfcf854a,
+ 0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16, 0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194, 0x45458acf,
+ 0xf9f9e910, 0x02020406, 0x7f7ffe81, 0x5050a0f0, 0x3c3c7844, 0x9f9f25ba, 0xa8a84be3, 0x5151a2f3, 0xa3a35dfe,
+ 0x404080c0, 0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104, 0xbcbc63df, 0xb6b677c1, 0xdadaaf75,
+ 0x21214263, 0x10102030, 0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d, 0xcdcd814c, 0x0c0c1814, 0x13132635, 0xececc32f,
+ 0x5f5fbee1, 0x979735a2, 0x444488cc, 0x17172e39, 0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47, 0x6464c8ac,
+ 0x5d5dbae7, 0x1919322b, 0x7373e695, 0x6060c0a0, 0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e,
+ 0x90903bab, 0x88880b83, 0x46468cca, 0xeeeec729, 0xb8b86bd3, 0x1414283c, 0xdedea779, 0x5e5ebce2, 0x0b0b161d,
+ 0xdbdbad76, 0xe0e0db3b, 0x32326456, 0x3a3a744e, 0x0a0a141e, 0x494992db, 0x06060c0a, 0x2424486c, 0x5c5cb8e4,
+ 0xc2c29f5d, 0xd3d3bd6e, 0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4, 0xe4e4d337, 0x7979f28b, 0xe7e7d532,
+ 0xc8c88b43, 0x37376e59, 0x6d6ddab7, 0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, 0x6c6cd8b4, 0x5656acfa,
+ 0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e, 0xaeae47e9, 0x08081018, 0xbaba6fd5, 0x7878f088, 0x25254a6f,
+ 0x2e2e5c72, 0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751, 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21,
+ 0x4b4b96dd, 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, 0x7070e090, 0x3e3e7c42, 0xb5b571c4, 0x6666ccaa, 0x484890d8,
+ 0x03030605, 0xf6f6f701, 0x0e0e1c12, 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0, 0x86861791, 0xc1c19958,
+ 0x1d1d3a27, 0x9e9e27b9, 0xe1e1d938, 0xf8f8eb13, 0x98982bb3, 0x11112233, 0x6969d2bb, 0xd9d9a970, 0x8e8e0789,
+ 0x949433a7, 0x9b9b2db6, 0x1e1e3c22, 0x87871592, 0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a,
+ 0x8c8c038f, 0xa1a159f8, 0x89890980, 0x0d0d1a17, 0xbfbf65da, 0xe6e6d731, 0x424284c6, 0x6868d0b8, 0x414182c3,
+ 0x999929b0, 0x2d2d5a77, 0x0f0f1e11, 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, 0x16162c3a };
+
+ private static final int[] T2 = { 0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2, 0x6bd6bd6b,
+ 0x6fdeb16f, 0xc59154c5, 0x30605030, 0x01020301, 0x67cea967, 0x2b567d2b, 0xfee719fe, 0xd7b562d7, 0xab4de6ab,
+ 0x76ec9a76, 0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d, 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0,
+ 0xad41ecad, 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4, 0x72e49672, 0xc09b5bc0, 0xb775c2b7,
+ 0xfde11cfd, 0x933dae93, 0x264c6a26, 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc, 0x34685c34, 0xa551f4a5,
+ 0xe5d134e5, 0xf1f908f1, 0x71e29371, 0xd8ab73d8, 0x31625331, 0x152a3f15, 0x04080c04, 0xc79552c7, 0x23466523,
+ 0xc39d5ec3, 0x18302818, 0x9637a196, 0x050a0f05, 0x9a2fb59a, 0x070e0907, 0x12243612, 0x801b9b80, 0xe2df3de2,
+ 0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, 0x09121b09, 0x831d9e83, 0x2c58742c, 0x1a342e1a, 0x1b362d1b,
+ 0x6edcb26e, 0x5ab4ee5a, 0xa05bfba0, 0x52a4f652, 0x3b764d3b, 0xd6b761d6, 0xb37dceb3, 0x29527b29, 0xe3dd3ee3,
+ 0x2f5e712f, 0x84139784, 0x53a6f553, 0xd1b968d1, 0x00000000, 0xedc12ced, 0x20406020, 0xfce31ffc, 0xb179c8b1,
+ 0x5bb6ed5b, 0x6ad4be6a, 0xcb8d46cb, 0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858, 0xcf854acf,
+ 0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb, 0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485, 0x458acf45,
+ 0xf9e910f9, 0x02040602, 0x7ffe817f, 0x50a0f050, 0x3c78443c, 0x9f25ba9f, 0xa84be3a8, 0x51a2f351, 0xa35dfea3,
+ 0x4080c040, 0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5, 0xbc63dfbc, 0xb677c1b6, 0xdaaf75da,
+ 0x21426321, 0x10203010, 0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2, 0xcd814ccd, 0x0c18140c, 0x13263513, 0xecc32fec,
+ 0x5fbee15f, 0x9735a297, 0x4488cc44, 0x172e3917, 0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d, 0x64c8ac64,
+ 0x5dbae75d, 0x19322b19, 0x73e69573, 0x60c0a060, 0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a,
+ 0x903bab90, 0x880b8388, 0x468cca46, 0xeec729ee, 0xb86bd3b8, 0x14283c14, 0xdea779de, 0x5ebce25e, 0x0b161d0b,
+ 0xdbad76db, 0xe0db3be0, 0x32645632, 0x3a744e3a, 0x0a141e0a, 0x4992db49, 0x060c0a06, 0x24486c24, 0x5cb8e45c,
+ 0xc29f5dc2, 0xd3bd6ed3, 0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495, 0xe4d337e4, 0x79f28b79, 0xe7d532e7,
+ 0xc88b43c8, 0x376e5937, 0x6ddab76d, 0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, 0x6cd8b46c, 0x56acfa56,
+ 0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a, 0xae47e9ae, 0x08101808, 0xba6fd5ba, 0x78f08878, 0x254a6f25,
+ 0x2e5c722e, 0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6, 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f,
+ 0x4b96dd4b, 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, 0x70e09070, 0x3e7c423e, 0xb571c4b5, 0x66ccaa66, 0x4890d848,
+ 0x03060503, 0xf6f701f6, 0x0e1c120e, 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9, 0x86179186, 0xc19958c1,
+ 0x1d3a271d, 0x9e27b99e, 0xe1d938e1, 0xf8eb13f8, 0x982bb398, 0x11223311, 0x69d2bb69, 0xd9a970d9, 0x8e07898e,
+ 0x9433a794, 0x9b2db69b, 0x1e3c221e, 0x87159287, 0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf,
+ 0x8c038f8c, 0xa159f8a1, 0x89098089, 0x0d1a170d, 0xbf65dabf, 0xe6d731e6, 0x4284c642, 0x68d0b868, 0x4182c341,
+ 0x9929b099, 0x2d5a772d, 0x0f1e110f, 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, 0x162c3a16 };
+
+ private static final int[] T3 = { 0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2, 0xd6bd6b6b,
+ 0xdeb16f6f, 0x9154c5c5, 0x60503030, 0x02030101, 0xcea96767, 0x567d2b2b, 0xe719fefe, 0xb562d7d7, 0x4de6abab,
+ 0xec9a7676, 0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d, 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0,
+ 0x41ecadad, 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4, 0xe4967272, 0x9b5bc0c0, 0x75c2b7b7,
+ 0xe11cfdfd, 0x3dae9393, 0x4c6a2626, 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc, 0x685c3434, 0x51f4a5a5,
+ 0xd134e5e5, 0xf908f1f1, 0xe2937171, 0xab73d8d8, 0x62533131, 0x2a3f1515, 0x080c0404, 0x9552c7c7, 0x46652323,
+ 0x9d5ec3c3, 0x30281818, 0x37a19696, 0x0a0f0505, 0x2fb59a9a, 0x0e090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2,
+ 0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, 0x121b0909, 0x1d9e8383, 0x58742c2c, 0x342e1a1a, 0x362d1b1b,
+ 0xdcb26e6e, 0xb4ee5a5a, 0x5bfba0a0, 0xa4f65252, 0x764d3b3b, 0xb761d6d6, 0x7dceb3b3, 0x527b2929, 0xdd3ee3e3,
+ 0x5e712f2f, 0x13978484, 0xa6f55353, 0xb968d1d1, 0x00000000, 0xc12ceded, 0x40602020, 0xe31ffcfc, 0x79c8b1b1,
+ 0xb6ed5b5b, 0xd4be6a6a, 0x8d46cbcb, 0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858, 0x854acfcf,
+ 0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb, 0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585, 0x8acf4545,
+ 0xe910f9f9, 0x04060202, 0xfe817f7f, 0xa0f05050, 0x78443c3c, 0x25ba9f9f, 0x4be3a8a8, 0xa2f35151, 0x5dfea3a3,
+ 0x80c04040, 0x058a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5, 0x63dfbcbc, 0x77c1b6b6, 0xaf75dada,
+ 0x42632121, 0x20301010, 0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2, 0x814ccdcd, 0x18140c0c, 0x26351313, 0xc32fecec,
+ 0xbee15f5f, 0x35a29797, 0x88cc4444, 0x2e391717, 0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d, 0xc8ac6464,
+ 0xbae75d5d, 0x322b1919, 0xe6957373, 0xc0a06060, 0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a,
+ 0x3bab9090, 0x0b838888, 0x8cca4646, 0xc729eeee, 0x6bd3b8b8, 0x283c1414, 0xa779dede, 0xbce25e5e, 0x161d0b0b,
+ 0xad76dbdb, 0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a, 0x92db4949, 0x0c0a0606, 0x486c2424, 0xb8e45c5c,
+ 0x9f5dc2c2, 0xbd6ed3d3, 0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595, 0xd337e4e4, 0xf28b7979, 0xd532e7e7,
+ 0x8b43c8c8, 0x6e593737, 0xdab76d6d, 0x018c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, 0xd8b46c6c, 0xacfa5656,
+ 0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a, 0x47e9aeae, 0x10180808, 0x6fd5baba, 0xf0887878, 0x4a6f2525,
+ 0x5c722e2e, 0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6, 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f,
+ 0x96dd4b4b, 0x61dcbdbd, 0x0d868b8b, 0x0f858a8a, 0xe0907070, 0x7c423e3e, 0x71c4b5b5, 0xccaa6666, 0x90d84848,
+ 0x06050303, 0xf701f6f6, 0x1c120e0e, 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9, 0x17918686, 0x9958c1c1,
+ 0x3a271d1d, 0x27b99e9e, 0xd938e1e1, 0xeb13f8f8, 0x2bb39898, 0x22331111, 0xd2bb6969, 0xa970d9d9, 0x07898e8e,
+ 0x33a79494, 0x2db69b9b, 0x3c221e1e, 0x15928787, 0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf,
+ 0x038f8c8c, 0x59f8a1a1, 0x09808989, 0x1a170d0d, 0x65dabfbf, 0xd731e6e6, 0x84c64242, 0xd0b86868, 0x82c34141,
+ 0x29b09999, 0x5a772d2d, 0x1e110f0f, 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, 0x2c3a1616 };
+
+ private static final int[] Tinv0 = { 0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b, 0xf1459d1f,
+ 0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad, 0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526,
+ 0x8fa362b5, 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d, 0x02752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b,
+ 0xe75f8f03, 0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458, 0x2969e049, 0x44c8c98e, 0x6a89c275,
+ 0x78798ef4, 0x6b3e5899, 0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d, 0x184adf63, 0x82311ae5,
+ 0x60335197, 0x457f5362, 0xe07764b1, 0x84ae6bbb, 0x1ca081fe, 0x942b08f9, 0x58684870, 0x19fd458f, 0x876cde94,
+ 0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3, 0x2aab5566, 0x0728ebb2, 0x03c2b52f, 0x9a7bc586, 0xa50837d3,
+ 0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, 0x2b1ccf8a, 0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65,
+ 0xd5be0506, 0x1f6234d1, 0x8afea6c4, 0x9d532e34, 0xa055f3a2, 0x32e18a05, 0x75ebf6a4, 0x39ec830b, 0xaaef6040,
+ 0x069f715e, 0x51106ebd, 0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491, 0x055dc471, 0x6fd40604,
+ 0xff155060, 0x24fb9819, 0x97e9bdd6, 0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7, 0xdbeec879,
+ 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x00000000, 0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, 0xfbff0efd,
+ 0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68, 0xd1545b9b, 0x3a2e3624, 0xb1670a0c, 0x0fe75793,
+ 0xd296eeb4, 0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c, 0x0aba93e2, 0xe52aa0c0, 0x43e0223c,
+ 0x1d171b12, 0x0b0d090e, 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, 0x8519f157, 0x4c0775af, 0xbbdd99ee, 0xfd607fa3,
+ 0x9f2601f7, 0xbcf5725c, 0xc53b6644, 0x347efb5b, 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8, 0xcadc31d7,
+ 0x10856342, 0x40229713, 0x2011c684, 0x7d244a85, 0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc,
+ 0xec52860d, 0xd0e3c177, 0x6c16b32b, 0x99b970a9, 0xfa489411, 0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56,
+ 0xef903322, 0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6, 0x28de7aa5, 0x268eb7da, 0xa4bfad3f,
+ 0xe49d3a2c, 0x0d927850, 0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e, 0xf5afc382, 0xbe805d9f,
+ 0x7c93d069, 0xa92dd56f, 0xb31225cf, 0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, 0x097826cd, 0xf418596e,
+ 0x01b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa, 0x08cfbc21, 0xe6e815ef, 0xd99be7ba, 0xce366f4a, 0xd4099fea,
+ 0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235, 0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733,
+ 0x4a9804f1, 0xf7daec41, 0x0e50cd7f, 0x2ff69117, 0x8dd64d76, 0x4db0ef43, 0x544daacc, 0xdf0496e4, 0xe3b5d19e,
+ 0x1b886a4c, 0xb81f2cc1, 0x7f516546, 0x04ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb, 0x5a1d67b3, 0x52d2db92,
+ 0x335610e9, 0x1347d66d, 0x8c61d79a, 0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7, 0xede51ce1,
+ 0x3cb1477a, 0x59dfd29c, 0x3f73f255, 0x79ce1418, 0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478,
+ 0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16, 0x0c25e2bc, 0x8b493c28, 0x41950dff, 0x7101a839,
+ 0xdeb30c08, 0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48, 0x4257b8d0 };
+
+ private static final int[] Tinv1 = { 0xa7f45150, 0x65417e53, 0xa4171ac3, 0x5e273a96, 0x6bab3bcb, 0x459d1ff1,
+ 0x58faacab, 0x03e34b93, 0xfa302055, 0x6d76adf6, 0x76cc8891, 0x4c02f525, 0xd7e54ffc, 0xcb2ac5d7, 0x44352680,
+ 0xa362b58f, 0x5ab1de49, 0x1bba2567, 0x0eea4598, 0xc0fe5de1, 0x752fc302, 0xf04c8112, 0x97468da3, 0xf9d36bc6,
+ 0x5f8f03e7, 0x9c921595, 0x7a6dbfeb, 0x595295da, 0x83bed42d, 0x217458d3, 0x69e04929, 0xc8c98e44, 0x89c2756a,
+ 0x798ef478, 0x3e58996b, 0x71b927dd, 0x4fe1beb6, 0xad88f017, 0xac20c966, 0x3ace7db4, 0x4adf6318, 0x311ae582,
+ 0x33519760, 0x7f536245, 0x7764b1e0, 0xae6bbb84, 0xa081fe1c, 0x2b08f994, 0x68487058, 0xfd458f19, 0x6cde9487,
+ 0xf87b52b7, 0xd373ab23, 0x024b72e2, 0x8f1fe357, 0xab55662a, 0x28ebb207, 0xc2b52f03, 0x7bc5869a, 0x0837d3a5,
+ 0x872830f2, 0xa5bf23b2, 0x6a0302ba, 0x8216ed5c, 0x1ccf8a2b, 0xb479a792, 0xf207f3f0, 0xe2694ea1, 0xf4da65cd,
+ 0xbe0506d5, 0x6234d11f, 0xfea6c48a, 0x532e349d, 0x55f3a2a0, 0xe18a0532, 0xebf6a475, 0xec830b39, 0xef6040aa,
+ 0x9f715e06, 0x106ebd51, 0x8a213ef9, 0x06dd963d, 0x053eddae, 0xbde64d46, 0x8d5491b5, 0x5dc47105, 0xd406046f,
+ 0x155060ff, 0xfb981924, 0xe9bdd697, 0x434089cc, 0x9ed96777, 0x42e8b0bd, 0x8b890788, 0x5b19e738, 0xeec879db,
+ 0x0a7ca147, 0x0f427ce9, 0x1e84f8c9, 0x00000000, 0x86800983, 0xed2b3248, 0x70111eac, 0x725a6c4e, 0xff0efdfb,
+ 0x38850f56, 0xd5ae3d1e, 0x392d3627, 0xd90f0a64, 0xa65c6821, 0x545b9bd1, 0x2e36243a, 0x670a0cb1, 0xe757930f,
+ 0x96eeb4d2, 0x919b1b9e, 0xc5c0804f, 0x20dc61a2, 0x4b775a69, 0x1a121c16, 0xba93e20a, 0x2aa0c0e5, 0xe0223c43,
+ 0x171b121d, 0x0d090e0b, 0xc78bf2ad, 0xa8b62db9, 0xa91e14c8, 0x19f15785, 0x0775af4c, 0xdd99eebb, 0x607fa3fd,
+ 0x2601f79f, 0xf5725cbc, 0x3b6644c5, 0x7efb5b34, 0x29438b76, 0xc623cbdc, 0xfcedb668, 0xf1e4b863, 0xdc31d7ca,
+ 0x85634210, 0x22971340, 0x11c68420, 0x244a857d, 0x3dbbd2f8, 0x32f9ae11, 0xa129c76d, 0x2f9e1d4b, 0x30b2dcf3,
+ 0x52860dec, 0xe3c177d0, 0x16b32b6c, 0xb970a999, 0x489411fa, 0x64e94722, 0x8cfca8c4, 0x3ff0a01a, 0x2c7d56d8,
+ 0x903322ef, 0x4e4987c7, 0xd138d9c1, 0xa2ca8cfe, 0x0bd49836, 0x81f5a6cf, 0xde7aa528, 0x8eb7da26, 0xbfad3fa4,
+ 0x9d3a2ce4, 0x9278500d, 0xcc5f6a9b, 0x467e5462, 0x138df6c2, 0xb8d890e8, 0xf7392e5e, 0xafc382f5, 0x805d9fbe,
+ 0x93d0697c, 0x2dd56fa9, 0x1225cfb3, 0x99acc83b, 0x7d1810a7, 0x639ce86e, 0xbb3bdb7b, 0x7826cd09, 0x18596ef4,
+ 0xb79aec01, 0x9a4f83a8, 0x6e95e665, 0xe6ffaa7e, 0xcfbc2108, 0xe815efe6, 0x9be7bad9, 0x366f4ace, 0x099fead4,
+ 0x7cb029d6, 0xb2a431af, 0x233f2a31, 0x94a5c630, 0x66a235c0, 0xbc4e7437, 0xca82fca6, 0xd090e0b0, 0xd8a73315,
+ 0x9804f14a, 0xdaec41f7, 0x50cd7f0e, 0xf691172f, 0xd64d768d, 0xb0ef434d, 0x4daacc54, 0x0496e4df, 0xb5d19ee3,
+ 0x886a4c1b, 0x1f2cc1b8, 0x5165467f, 0xea5e9d04, 0x358c015d, 0x7487fa73, 0x410bfb2e, 0x1d67b35a, 0xd2db9252,
+ 0x5610e933, 0x47d66d13, 0x61d79a8c, 0x0ca1377a, 0x14f8598e, 0x3c13eb89, 0x27a9ceee, 0xc961b735, 0xe51ce1ed,
+ 0xb1477a3c, 0xdfd29c59, 0x73f2553f, 0xce141879, 0x37c773bf, 0xcdf753ea, 0xaafd5f5b, 0x6f3ddf14, 0xdb447886,
+ 0xf3afca81, 0xc468b93e, 0x3424382c, 0x40a3c25f, 0xc31d1672, 0x25e2bc0c, 0x493c288b, 0x950dff41, 0x01a83971,
+ 0xb30c08de, 0xe4b4d89c, 0xc1566490, 0x84cb7b61, 0xb632d570, 0x5c6c4874, 0x57b8d042 };
+
+ private static final int[] Tinv2 = { 0xf45150a7, 0x417e5365, 0x171ac3a4, 0x273a965e, 0xab3bcb6b, 0x9d1ff145,
+ 0xfaacab58, 0xe34b9303, 0x302055fa, 0x76adf66d, 0xcc889176, 0x02f5254c, 0xe54ffcd7, 0x2ac5d7cb, 0x35268044,
+ 0x62b58fa3, 0xb1de495a, 0xba25671b, 0xea45980e, 0xfe5de1c0, 0x2fc30275, 0x4c8112f0, 0x468da397, 0xd36bc6f9,
+ 0x8f03e75f, 0x9215959c, 0x6dbfeb7a, 0x5295da59, 0xbed42d83, 0x7458d321, 0xe0492969, 0xc98e44c8, 0xc2756a89,
+ 0x8ef47879, 0x58996b3e, 0xb927dd71, 0xe1beb64f, 0x88f017ad, 0x20c966ac, 0xce7db43a, 0xdf63184a, 0x1ae58231,
+ 0x51976033, 0x5362457f, 0x64b1e077, 0x6bbb84ae, 0x81fe1ca0, 0x08f9942b, 0x48705868, 0x458f19fd, 0xde94876c,
+ 0x7b52b7f8, 0x73ab23d3, 0x4b72e202, 0x1fe3578f, 0x55662aab, 0xebb20728, 0xb52f03c2, 0xc5869a7b, 0x37d3a508,
+ 0x2830f287, 0xbf23b2a5, 0x0302ba6a, 0x16ed5c82, 0xcf8a2b1c, 0x79a792b4, 0x07f3f0f2, 0x694ea1e2, 0xda65cdf4,
+ 0x0506d5be, 0x34d11f62, 0xa6c48afe, 0x2e349d53, 0xf3a2a055, 0x8a0532e1, 0xf6a475eb, 0x830b39ec, 0x6040aaef,
+ 0x715e069f, 0x6ebd5110, 0x213ef98a, 0xdd963d06, 0x3eddae05, 0xe64d46bd, 0x5491b58d, 0xc471055d, 0x06046fd4,
+ 0x5060ff15, 0x981924fb, 0xbdd697e9, 0x4089cc43, 0xd967779e, 0xe8b0bd42, 0x8907888b, 0x19e7385b, 0xc879dbee,
+ 0x7ca1470a, 0x427ce90f, 0x84f8c91e, 0x00000000, 0x80098386, 0x2b3248ed, 0x111eac70, 0x5a6c4e72, 0x0efdfbff,
+ 0x850f5638, 0xae3d1ed5, 0x2d362739, 0x0f0a64d9, 0x5c6821a6, 0x5b9bd154, 0x36243a2e, 0x0a0cb167, 0x57930fe7,
+ 0xeeb4d296, 0x9b1b9e91, 0xc0804fc5, 0xdc61a220, 0x775a694b, 0x121c161a, 0x93e20aba, 0xa0c0e52a, 0x223c43e0,
+ 0x1b121d17, 0x090e0b0d, 0x8bf2adc7, 0xb62db9a8, 0x1e14c8a9, 0xf1578519, 0x75af4c07, 0x99eebbdd, 0x7fa3fd60,
+ 0x01f79f26, 0x725cbcf5, 0x6644c53b, 0xfb5b347e, 0x438b7629, 0x23cbdcc6, 0xedb668fc, 0xe4b863f1, 0x31d7cadc,
+ 0x63421085, 0x97134022, 0xc6842011, 0x4a857d24, 0xbbd2f83d, 0xf9ae1132, 0x29c76da1, 0x9e1d4b2f, 0xb2dcf330,
+ 0x860dec52, 0xc177d0e3, 0xb32b6c16, 0x70a999b9, 0x9411fa48, 0xe9472264, 0xfca8c48c, 0xf0a01a3f, 0x7d56d82c,
+ 0x3322ef90, 0x4987c74e, 0x38d9c1d1, 0xca8cfea2, 0xd498360b, 0xf5a6cf81, 0x7aa528de, 0xb7da268e, 0xad3fa4bf,
+ 0x3a2ce49d, 0x78500d92, 0x5f6a9bcc, 0x7e546246, 0x8df6c213, 0xd890e8b8, 0x392e5ef7, 0xc382f5af, 0x5d9fbe80,
+ 0xd0697c93, 0xd56fa92d, 0x25cfb312, 0xacc83b99, 0x1810a77d, 0x9ce86e63, 0x3bdb7bbb, 0x26cd0978, 0x596ef418,
+ 0x9aec01b7, 0x4f83a89a, 0x95e6656e, 0xffaa7ee6, 0xbc2108cf, 0x15efe6e8, 0xe7bad99b, 0x6f4ace36, 0x9fead409,
+ 0xb029d67c, 0xa431afb2, 0x3f2a3123, 0xa5c63094, 0xa235c066, 0x4e7437bc, 0x82fca6ca, 0x90e0b0d0, 0xa73315d8,
+ 0x04f14a98, 0xec41f7da, 0xcd7f0e50, 0x91172ff6, 0x4d768dd6, 0xef434db0, 0xaacc544d, 0x96e4df04, 0xd19ee3b5,
+ 0x6a4c1b88, 0x2cc1b81f, 0x65467f51, 0x5e9d04ea, 0x8c015d35, 0x87fa7374, 0x0bfb2e41, 0x67b35a1d, 0xdb9252d2,
+ 0x10e93356, 0xd66d1347, 0xd79a8c61, 0xa1377a0c, 0xf8598e14, 0x13eb893c, 0xa9ceee27, 0x61b735c9, 0x1ce1ede5,
+ 0x477a3cb1, 0xd29c59df, 0xf2553f73, 0x141879ce, 0xc773bf37, 0xf753eacd, 0xfd5f5baa, 0x3ddf146f, 0x447886db,
+ 0xafca81f3, 0x68b93ec4, 0x24382c34, 0xa3c25f40, 0x1d1672c3, 0xe2bc0c25, 0x3c288b49, 0x0dff4195, 0xa8397101,
+ 0x0c08deb3, 0xb4d89ce4, 0x566490c1, 0xcb7b6184, 0x32d570b6, 0x6c48745c, 0xb8d04257 };
+
+ private static final int[] Tinv3 = { 0x5150a7f4, 0x7e536541, 0x1ac3a417, 0x3a965e27, 0x3bcb6bab, 0x1ff1459d,
+ 0xacab58fa, 0x4b9303e3, 0x2055fa30, 0xadf66d76, 0x889176cc, 0xf5254c02, 0x4ffcd7e5, 0xc5d7cb2a, 0x26804435,
+ 0xb58fa362, 0xde495ab1, 0x25671bba, 0x45980eea, 0x5de1c0fe, 0xc302752f, 0x8112f04c, 0x8da39746, 0x6bc6f9d3,
+ 0x03e75f8f, 0x15959c92, 0xbfeb7a6d, 0x95da5952, 0xd42d83be, 0x58d32174, 0x492969e0, 0x8e44c8c9, 0x756a89c2,
+ 0xf478798e, 0x996b3e58, 0x27dd71b9, 0xbeb64fe1, 0xf017ad88, 0xc966ac20, 0x7db43ace, 0x63184adf, 0xe582311a,
+ 0x97603351, 0x62457f53, 0xb1e07764, 0xbb84ae6b, 0xfe1ca081, 0xf9942b08, 0x70586848, 0x8f19fd45, 0x94876cde,
+ 0x52b7f87b, 0xab23d373, 0x72e2024b, 0xe3578f1f, 0x662aab55, 0xb20728eb, 0x2f03c2b5, 0x869a7bc5, 0xd3a50837,
+ 0x30f28728, 0x23b2a5bf, 0x02ba6a03, 0xed5c8216, 0x8a2b1ccf, 0xa792b479, 0xf3f0f207, 0x4ea1e269, 0x65cdf4da,
+ 0x06d5be05, 0xd11f6234, 0xc48afea6, 0x349d532e, 0xa2a055f3, 0x0532e18a, 0xa475ebf6, 0x0b39ec83, 0x40aaef60,
+ 0x5e069f71, 0xbd51106e, 0x3ef98a21, 0x963d06dd, 0xddae053e, 0x4d46bde6, 0x91b58d54, 0x71055dc4, 0x046fd406,
+ 0x60ff1550, 0x1924fb98, 0xd697e9bd, 0x89cc4340, 0x67779ed9, 0xb0bd42e8, 0x07888b89, 0xe7385b19, 0x79dbeec8,
+ 0xa1470a7c, 0x7ce90f42, 0xf8c91e84, 0x00000000, 0x09838680, 0x3248ed2b, 0x1eac7011, 0x6c4e725a, 0xfdfbff0e,
+ 0x0f563885, 0x3d1ed5ae, 0x3627392d, 0x0a64d90f, 0x6821a65c, 0x9bd1545b, 0x243a2e36, 0x0cb1670a, 0x930fe757,
+ 0xb4d296ee, 0x1b9e919b, 0x804fc5c0, 0x61a220dc, 0x5a694b77, 0x1c161a12, 0xe20aba93, 0xc0e52aa0, 0x3c43e022,
+ 0x121d171b, 0x0e0b0d09, 0xf2adc78b, 0x2db9a8b6, 0x14c8a91e, 0x578519f1, 0xaf4c0775, 0xeebbdd99, 0xa3fd607f,
+ 0xf79f2601, 0x5cbcf572, 0x44c53b66, 0x5b347efb, 0x8b762943, 0xcbdcc623, 0xb668fced, 0xb863f1e4, 0xd7cadc31,
+ 0x42108563, 0x13402297, 0x842011c6, 0x857d244a, 0xd2f83dbb, 0xae1132f9, 0xc76da129, 0x1d4b2f9e, 0xdcf330b2,
+ 0x0dec5286, 0x77d0e3c1, 0x2b6c16b3, 0xa999b970, 0x11fa4894, 0x472264e9, 0xa8c48cfc, 0xa01a3ff0, 0x56d82c7d,
+ 0x22ef9033, 0x87c74e49, 0xd9c1d138, 0x8cfea2ca, 0x98360bd4, 0xa6cf81f5, 0xa528de7a, 0xda268eb7, 0x3fa4bfad,
+ 0x2ce49d3a, 0x500d9278, 0x6a9bcc5f, 0x5462467e, 0xf6c2138d, 0x90e8b8d8, 0x2e5ef739, 0x82f5afc3, 0x9fbe805d,
+ 0x697c93d0, 0x6fa92dd5, 0xcfb31225, 0xc83b99ac, 0x10a77d18, 0xe86e639c, 0xdb7bbb3b, 0xcd097826, 0x6ef41859,
+ 0xec01b79a, 0x83a89a4f, 0xe6656e95, 0xaa7ee6ff, 0x2108cfbc, 0xefe6e815, 0xbad99be7, 0x4ace366f, 0xead4099f,
+ 0x29d67cb0, 0x31afb2a4, 0x2a31233f, 0xc63094a5, 0x35c066a2, 0x7437bc4e, 0xfca6ca82, 0xe0b0d090, 0x3315d8a7,
+ 0xf14a9804, 0x41f7daec, 0x7f0e50cd, 0x172ff691, 0x768dd64d, 0x434db0ef, 0xcc544daa, 0xe4df0496, 0x9ee3b5d1,
+ 0x4c1b886a, 0xc1b81f2c, 0x467f5165, 0x9d04ea5e, 0x015d358c, 0xfa737487, 0xfb2e410b, 0xb35a1d67, 0x9252d2db,
+ 0xe9335610, 0x6d1347d6, 0x9a8c61d7, 0x377a0ca1, 0x598e14f8, 0xeb893c13, 0xceee27a9, 0xb735c961, 0xe1ede51c,
+ 0x7a3cb147, 0x9c59dfd2, 0x553f73f2, 0x1879ce14, 0x73bf37c7, 0x53eacdf7, 0x5f5baafd, 0xdf146f3d, 0x7886db44,
+ 0xca81f3af, 0xb93ec468, 0x382c3424, 0xc25f40a3, 0x1672c31d, 0xbc0c25e2, 0x288b493c, 0xff41950d, 0x397101a8,
+ 0x08deb30c, 0xd89ce4b4, 0x6490c156, 0x7b6184cb, 0xd570b632, 0x48745c6c, 0xd04257b8 };
+
+ private final int shift(int r, int shift)
+ {
+ return (((r >>> shift) | (r << (32 - shift))));
+ }
+
+ /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */
+
+ private static final int m1 = 0x80808080;
+ private static final int m2 = 0x7f7f7f7f;
+ private static final int m3 = 0x0000001b;
+
+ private final int FFmulX(int x)
+ {
+ return (((x & m2) << 1) ^ (((x & m1) >>> 7) * m3));
+ }
+
+ /*
+ * The following defines provide alternative definitions of FFmulX that
+ * might give improved performance if a fast 32-bit multiply is not
+ * available.
+ *
+ * private int FFmulX(int x) { int u = x & m1; u |= (u >> 1); return ((x &
+ * m2) < < 1) ^ ((u >>> 3) | (u >>> 6)); } private static final int m4 =
+ * 0x1b1b1b1b; private int FFmulX(int x) { int u = x & m1; return ((x & m2) < <
+ * 1) ^ ((u - (u >>> 7)) & m4); }
+ *
+ */
+
+ private final int inv_mcol(int x)
+ {
+ int f2 = FFmulX(x);
+ int f4 = FFmulX(f2);
+ int f8 = FFmulX(f4);
+ int f9 = x ^ f8;
+
+ return f2 ^ f4 ^ f8 ^ shift(f2 ^ f9, 8) ^ shift(f4 ^ f9, 16) ^ shift(f9, 24);
+ }
+
+ private final int subWord(int x)
+ {
+ return (S[x & 255] & 255 | ((S[(x >> 8) & 255] & 255) << 8) | ((S[(x >> 16) & 255] & 255) << 16) | S[(x >> 24) & 255] << 24);
+ }
+
+ /**
+ * Calculate the necessary round keys The number of calculations depends on
+ * key size and block size AES specified a fixed block size of 128 bits and
+ * key sizes 128/192/256 bits This code is written assuming those are the
+ * only possible values
+ */
+ private final int[][] generateWorkingKey(byte[] key, boolean forEncryption)
+ {
+ int KC = key.length / 4; // key length in words
+ int t;
+
+ if (((KC != 4) && (KC != 6) && (KC != 8)) || ((KC * 4) != key.length))
+ {
+ throw new IllegalArgumentException("Key length not 128/192/256 bits.");
+ }
+
+ ROUNDS = KC + 6; // This is not always true for the generalized
+ // Rijndael that allows larger block sizes
+ int[][] W = new int[ROUNDS + 1][4]; // 4 words in a block
+
+ //
+ // copy the key into the round key array
+ //
+
+ t = 0;
+ for (int i = 0; i < key.length; t++)
+ {
+ W[t >> 2][t & 3] = (key[i] & 0xff) | ((key[i + 1] & 0xff) << 8) | ((key[i + 2] & 0xff) << 16)
+ | (key[i + 3] << 24);
+ i += 4;
+ }
+
+ //
+ // while not enough round key material calculated
+ // calculate new values
+ //
+ int k = (ROUNDS + 1) << 2;
+ for (int i = KC; (i < k); i++)
+ {
+ int temp = W[(i - 1) >> 2][(i - 1) & 3];
+ if ((i % KC) == 0)
+ {
+ temp = subWord(shift(temp, 8)) ^ rcon[(i / KC) - 1];
+ }
+ else if ((KC > 6) && ((i % KC) == 4))
+ {
+ temp = subWord(temp);
+ }
+
+ W[i >> 2][i & 3] = W[(i - KC) >> 2][(i - KC) & 3] ^ temp;
+ }
+
+ if (!forEncryption)
+ {
+ for (int j = 1; j < ROUNDS; j++)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ W[j][i] = inv_mcol(W[j][i]);
+ }
+ }
+ }
+
+ return W;
+ }
+
+ private int ROUNDS;
+ private int[][] WorkingKey = null;
+ private int C0, C1, C2, C3;
+ private boolean doEncrypt;
+
+ private static final int BLOCK_SIZE = 16;
+
+ /**
+ * default constructor - 128 bit block size.
+ */
+ public AES()
+ {
+ }
+
+ /**
+ * initialise an AES cipher.
+ *
+ * @param forEncryption
+ * whether or not we are for encryption.
+ * @param key
+ * the key required to set up the cipher.
+ * @exception IllegalArgumentException
+ * if the params argument is inappropriate.
+ */
+
+ public final void init(boolean forEncryption, byte[] key)
+ {
+ WorkingKey = generateWorkingKey(key, forEncryption);
+ this.doEncrypt = forEncryption;
+ }
+
+ public final String getAlgorithmName()
+ {
+ return "AES";
+ }
+
+ public final int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public final int processBlock(byte[] in, int inOff, byte[] out, int outOff)
+ {
+ if (WorkingKey == null)
+ {
+ throw new IllegalStateException("AES engine not initialised");
+ }
+
+ if ((inOff + (32 / 2)) > in.length)
+ {
+ throw new IllegalArgumentException("input buffer too short");
+ }
+
+ if ((outOff + (32 / 2)) > out.length)
+ {
+ throw new IllegalArgumentException("output buffer too short");
+ }
+
+ if (doEncrypt)
+ {
+ unpackBlock(in, inOff);
+ encryptBlock(WorkingKey);
+ packBlock(out, outOff);
+ }
+ else
+ {
+ unpackBlock(in, inOff);
+ decryptBlock(WorkingKey);
+ packBlock(out, outOff);
+ }
+
+ return BLOCK_SIZE;
+ }
+
+ public final void reset()
+ {
+ }
+
+ private final void unpackBlock(byte[] bytes, int off)
+ {
+ int index = off;
+
+ C0 = (bytes[index++] & 0xff);
+ C0 |= (bytes[index++] & 0xff) << 8;
+ C0 |= (bytes[index++] & 0xff) << 16;
+ C0 |= bytes[index++] << 24;
+
+ C1 = (bytes[index++] & 0xff);
+ C1 |= (bytes[index++] & 0xff) << 8;
+ C1 |= (bytes[index++] & 0xff) << 16;
+ C1 |= bytes[index++] << 24;
+
+ C2 = (bytes[index++] & 0xff);
+ C2 |= (bytes[index++] & 0xff) << 8;
+ C2 |= (bytes[index++] & 0xff) << 16;
+ C2 |= bytes[index++] << 24;
+
+ C3 = (bytes[index++] & 0xff);
+ C3 |= (bytes[index++] & 0xff) << 8;
+ C3 |= (bytes[index++] & 0xff) << 16;
+ C3 |= bytes[index++] << 24;
+ }
+
+ private final void packBlock(byte[] bytes, int off)
+ {
+ int index = off;
+
+ bytes[index++] = (byte) C0;
+ bytes[index++] = (byte) (C0 >> 8);
+ bytes[index++] = (byte) (C0 >> 16);
+ bytes[index++] = (byte) (C0 >> 24);
+
+ bytes[index++] = (byte) C1;
+ bytes[index++] = (byte) (C1 >> 8);
+ bytes[index++] = (byte) (C1 >> 16);
+ bytes[index++] = (byte) (C1 >> 24);
+
+ bytes[index++] = (byte) C2;
+ bytes[index++] = (byte) (C2 >> 8);
+ bytes[index++] = (byte) (C2 >> 16);
+ bytes[index++] = (byte) (C2 >> 24);
+
+ bytes[index++] = (byte) C3;
+ bytes[index++] = (byte) (C3 >> 8);
+ bytes[index++] = (byte) (C3 >> 16);
+ bytes[index++] = (byte) (C3 >> 24);
+ }
+
+ private final void encryptBlock(int[][] KW)
+ {
+ int r, r0, r1, r2, r3;
+
+ C0 ^= KW[0][0];
+ C1 ^= KW[0][1];
+ C2 ^= KW[0][2];
+ C3 ^= KW[0][3];
+
+ for (r = 1; r < ROUNDS - 1;)
+ {
+ r0 = T0[C0 & 255] ^ T1[(C1 >> 8) & 255] ^ T2[(C2 >> 16) & 255] ^ T3[(C3 >> 24) & 255] ^ KW[r][0];
+ r1 = T0[C1 & 255] ^ T1[(C2 >> 8) & 255] ^ T2[(C3 >> 16) & 255] ^ T3[(C0 >> 24) & 255] ^ KW[r][1];
+ r2 = T0[C2 & 255] ^ T1[(C3 >> 8) & 255] ^ T2[(C0 >> 16) & 255] ^ T3[(C1 >> 24) & 255] ^ KW[r][2];
+ r3 = T0[C3 & 255] ^ T1[(C0 >> 8) & 255] ^ T2[(C1 >> 16) & 255] ^ T3[(C2 >> 24) & 255] ^ KW[r++][3];
+ C0 = T0[r0 & 255] ^ T1[(r1 >> 8) & 255] ^ T2[(r2 >> 16) & 255] ^ T3[(r3 >> 24) & 255] ^ KW[r][0];
+ C1 = T0[r1 & 255] ^ T1[(r2 >> 8) & 255] ^ T2[(r3 >> 16) & 255] ^ T3[(r0 >> 24) & 255] ^ KW[r][1];
+ C2 = T0[r2 & 255] ^ T1[(r3 >> 8) & 255] ^ T2[(r0 >> 16) & 255] ^ T3[(r1 >> 24) & 255] ^ KW[r][2];
+ C3 = T0[r3 & 255] ^ T1[(r0 >> 8) & 255] ^ T2[(r1 >> 16) & 255] ^ T3[(r2 >> 24) & 255] ^ KW[r++][3];
+ }
+
+ r0 = T0[C0 & 255] ^ T1[(C1 >> 8) & 255] ^ T2[(C2 >> 16) & 255] ^ T3[(C3 >> 24) & 255] ^ KW[r][0];
+ r1 = T0[C1 & 255] ^ T1[(C2 >> 8) & 255] ^ T2[(C3 >> 16) & 255] ^ T3[(C0 >> 24) & 255] ^ KW[r][1];
+ r2 = T0[C2 & 255] ^ T1[(C3 >> 8) & 255] ^ T2[(C0 >> 16) & 255] ^ T3[(C1 >> 24) & 255] ^ KW[r][2];
+ r3 = T0[C3 & 255] ^ T1[(C0 >> 8) & 255] ^ T2[(C1 >> 16) & 255] ^ T3[(C2 >> 24) & 255] ^ KW[r++][3];
+
+ // the final round's table is a simple function of S so we don't use a
+ // whole other four tables for it
+
+ C0 = (S[r0 & 255] & 255) ^ ((S[(r1 >> 8) & 255] & 255) << 8) ^ ((S[(r2 >> 16) & 255] & 255) << 16)
+ ^ (S[(r3 >> 24) & 255] << 24) ^ KW[r][0];
+ C1 = (S[r1 & 255] & 255) ^ ((S[(r2 >> 8) & 255] & 255) << 8) ^ ((S[(r3 >> 16) & 255] & 255) << 16)
+ ^ (S[(r0 >> 24) & 255] << 24) ^ KW[r][1];
+ C2 = (S[r2 & 255] & 255) ^ ((S[(r3 >> 8) & 255] & 255) << 8) ^ ((S[(r0 >> 16) & 255] & 255) << 16)
+ ^ (S[(r1 >> 24) & 255] << 24) ^ KW[r][2];
+ C3 = (S[r3 & 255] & 255) ^ ((S[(r0 >> 8) & 255] & 255) << 8) ^ ((S[(r1 >> 16) & 255] & 255) << 16)
+ ^ (S[(r2 >> 24) & 255] << 24) ^ KW[r][3];
+
+ }
+
+ private final void decryptBlock(int[][] KW)
+ {
+ int r, r0, r1, r2, r3;
+
+ C0 ^= KW[ROUNDS][0];
+ C1 ^= KW[ROUNDS][1];
+ C2 ^= KW[ROUNDS][2];
+ C3 ^= KW[ROUNDS][3];
+
+ for (r = ROUNDS - 1; r > 1;)
+ {
+ r0 = Tinv0[C0 & 255] ^ Tinv1[(C3 >> 8) & 255] ^ Tinv2[(C2 >> 16) & 255] ^ Tinv3[(C1 >> 24) & 255]
+ ^ KW[r][0];
+ r1 = Tinv0[C1 & 255] ^ Tinv1[(C0 >> 8) & 255] ^ Tinv2[(C3 >> 16) & 255] ^ Tinv3[(C2 >> 24) & 255]
+ ^ KW[r][1];
+ r2 = Tinv0[C2 & 255] ^ Tinv1[(C1 >> 8) & 255] ^ Tinv2[(C0 >> 16) & 255] ^ Tinv3[(C3 >> 24) & 255]
+ ^ KW[r][2];
+ r3 = Tinv0[C3 & 255] ^ Tinv1[(C2 >> 8) & 255] ^ Tinv2[(C1 >> 16) & 255] ^ Tinv3[(C0 >> 24) & 255]
+ ^ KW[r--][3];
+ C0 = Tinv0[r0 & 255] ^ Tinv1[(r3 >> 8) & 255] ^ Tinv2[(r2 >> 16) & 255] ^ Tinv3[(r1 >> 24) & 255]
+ ^ KW[r][0];
+ C1 = Tinv0[r1 & 255] ^ Tinv1[(r0 >> 8) & 255] ^ Tinv2[(r3 >> 16) & 255] ^ Tinv3[(r2 >> 24) & 255]
+ ^ KW[r][1];
+ C2 = Tinv0[r2 & 255] ^ Tinv1[(r1 >> 8) & 255] ^ Tinv2[(r0 >> 16) & 255] ^ Tinv3[(r3 >> 24) & 255]
+ ^ KW[r][2];
+ C3 = Tinv0[r3 & 255] ^ Tinv1[(r2 >> 8) & 255] ^ Tinv2[(r1 >> 16) & 255] ^ Tinv3[(r0 >> 24) & 255]
+ ^ KW[r--][3];
+ }
+
+ r0 = Tinv0[C0 & 255] ^ Tinv1[(C3 >> 8) & 255] ^ Tinv2[(C2 >> 16) & 255] ^ Tinv3[(C1 >> 24) & 255] ^ KW[r][0];
+ r1 = Tinv0[C1 & 255] ^ Tinv1[(C0 >> 8) & 255] ^ Tinv2[(C3 >> 16) & 255] ^ Tinv3[(C2 >> 24) & 255] ^ KW[r][1];
+ r2 = Tinv0[C2 & 255] ^ Tinv1[(C1 >> 8) & 255] ^ Tinv2[(C0 >> 16) & 255] ^ Tinv3[(C3 >> 24) & 255] ^ KW[r][2];
+ r3 = Tinv0[C3 & 255] ^ Tinv1[(C2 >> 8) & 255] ^ Tinv2[(C1 >> 16) & 255] ^ Tinv3[(C0 >> 24) & 255] ^ KW[r--][3];
+
+ // the final round's table is a simple function of Si so we don't use a
+ // whole other four tables for it
+
+ C0 = (Si[r0 & 255] & 255) ^ ((Si[(r3 >> 8) & 255] & 255) << 8) ^ ((Si[(r2 >> 16) & 255] & 255) << 16)
+ ^ (Si[(r1 >> 24) & 255] << 24) ^ KW[0][0];
+ C1 = (Si[r1 & 255] & 255) ^ ((Si[(r0 >> 8) & 255] & 255) << 8) ^ ((Si[(r3 >> 16) & 255] & 255) << 16)
+ ^ (Si[(r2 >> 24) & 255] << 24) ^ KW[0][1];
+ C2 = (Si[r2 & 255] & 255) ^ ((Si[(r1 >> 8) & 255] & 255) << 8) ^ ((Si[(r0 >> 16) & 255] & 255) << 16)
+ ^ (Si[(r3 >> 24) & 255] << 24) ^ KW[0][2];
+ C3 = (Si[r3 & 255] & 255) ^ ((Si[(r2 >> 8) & 255] & 255) << 8) ^ ((Si[(r1 >> 16) & 255] & 255) << 16)
+ ^ (Si[(r0 >> 24) & 255] << 24) ^ KW[0][3];
+ }
+
+ public void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff)
+ {
+ processBlock(src, srcoff, dst, dstoff);
+ }
+}
diff --git a/src/com/trilead/ssh2/crypto/cipher/BlockCipher.java b/src/com/trilead/ssh2/crypto/cipher/BlockCipher.java
index de60952..4cc28ab 100644
--- a/src/com/trilead/ssh2/crypto/cipher/BlockCipher.java
+++ b/src/com/trilead/ssh2/crypto/cipher/BlockCipher.java
@@ -1,16 +1,16 @@
-package com.trilead.ssh2.crypto.cipher;
-
-/**
- * BlockCipher.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: BlockCipher.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public interface BlockCipher
-{
- public void init(boolean forEncryption, byte[] key);
-
- public int getBlockSize();
-
- public void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff);
-}
+package com.trilead.ssh2.crypto.cipher;
+
+/**
+ * BlockCipher.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: BlockCipher.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public interface BlockCipher
+{
+ public void init(boolean forEncryption, byte[] key);
+
+ public int getBlockSize();
+
+ public void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff);
+}
diff --git a/src/com/trilead/ssh2/crypto/cipher/BlockCipherFactory.java b/src/com/trilead/ssh2/crypto/cipher/BlockCipherFactory.java
index cf18f43..6e386a5 100644
--- a/src/com/trilead/ssh2/crypto/cipher/BlockCipherFactory.java
+++ b/src/com/trilead/ssh2/crypto/cipher/BlockCipherFactory.java
@@ -1,115 +1,115 @@
-
-package com.trilead.ssh2.crypto.cipher;
-
-import java.util.Vector;
-
-/**
- * BlockCipherFactory.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: BlockCipherFactory.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
- */
-public class BlockCipherFactory
-{
- static class CipherEntry
- {
- String type;
- int blocksize;
- int keysize;
- String cipherClass;
-
- public CipherEntry(String type, int blockSize, int keySize, String cipherClass)
- {
- this.type = type;
- this.blocksize = blockSize;
- this.keysize = keySize;
- this.cipherClass = cipherClass;
- }
- }
-
- static Vector<CipherEntry> ciphers = new Vector<CipherEntry>();
-
- static
- {
- /* Higher Priority First */
-
- ciphers.addElement(new CipherEntry("aes256-ctr", 16, 32, "com.trilead.ssh2.crypto.cipher.AES"));
- ciphers.addElement(new CipherEntry("aes192-ctr", 16, 24, "com.trilead.ssh2.crypto.cipher.AES"));
- ciphers.addElement(new CipherEntry("aes128-ctr", 16, 16, "com.trilead.ssh2.crypto.cipher.AES"));
- ciphers.addElement(new CipherEntry("blowfish-ctr", 8, 16, "com.trilead.ssh2.crypto.cipher.BlowFish"));
-
- ciphers.addElement(new CipherEntry("aes256-cbc", 16, 32, "com.trilead.ssh2.crypto.cipher.AES"));
- ciphers.addElement(new CipherEntry("aes192-cbc", 16, 24, "com.trilead.ssh2.crypto.cipher.AES"));
- ciphers.addElement(new CipherEntry("aes128-cbc", 16, 16, "com.trilead.ssh2.crypto.cipher.AES"));
- ciphers.addElement(new CipherEntry("blowfish-cbc", 8, 16, "com.trilead.ssh2.crypto.cipher.BlowFish"));
-
- ciphers.addElement(new CipherEntry("3des-ctr", 8, 24, "com.trilead.ssh2.crypto.cipher.DESede"));
- ciphers.addElement(new CipherEntry("3des-cbc", 8, 24, "com.trilead.ssh2.crypto.cipher.DESede"));
- }
-
- public static String[] getDefaultCipherList()
- {
- String list[] = new String[ciphers.size()];
- for (int i = 0; i < ciphers.size(); i++)
- {
- CipherEntry ce = ciphers.elementAt(i);
- list[i] = new String(ce.type);
- }
- return list;
- }
-
- public static void checkCipherList(String[] cipherCandidates)
- {
- for (int i = 0; i < cipherCandidates.length; i++)
- getEntry(cipherCandidates[i]);
- }
-
- public static BlockCipher createCipher(String type, boolean encrypt, byte[] key, byte[] iv)
- {
- try
- {
- CipherEntry ce = getEntry(type);
- Class cc = Class.forName(ce.cipherClass);
- BlockCipher bc = (BlockCipher) cc.newInstance();
-
- if (type.endsWith("-cbc"))
- {
- bc.init(encrypt, key);
- return new CBCMode(bc, iv, encrypt);
- }
- else if (type.endsWith("-ctr"))
- {
- bc.init(true, key);
- return new CTRMode(bc, iv, encrypt);
- }
- throw new IllegalArgumentException("Cannot instantiate " + type);
- }
- catch (Exception e)
- {
- throw new IllegalArgumentException("Cannot instantiate " + type);
- }
- }
-
- private static CipherEntry getEntry(String type)
- {
- for (int i = 0; i < ciphers.size(); i++)
- {
- CipherEntry ce = ciphers.elementAt(i);
- if (ce.type.equals(type))
- return ce;
- }
- throw new IllegalArgumentException("Unkown algorithm " + type);
- }
-
- public static int getBlockSize(String type)
- {
- CipherEntry ce = getEntry(type);
- return ce.blocksize;
- }
-
- public static int getKeySize(String type)
- {
- CipherEntry ce = getEntry(type);
- return ce.keysize;
- }
-}
+
+package com.trilead.ssh2.crypto.cipher;
+
+import java.util.Vector;
+
+/**
+ * BlockCipherFactory.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: BlockCipherFactory.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
+ */
+public class BlockCipherFactory
+{
+ static class CipherEntry
+ {
+ String type;
+ int blocksize;
+ int keysize;
+ String cipherClass;
+
+ public CipherEntry(String type, int blockSize, int keySize, String cipherClass)
+ {
+ this.type = type;
+ this.blocksize = blockSize;
+ this.keysize = keySize;
+ this.cipherClass = cipherClass;
+ }
+ }
+
+ static Vector<CipherEntry> ciphers = new Vector<CipherEntry>();
+
+ static
+ {
+ /* Higher Priority First */
+
+ ciphers.addElement(new CipherEntry("aes256-ctr", 16, 32, "com.trilead.ssh2.crypto.cipher.AES"));
+ ciphers.addElement(new CipherEntry("aes192-ctr", 16, 24, "com.trilead.ssh2.crypto.cipher.AES"));
+ ciphers.addElement(new CipherEntry("aes128-ctr", 16, 16, "com.trilead.ssh2.crypto.cipher.AES"));
+ ciphers.addElement(new CipherEntry("blowfish-ctr", 8, 16, "com.trilead.ssh2.crypto.cipher.BlowFish"));
+
+ ciphers.addElement(new CipherEntry("aes256-cbc", 16, 32, "com.trilead.ssh2.crypto.cipher.AES"));
+ ciphers.addElement(new CipherEntry("aes192-cbc", 16, 24, "com.trilead.ssh2.crypto.cipher.AES"));
+ ciphers.addElement(new CipherEntry("aes128-cbc", 16, 16, "com.trilead.ssh2.crypto.cipher.AES"));
+ ciphers.addElement(new CipherEntry("blowfish-cbc", 8, 16, "com.trilead.ssh2.crypto.cipher.BlowFish"));
+
+ ciphers.addElement(new CipherEntry("3des-ctr", 8, 24, "com.trilead.ssh2.crypto.cipher.DESede"));
+ ciphers.addElement(new CipherEntry("3des-cbc", 8, 24, "com.trilead.ssh2.crypto.cipher.DESede"));
+ }
+
+ public static String[] getDefaultCipherList()
+ {
+ String list[] = new String[ciphers.size()];
+ for (int i = 0; i < ciphers.size(); i++)
+ {
+ CipherEntry ce = ciphers.elementAt(i);
+ list[i] = new String(ce.type);
+ }
+ return list;
+ }
+
+ public static void checkCipherList(String[] cipherCandidates)
+ {
+ for (int i = 0; i < cipherCandidates.length; i++)
+ getEntry(cipherCandidates[i]);
+ }
+
+ public static BlockCipher createCipher(String type, boolean encrypt, byte[] key, byte[] iv)
+ {
+ try
+ {
+ CipherEntry ce = getEntry(type);
+ Class cc = Class.forName(ce.cipherClass);
+ BlockCipher bc = (BlockCipher) cc.newInstance();
+
+ if (type.endsWith("-cbc"))
+ {
+ bc.init(encrypt, key);
+ return new CBCMode(bc, iv, encrypt);
+ }
+ else if (type.endsWith("-ctr"))
+ {
+ bc.init(true, key);
+ return new CTRMode(bc, iv, encrypt);
+ }
+ throw new IllegalArgumentException("Cannot instantiate " + type);
+ }
+ catch (Exception e)
+ {
+ throw new IllegalArgumentException("Cannot instantiate " + type);
+ }
+ }
+
+ private static CipherEntry getEntry(String type)
+ {
+ for (int i = 0; i < ciphers.size(); i++)
+ {
+ CipherEntry ce = ciphers.elementAt(i);
+ if (ce.type.equals(type))
+ return ce;
+ }
+ throw new IllegalArgumentException("Unkown algorithm " + type);
+ }
+
+ public static int getBlockSize(String type)
+ {
+ CipherEntry ce = getEntry(type);
+ return ce.blocksize;
+ }
+
+ public static int getKeySize(String type)
+ {
+ CipherEntry ce = getEntry(type);
+ return ce.keysize;
+ }
+}
diff --git a/src/com/trilead/ssh2/crypto/cipher/BlowFish.java b/src/com/trilead/ssh2/crypto/cipher/BlowFish.java
index ea0f614..0b2f295 100644
--- a/src/com/trilead/ssh2/crypto/cipher/BlowFish.java
+++ b/src/com/trilead/ssh2/crypto/cipher/BlowFish.java
@@ -1,403 +1,403 @@
-
-package com.trilead.ssh2.crypto.cipher;
-
-/*
- This file was shamelessly taken from the Bouncy Castle Crypto package.
- Their licence file states the following:
-
- Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle
- (http://www.bouncycastle.org)
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
- */
-
-/**
- * A class that provides Blowfish key encryption operations, such as encoding
- * data and generating keys. All the algorithms herein are from Applied
- * Cryptography and implement a simplified cryptography interface.
- *
- * @author See comments in the source file
- * @version $Id: BlowFish.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class BlowFish implements BlockCipher
-{
-
- private final static int[] KP = { 0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344, 0xA4093822, 0x299F31D0,
- 0x082EFA98, 0xEC4E6C89, 0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C, 0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5,
- 0xB5470917, 0x9216D5D9, 0x8979FB1B },
-
- KS0 = { 0xD1310BA6, 0x98DFB5AC, 0x2FFD72DB, 0xD01ADFB7, 0xB8E1AFED, 0x6A267E96, 0xBA7C9045, 0xF12C7F99, 0x24A19947,
- 0xB3916CF7, 0x0801F2E2, 0x858EFC16, 0x636920D8, 0x71574E69, 0xA458FEA3, 0xF4933D7E, 0x0D95748F, 0x728EB658,
- 0x718BCD58, 0x82154AEE, 0x7B54A41D, 0xC25A59B5, 0x9C30D539, 0x2AF26013, 0xC5D1B023, 0x286085F0, 0xCA417918,
- 0xB8DB38EF, 0x8E79DCB0, 0x603A180E, 0x6C9E0E8B, 0xB01E8A3E, 0xD71577C1, 0xBD314B27, 0x78AF2FDA, 0x55605C60,
- 0xE65525F3, 0xAA55AB94, 0x57489862, 0x63E81440, 0x55CA396A, 0x2AAB10B6, 0xB4CC5C34, 0x1141E8CE, 0xA15486AF,
- 0x7C72E993, 0xB3EE1411, 0x636FBC2A, 0x2BA9C55D, 0x741831F6, 0xCE5C3E16, 0x9B87931E, 0xAFD6BA33, 0x6C24CF5C,
- 0x7A325381, 0x28958677, 0x3B8F4898, 0x6B4BB9AF, 0xC4BFE81B, 0x66282193, 0x61D809CC, 0xFB21A991, 0x487CAC60,
- 0x5DEC8032, 0xEF845D5D, 0xE98575B1, 0xDC262302, 0xEB651B88, 0x23893E81, 0xD396ACC5, 0x0F6D6FF3, 0x83F44239,
- 0x2E0B4482, 0xA4842004, 0x69C8F04A, 0x9E1F9B5E, 0x21C66842, 0xF6E96C9A, 0x670C9C61, 0xABD388F0, 0x6A51A0D2,
- 0xD8542F68, 0x960FA728, 0xAB5133A3, 0x6EEF0B6C, 0x137A3BE4, 0xBA3BF050, 0x7EFB2A98, 0xA1F1651D, 0x39AF0176,
- 0x66CA593E, 0x82430E88, 0x8CEE8619, 0x456F9FB4, 0x7D84A5C3, 0x3B8B5EBE, 0xE06F75D8, 0x85C12073, 0x401A449F,
- 0x56C16AA6, 0x4ED3AA62, 0x363F7706, 0x1BFEDF72, 0x429B023D, 0x37D0D724, 0xD00A1248, 0xDB0FEAD3, 0x49F1C09B,
- 0x075372C9, 0x80991B7B, 0x25D479D8, 0xF6E8DEF7, 0xE3FE501A, 0xB6794C3B, 0x976CE0BD, 0x04C006BA, 0xC1A94FB6,
- 0x409F60C4, 0x5E5C9EC2, 0x196A2463, 0x68FB6FAF, 0x3E6C53B5, 0x1339B2EB, 0x3B52EC6F, 0x6DFC511F, 0x9B30952C,
- 0xCC814544, 0xAF5EBD09, 0xBEE3D004, 0xDE334AFD, 0x660F2807, 0x192E4BB3, 0xC0CBA857, 0x45C8740F, 0xD20B5F39,
- 0xB9D3FBDB, 0x5579C0BD, 0x1A60320A, 0xD6A100C6, 0x402C7279, 0x679F25FE, 0xFB1FA3CC, 0x8EA5E9F8, 0xDB3222F8,
- 0x3C7516DF, 0xFD616B15, 0x2F501EC8, 0xAD0552AB, 0x323DB5FA, 0xFD238760, 0x53317B48, 0x3E00DF82, 0x9E5C57BB,
- 0xCA6F8CA0, 0x1A87562E, 0xDF1769DB, 0xD542A8F6, 0x287EFFC3, 0xAC6732C6, 0x8C4F5573, 0x695B27B0, 0xBBCA58C8,
- 0xE1FFA35D, 0xB8F011A0, 0x10FA3D98, 0xFD2183B8, 0x4AFCB56C, 0x2DD1D35B, 0x9A53E479, 0xB6F84565, 0xD28E49BC,
- 0x4BFB9790, 0xE1DDF2DA, 0xA4CB7E33, 0x62FB1341, 0xCEE4C6E8, 0xEF20CADA, 0x36774C01, 0xD07E9EFE, 0x2BF11FB4,
- 0x95DBDA4D, 0xAE909198, 0xEAAD8E71, 0x6B93D5A0, 0xD08ED1D0, 0xAFC725E0, 0x8E3C5B2F, 0x8E7594B7, 0x8FF6E2FB,
- 0xF2122B64, 0x8888B812, 0x900DF01C, 0x4FAD5EA0, 0x688FC31C, 0xD1CFF191, 0xB3A8C1AD, 0x2F2F2218, 0xBE0E1777,
- 0xEA752DFE, 0x8B021FA1, 0xE5A0CC0F, 0xB56F74E8, 0x18ACF3D6, 0xCE89E299, 0xB4A84FE0, 0xFD13E0B7, 0x7CC43B81,
- 0xD2ADA8D9, 0x165FA266, 0x80957705, 0x93CC7314, 0x211A1477, 0xE6AD2065, 0x77B5FA86, 0xC75442F5, 0xFB9D35CF,
- 0xEBCDAF0C, 0x7B3E89A0, 0xD6411BD3, 0xAE1E7E49, 0x00250E2D, 0x2071B35E, 0x226800BB, 0x57B8E0AF, 0x2464369B,
- 0xF009B91E, 0x5563911D, 0x59DFA6AA, 0x78C14389, 0xD95A537F, 0x207D5BA2, 0x02E5B9C5, 0x83260376, 0x6295CFA9,
- 0x11C81968, 0x4E734A41, 0xB3472DCA, 0x7B14A94A, 0x1B510052, 0x9A532915, 0xD60F573F, 0xBC9BC6E4, 0x2B60A476,
- 0x81E67400, 0x08BA6FB5, 0x571BE91F, 0xF296EC6B, 0x2A0DD915, 0xB6636521, 0xE7B9F9B6, 0xFF34052E, 0xC5855664,
- 0x53B02D5D, 0xA99F8FA1, 0x08BA4799, 0x6E85076A },
-
- KS1 = { 0x4B7A70E9, 0xB5B32944, 0xDB75092E, 0xC4192623, 0xAD6EA6B0, 0x49A7DF7D, 0x9CEE60B8, 0x8FEDB266, 0xECAA8C71,
- 0x699A17FF, 0x5664526C, 0xC2B19EE1, 0x193602A5, 0x75094C29, 0xA0591340, 0xE4183A3E, 0x3F54989A, 0x5B429D65,
- 0x6B8FE4D6, 0x99F73FD6, 0xA1D29C07, 0xEFE830F5, 0x4D2D38E6, 0xF0255DC1, 0x4CDD2086, 0x8470EB26, 0x6382E9C6,
- 0x021ECC5E, 0x09686B3F, 0x3EBAEFC9, 0x3C971814, 0x6B6A70A1, 0x687F3584, 0x52A0E286, 0xB79C5305, 0xAA500737,
- 0x3E07841C, 0x7FDEAE5C, 0x8E7D44EC, 0x5716F2B8, 0xB03ADA37, 0xF0500C0D, 0xF01C1F04, 0x0200B3FF, 0xAE0CF51A,
- 0x3CB574B2, 0x25837A58, 0xDC0921BD, 0xD19113F9, 0x7CA92FF6, 0x94324773, 0x22F54701, 0x3AE5E581, 0x37C2DADC,
- 0xC8B57634, 0x9AF3DDA7, 0xA9446146, 0x0FD0030E, 0xECC8C73E, 0xA4751E41, 0xE238CD99, 0x3BEA0E2F, 0x3280BBA1,
- 0x183EB331, 0x4E548B38, 0x4F6DB908, 0x6F420D03, 0xF60A04BF, 0x2CB81290, 0x24977C79, 0x5679B072, 0xBCAF89AF,
- 0xDE9A771F, 0xD9930810, 0xB38BAE12, 0xDCCF3F2E, 0x5512721F, 0x2E6B7124, 0x501ADDE6, 0x9F84CD87, 0x7A584718,
- 0x7408DA17, 0xBC9F9ABC, 0xE94B7D8C, 0xEC7AEC3A, 0xDB851DFA, 0x63094366, 0xC464C3D2, 0xEF1C1847, 0x3215D908,
- 0xDD433B37, 0x24C2BA16, 0x12A14D43, 0x2A65C451, 0x50940002, 0x133AE4DD, 0x71DFF89E, 0x10314E55, 0x81AC77D6,
- 0x5F11199B, 0x043556F1, 0xD7A3C76B, 0x3C11183B, 0x5924A509, 0xF28FE6ED, 0x97F1FBFA, 0x9EBABF2C, 0x1E153C6E,
- 0x86E34570, 0xEAE96FB1, 0x860E5E0A, 0x5A3E2AB3, 0x771FE71C, 0x4E3D06FA, 0x2965DCB9, 0x99E71D0F, 0x803E89D6,
- 0x5266C825, 0x2E4CC978, 0x9C10B36A, 0xC6150EBA, 0x94E2EA78, 0xA5FC3C53, 0x1E0A2DF4, 0xF2F74EA7, 0x361D2B3D,
- 0x1939260F, 0x19C27960, 0x5223A708, 0xF71312B6, 0xEBADFE6E, 0xEAC31F66, 0xE3BC4595, 0xA67BC883, 0xB17F37D1,
- 0x018CFF28, 0xC332DDEF, 0xBE6C5AA5, 0x65582185, 0x68AB9802, 0xEECEA50F, 0xDB2F953B, 0x2AEF7DAD, 0x5B6E2F84,
- 0x1521B628, 0x29076170, 0xECDD4775, 0x619F1510, 0x13CCA830, 0xEB61BD96, 0x0334FE1E, 0xAA0363CF, 0xB5735C90,
- 0x4C70A239, 0xD59E9E0B, 0xCBAADE14, 0xEECC86BC, 0x60622CA7, 0x9CAB5CAB, 0xB2F3846E, 0x648B1EAF, 0x19BDF0CA,
- 0xA02369B9, 0x655ABB50, 0x40685A32, 0x3C2AB4B3, 0x319EE9D5, 0xC021B8F7, 0x9B540B19, 0x875FA099, 0x95F7997E,
- 0x623D7DA8, 0xF837889A, 0x97E32D77, 0x11ED935F, 0x16681281, 0x0E358829, 0xC7E61FD6, 0x96DEDFA1, 0x7858BA99,
- 0x57F584A5, 0x1B227263, 0x9B83C3FF, 0x1AC24696, 0xCDB30AEB, 0x532E3054, 0x8FD948E4, 0x6DBC3128, 0x58EBF2EF,
- 0x34C6FFEA, 0xFE28ED61, 0xEE7C3C73, 0x5D4A14D9, 0xE864B7E3, 0x42105D14, 0x203E13E0, 0x45EEE2B6, 0xA3AAABEA,
- 0xDB6C4F15, 0xFACB4FD0, 0xC742F442, 0xEF6ABBB5, 0x654F3B1D, 0x41CD2105, 0xD81E799E, 0x86854DC7, 0xE44B476A,
- 0x3D816250, 0xCF62A1F2, 0x5B8D2646, 0xFC8883A0, 0xC1C7B6A3, 0x7F1524C3, 0x69CB7492, 0x47848A0B, 0x5692B285,
- 0x095BBF00, 0xAD19489D, 0x1462B174, 0x23820E00, 0x58428D2A, 0x0C55F5EA, 0x1DADF43E, 0x233F7061, 0x3372F092,
- 0x8D937E41, 0xD65FECF1, 0x6C223BDB, 0x7CDE3759, 0xCBEE7460, 0x4085F2A7, 0xCE77326E, 0xA6078084, 0x19F8509E,
- 0xE8EFD855, 0x61D99735, 0xA969A7AA, 0xC50C06C2, 0x5A04ABFC, 0x800BCADC, 0x9E447A2E, 0xC3453484, 0xFDD56705,
- 0x0E1E9EC9, 0xDB73DBD3, 0x105588CD, 0x675FDA79, 0xE3674340, 0xC5C43465, 0x713E38D8, 0x3D28F89E, 0xF16DFF20,
- 0x153E21E7, 0x8FB03D4A, 0xE6E39F2B, 0xDB83ADF7 },
-
- KS2 = { 0xE93D5A68, 0x948140F7, 0xF64C261C, 0x94692934, 0x411520F7, 0x7602D4F7, 0xBCF46B2E, 0xD4A20068, 0xD4082471,
- 0x3320F46A, 0x43B7D4B7, 0x500061AF, 0x1E39F62E, 0x97244546, 0x14214F74, 0xBF8B8840, 0x4D95FC1D, 0x96B591AF,
- 0x70F4DDD3, 0x66A02F45, 0xBFBC09EC, 0x03BD9785, 0x7FAC6DD0, 0x31CB8504, 0x96EB27B3, 0x55FD3941, 0xDA2547E6,
- 0xABCA0A9A, 0x28507825, 0x530429F4, 0x0A2C86DA, 0xE9B66DFB, 0x68DC1462, 0xD7486900, 0x680EC0A4, 0x27A18DEE,
- 0x4F3FFEA2, 0xE887AD8C, 0xB58CE006, 0x7AF4D6B6, 0xAACE1E7C, 0xD3375FEC, 0xCE78A399, 0x406B2A42, 0x20FE9E35,
- 0xD9F385B9, 0xEE39D7AB, 0x3B124E8B, 0x1DC9FAF7, 0x4B6D1856, 0x26A36631, 0xEAE397B2, 0x3A6EFA74, 0xDD5B4332,
- 0x6841E7F7, 0xCA7820FB, 0xFB0AF54E, 0xD8FEB397, 0x454056AC, 0xBA489527, 0x55533A3A, 0x20838D87, 0xFE6BA9B7,
- 0xD096954B, 0x55A867BC, 0xA1159A58, 0xCCA92963, 0x99E1DB33, 0xA62A4A56, 0x3F3125F9, 0x5EF47E1C, 0x9029317C,
- 0xFDF8E802, 0x04272F70, 0x80BB155C, 0x05282CE3, 0x95C11548, 0xE4C66D22, 0x48C1133F, 0xC70F86DC, 0x07F9C9EE,
- 0x41041F0F, 0x404779A4, 0x5D886E17, 0x325F51EB, 0xD59BC0D1, 0xF2BCC18F, 0x41113564, 0x257B7834, 0x602A9C60,
- 0xDFF8E8A3, 0x1F636C1B, 0x0E12B4C2, 0x02E1329E, 0xAF664FD1, 0xCAD18115, 0x6B2395E0, 0x333E92E1, 0x3B240B62,
- 0xEEBEB922, 0x85B2A20E, 0xE6BA0D99, 0xDE720C8C, 0x2DA2F728, 0xD0127845, 0x95B794FD, 0x647D0862, 0xE7CCF5F0,
- 0x5449A36F, 0x877D48FA, 0xC39DFD27, 0xF33E8D1E, 0x0A476341, 0x992EFF74, 0x3A6F6EAB, 0xF4F8FD37, 0xA812DC60,
- 0xA1EBDDF8, 0x991BE14C, 0xDB6E6B0D, 0xC67B5510, 0x6D672C37, 0x2765D43B, 0xDCD0E804, 0xF1290DC7, 0xCC00FFA3,
- 0xB5390F92, 0x690FED0B, 0x667B9FFB, 0xCEDB7D9C, 0xA091CF0B, 0xD9155EA3, 0xBB132F88, 0x515BAD24, 0x7B9479BF,
- 0x763BD6EB, 0x37392EB3, 0xCC115979, 0x8026E297, 0xF42E312D, 0x6842ADA7, 0xC66A2B3B, 0x12754CCC, 0x782EF11C,
- 0x6A124237, 0xB79251E7, 0x06A1BBE6, 0x4BFB6350, 0x1A6B1018, 0x11CAEDFA, 0x3D25BDD8, 0xE2E1C3C9, 0x44421659,
- 0x0A121386, 0xD90CEC6E, 0xD5ABEA2A, 0x64AF674E, 0xDA86A85F, 0xBEBFE988, 0x64E4C3FE, 0x9DBC8057, 0xF0F7C086,
- 0x60787BF8, 0x6003604D, 0xD1FD8346, 0xF6381FB0, 0x7745AE04, 0xD736FCCC, 0x83426B33, 0xF01EAB71, 0xB0804187,
- 0x3C005E5F, 0x77A057BE, 0xBDE8AE24, 0x55464299, 0xBF582E61, 0x4E58F48F, 0xF2DDFDA2, 0xF474EF38, 0x8789BDC2,
- 0x5366F9C3, 0xC8B38E74, 0xB475F255, 0x46FCD9B9, 0x7AEB2661, 0x8B1DDF84, 0x846A0E79, 0x915F95E2, 0x466E598E,
- 0x20B45770, 0x8CD55591, 0xC902DE4C, 0xB90BACE1, 0xBB8205D0, 0x11A86248, 0x7574A99E, 0xB77F19B6, 0xE0A9DC09,
- 0x662D09A1, 0xC4324633, 0xE85A1F02, 0x09F0BE8C, 0x4A99A025, 0x1D6EFE10, 0x1AB93D1D, 0x0BA5A4DF, 0xA186F20F,
- 0x2868F169, 0xDCB7DA83, 0x573906FE, 0xA1E2CE9B, 0x4FCD7F52, 0x50115E01, 0xA70683FA, 0xA002B5C4, 0x0DE6D027,
- 0x9AF88C27, 0x773F8641, 0xC3604C06, 0x61A806B5, 0xF0177A28, 0xC0F586E0, 0x006058AA, 0x30DC7D62, 0x11E69ED7,
- 0x2338EA63, 0x53C2DD94, 0xC2C21634, 0xBBCBEE56, 0x90BCB6DE, 0xEBFC7DA1, 0xCE591D76, 0x6F05E409, 0x4B7C0188,
- 0x39720A3D, 0x7C927C24, 0x86E3725F, 0x724D9DB9, 0x1AC15BB4, 0xD39EB8FC, 0xED545578, 0x08FCA5B5, 0xD83D7CD3,
- 0x4DAD0FC4, 0x1E50EF5E, 0xB161E6F8, 0xA28514D9, 0x6C51133C, 0x6FD5C7E7, 0x56E14EC4, 0x362ABFCE, 0xDDC6C837,
- 0xD79A3234, 0x92638212, 0x670EFA8E, 0x406000E0 },
-
- KS3 = { 0x3A39CE37, 0xD3FAF5CF, 0xABC27737, 0x5AC52D1B, 0x5CB0679E, 0x4FA33742, 0xD3822740, 0x99BC9BBE, 0xD5118E9D,
- 0xBF0F7315, 0xD62D1C7E, 0xC700C47B, 0xB78C1B6B, 0x21A19045, 0xB26EB1BE, 0x6A366EB4, 0x5748AB2F, 0xBC946E79,
- 0xC6A376D2, 0x6549C2C8, 0x530FF8EE, 0x468DDE7D, 0xD5730A1D, 0x4CD04DC6, 0x2939BBDB, 0xA9BA4650, 0xAC9526E8,
- 0xBE5EE304, 0xA1FAD5F0, 0x6A2D519A, 0x63EF8CE2, 0x9A86EE22, 0xC089C2B8, 0x43242EF6, 0xA51E03AA, 0x9CF2D0A4,
- 0x83C061BA, 0x9BE96A4D, 0x8FE51550, 0xBA645BD6, 0x2826A2F9, 0xA73A3AE1, 0x4BA99586, 0xEF5562E9, 0xC72FEFD3,
- 0xF752F7DA, 0x3F046F69, 0x77FA0A59, 0x80E4A915, 0x87B08601, 0x9B09E6AD, 0x3B3EE593, 0xE990FD5A, 0x9E34D797,
- 0x2CF0B7D9, 0x022B8B51, 0x96D5AC3A, 0x017DA67D, 0xD1CF3ED6, 0x7C7D2D28, 0x1F9F25CF, 0xADF2B89B, 0x5AD6B472,
- 0x5A88F54C, 0xE029AC71, 0xE019A5E6, 0x47B0ACFD, 0xED93FA9B, 0xE8D3C48D, 0x283B57CC, 0xF8D56629, 0x79132E28,
- 0x785F0191, 0xED756055, 0xF7960E44, 0xE3D35E8C, 0x15056DD4, 0x88F46DBA, 0x03A16125, 0x0564F0BD, 0xC3EB9E15,
- 0x3C9057A2, 0x97271AEC, 0xA93A072A, 0x1B3F6D9B, 0x1E6321F5, 0xF59C66FB, 0x26DCF319, 0x7533D928, 0xB155FDF5,
- 0x03563482, 0x8ABA3CBB, 0x28517711, 0xC20AD9F8, 0xABCC5167, 0xCCAD925F, 0x4DE81751, 0x3830DC8E, 0x379D5862,
- 0x9320F991, 0xEA7A90C2, 0xFB3E7BCE, 0x5121CE64, 0x774FBE32, 0xA8B6E37E, 0xC3293D46, 0x48DE5369, 0x6413E680,
- 0xA2AE0810, 0xDD6DB224, 0x69852DFD, 0x09072166, 0xB39A460A, 0x6445C0DD, 0x586CDECF, 0x1C20C8AE, 0x5BBEF7DD,
- 0x1B588D40, 0xCCD2017F, 0x6BB4E3BB, 0xDDA26A7E, 0x3A59FF45, 0x3E350A44, 0xBCB4CDD5, 0x72EACEA8, 0xFA6484BB,
- 0x8D6612AE, 0xBF3C6F47, 0xD29BE463, 0x542F5D9E, 0xAEC2771B, 0xF64E6370, 0x740E0D8D, 0xE75B1357, 0xF8721671,
- 0xAF537D5D, 0x4040CB08, 0x4EB4E2CC, 0x34D2466A, 0x0115AF84, 0xE1B00428, 0x95983A1D, 0x06B89FB4, 0xCE6EA048,
- 0x6F3F3B82, 0x3520AB82, 0x011A1D4B, 0x277227F8, 0x611560B1, 0xE7933FDC, 0xBB3A792B, 0x344525BD, 0xA08839E1,
- 0x51CE794B, 0x2F32C9B7, 0xA01FBAC9, 0xE01CC87E, 0xBCC7D1F6, 0xCF0111C3, 0xA1E8AAC7, 0x1A908749, 0xD44FBD9A,
- 0xD0DADECB, 0xD50ADA38, 0x0339C32A, 0xC6913667, 0x8DF9317C, 0xE0B12B4F, 0xF79E59B7, 0x43F5BB3A, 0xF2D519FF,
- 0x27D9459C, 0xBF97222C, 0x15E6FC2A, 0x0F91FC71, 0x9B941525, 0xFAE59361, 0xCEB69CEB, 0xC2A86459, 0x12BAA8D1,
- 0xB6C1075E, 0xE3056A0C, 0x10D25065, 0xCB03A442, 0xE0EC6E0E, 0x1698DB3B, 0x4C98A0BE, 0x3278E964, 0x9F1F9532,
- 0xE0D392DF, 0xD3A0342B, 0x8971F21E, 0x1B0A7441, 0x4BA3348C, 0xC5BE7120, 0xC37632D8, 0xDF359F8D, 0x9B992F2E,
- 0xE60B6F47, 0x0FE3F11D, 0xE54CDA54, 0x1EDAD891, 0xCE6279CF, 0xCD3E7E6F, 0x1618B166, 0xFD2C1D05, 0x848FD2C5,
- 0xF6FB2299, 0xF523F357, 0xA6327623, 0x93A83531, 0x56CCCD02, 0xACF08162, 0x5A75EBB5, 0x6E163697, 0x88D273CC,
- 0xDE966292, 0x81B949D0, 0x4C50901B, 0x71C65614, 0xE6C6C7BD, 0x327A140A, 0x45E1D006, 0xC3F27B9A, 0xC9AA53FD,
- 0x62A80F00, 0xBB25BFE2, 0x35BDD2F6, 0x71126905, 0xB2040222, 0xB6CBCF7C, 0xCD769C2B, 0x53113EC0, 0x1640E3D3,
- 0x38ABBD60, 0x2547ADF0, 0xBA38209C, 0xF746CE76, 0x77AFA1C5, 0x20756060, 0x85CBFE4E, 0x8AE88DD8, 0x7AAAF9B0,
- 0x4CF9AA7E, 0x1948C25C, 0x02FB8A8C, 0x01C36AE4, 0xD6EBE1F9, 0x90D4F869, 0xA65CDEA0, 0x3F09252D, 0xC208E69F,
- 0xB74E6132, 0xCE77E25B, 0x578FDFE3, 0x3AC372E6 };
-
- // ====================================
- // Useful constants
- // ====================================
-
- private static final int ROUNDS = 16;
- private static final int BLOCK_SIZE = 8; // bytes = 64 bits
- private static final int SBOX_SK = 256;
- private static final int P_SZ = ROUNDS + 2;
-
- private final int[] S0, S1, S2, S3; // the s-boxes
- private final int[] P; // the p-array
-
- private boolean doEncrypt = false;
-
- private byte[] workingKey = null;
-
- public BlowFish()
- {
- S0 = new int[SBOX_SK];
- S1 = new int[SBOX_SK];
- S2 = new int[SBOX_SK];
- S3 = new int[SBOX_SK];
- P = new int[P_SZ];
- }
-
- /**
- * initialise a Blowfish cipher.
- *
- * @param encrypting
- * whether or not we are for encryption.
- * @param key
- * the key required to set up the cipher.
- * @exception IllegalArgumentException
- * if the params argument is inappropriate.
- */
- public void init(boolean encrypting, byte[] key)
- {
- this.doEncrypt = encrypting;
- this.workingKey = key;
- setKey(this.workingKey);
- }
-
- public String getAlgorithmName()
- {
- return "Blowfish";
- }
-
- public final void transformBlock(byte[] in, int inOff, byte[] out, int outOff)
- {
- if (workingKey == null)
- {
- throw new IllegalStateException("Blowfish not initialised");
- }
-
- if (doEncrypt)
- {
- encryptBlock(in, inOff, out, outOff);
- }
- else
- {
- decryptBlock(in, inOff, out, outOff);
- }
- }
-
- public void reset()
- {
- }
-
- public int getBlockSize()
- {
- return BLOCK_SIZE;
- }
-
- // ==================================
- // Private Implementation
- // ==================================
-
- private int F(int x)
- {
- return (((S0[(x >>> 24)] + S1[(x >>> 16) & 0xff]) ^ S2[(x >>> 8) & 0xff]) + S3[x & 0xff]);
- }
-
- /**
- * apply the encryption cycle to each value pair in the table.
- */
- private void processTable(int xl, int xr, int[] table)
- {
- int size = table.length;
-
- for (int s = 0; s < size; s += 2)
- {
- xl ^= P[0];
-
- for (int i = 1; i < ROUNDS; i += 2)
- {
- xr ^= F(xl) ^ P[i];
- xl ^= F(xr) ^ P[i + 1];
- }
-
- xr ^= P[ROUNDS + 1];
-
- table[s] = xr;
- table[s + 1] = xl;
-
- xr = xl; // end of cycle swap
- xl = table[s];
- }
- }
-
- private void setKey(byte[] key)
- {
- /*
- * - comments are from _Applied Crypto_, Schneier, p338 please be
- * careful comparing the two, AC numbers the arrays from 1, the enclosed
- * code from 0.
- *
- * (1) Initialise the S-boxes and the P-array, with a fixed string This
- * string contains the hexadecimal digits of pi (3.141...)
- */
- System.arraycopy(KS0, 0, S0, 0, SBOX_SK);
- System.arraycopy(KS1, 0, S1, 0, SBOX_SK);
- System.arraycopy(KS2, 0, S2, 0, SBOX_SK);
- System.arraycopy(KS3, 0, S3, 0, SBOX_SK);
-
- System.arraycopy(KP, 0, P, 0, P_SZ);
-
- /*
- * (2) Now, XOR P[0] with the first 32 bits of the key, XOR P[1] with
- * the second 32-bits of the key, and so on for all bits of the key (up
- * to P[17]). Repeatedly cycle through the key bits until the entire
- * P-array has been XOR-ed with the key bits
- */
- int keyLength = key.length;
- int keyIndex = 0;
-
- for (int i = 0; i < P_SZ; i++)
- {
- // get the 32 bits of the key, in 4 * 8 bit chunks
- int data = 0x0000000;
- for (int j = 0; j < 4; j++)
- {
- // create a 32 bit block
- data = (data << 8) | (key[keyIndex++] & 0xff);
-
- // wrap when we get to the end of the key
- if (keyIndex >= keyLength)
- {
- keyIndex = 0;
- }
- }
- // XOR the newly created 32 bit chunk onto the P-array
- P[i] ^= data;
- }
-
- /*
- * (3) Encrypt the all-zero string with the Blowfish algorithm, using
- * the subkeys described in (1) and (2)
- *
- * (4) Replace P1 and P2 with the output of step (3)
- *
- * (5) Encrypt the output of step(3) using the Blowfish algorithm, with
- * the modified subkeys.
- *
- * (6) Replace P3 and P4 with the output of step (5)
- *
- * (7) Continue the process, replacing all elements of the P-array and
- * then all four S-boxes in order, with the output of the continuously
- * changing Blowfish algorithm
- */
-
- processTable(0, 0, P);
- processTable(P[P_SZ - 2], P[P_SZ - 1], S0);
- processTable(S0[SBOX_SK - 2], S0[SBOX_SK - 1], S1);
- processTable(S1[SBOX_SK - 2], S1[SBOX_SK - 1], S2);
- processTable(S2[SBOX_SK - 2], S2[SBOX_SK - 1], S3);
- }
-
- /**
- * Encrypt the given input starting at the given offset and place the result
- * in the provided buffer starting at the given offset. The input will be an
- * exact multiple of our blocksize.
- */
- private void encryptBlock(byte[] src, int srcIndex, byte[] dst, int dstIndex)
- {
- int xl = BytesTo32bits(src, srcIndex);
- int xr = BytesTo32bits(src, srcIndex + 4);
-
- xl ^= P[0];
-
- for (int i = 1; i < ROUNDS; i += 2)
- {
- xr ^= F(xl) ^ P[i];
- xl ^= F(xr) ^ P[i + 1];
- }
-
- xr ^= P[ROUNDS + 1];
-
- Bits32ToBytes(xr, dst, dstIndex);
- Bits32ToBytes(xl, dst, dstIndex + 4);
- }
-
- /**
- * Decrypt the given input starting at the given offset and place the result
- * in the provided buffer starting at the given offset. The input will be an
- * exact multiple of our blocksize.
- */
- private void decryptBlock(byte[] src, int srcIndex, byte[] dst, int dstIndex)
- {
- int xl = BytesTo32bits(src, srcIndex);
- int xr = BytesTo32bits(src, srcIndex + 4);
-
- xl ^= P[ROUNDS + 1];
-
- for (int i = ROUNDS; i > 0; i -= 2)
- {
- xr ^= F(xl) ^ P[i];
- xl ^= F(xr) ^ P[i - 1];
- }
-
- xr ^= P[0];
-
- Bits32ToBytes(xr, dst, dstIndex);
- Bits32ToBytes(xl, dst, dstIndex + 4);
- }
-
- private int BytesTo32bits(byte[] b, int i)
- {
- return ((b[i] & 0xff) << 24) | ((b[i + 1] & 0xff) << 16) | ((b[i + 2] & 0xff) << 8) | ((b[i + 3] & 0xff));
- }
-
- private void Bits32ToBytes(int in, byte[] b, int offset)
- {
- b[offset + 3] = (byte) in;
- b[offset + 2] = (byte) (in >> 8);
- b[offset + 1] = (byte) (in >> 16);
- b[offset] = (byte) (in >> 24);
- }
-
-}
+
+package com.trilead.ssh2.crypto.cipher;
+
+/*
+ This file was shamelessly taken from the Bouncy Castle Crypto package.
+ Their licence file states the following:
+
+ Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle
+ (http://www.bouncycastle.org)
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ */
+
+/**
+ * A class that provides Blowfish key encryption operations, such as encoding
+ * data and generating keys. All the algorithms herein are from Applied
+ * Cryptography and implement a simplified cryptography interface.
+ *
+ * @author See comments in the source file
+ * @version $Id: BlowFish.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class BlowFish implements BlockCipher
+{
+
+ private final static int[] KP = { 0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344, 0xA4093822, 0x299F31D0,
+ 0x082EFA98, 0xEC4E6C89, 0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C, 0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5,
+ 0xB5470917, 0x9216D5D9, 0x8979FB1B },
+
+ KS0 = { 0xD1310BA6, 0x98DFB5AC, 0x2FFD72DB, 0xD01ADFB7, 0xB8E1AFED, 0x6A267E96, 0xBA7C9045, 0xF12C7F99, 0x24A19947,
+ 0xB3916CF7, 0x0801F2E2, 0x858EFC16, 0x636920D8, 0x71574E69, 0xA458FEA3, 0xF4933D7E, 0x0D95748F, 0x728EB658,
+ 0x718BCD58, 0x82154AEE, 0x7B54A41D, 0xC25A59B5, 0x9C30D539, 0x2AF26013, 0xC5D1B023, 0x286085F0, 0xCA417918,
+ 0xB8DB38EF, 0x8E79DCB0, 0x603A180E, 0x6C9E0E8B, 0xB01E8A3E, 0xD71577C1, 0xBD314B27, 0x78AF2FDA, 0x55605C60,
+ 0xE65525F3, 0xAA55AB94, 0x57489862, 0x63E81440, 0x55CA396A, 0x2AAB10B6, 0xB4CC5C34, 0x1141E8CE, 0xA15486AF,
+ 0x7C72E993, 0xB3EE1411, 0x636FBC2A, 0x2BA9C55D, 0x741831F6, 0xCE5C3E16, 0x9B87931E, 0xAFD6BA33, 0x6C24CF5C,
+ 0x7A325381, 0x28958677, 0x3B8F4898, 0x6B4BB9AF, 0xC4BFE81B, 0x66282193, 0x61D809CC, 0xFB21A991, 0x487CAC60,
+ 0x5DEC8032, 0xEF845D5D, 0xE98575B1, 0xDC262302, 0xEB651B88, 0x23893E81, 0xD396ACC5, 0x0F6D6FF3, 0x83F44239,
+ 0x2E0B4482, 0xA4842004, 0x69C8F04A, 0x9E1F9B5E, 0x21C66842, 0xF6E96C9A, 0x670C9C61, 0xABD388F0, 0x6A51A0D2,
+ 0xD8542F68, 0x960FA728, 0xAB5133A3, 0x6EEF0B6C, 0x137A3BE4, 0xBA3BF050, 0x7EFB2A98, 0xA1F1651D, 0x39AF0176,
+ 0x66CA593E, 0x82430E88, 0x8CEE8619, 0x456F9FB4, 0x7D84A5C3, 0x3B8B5EBE, 0xE06F75D8, 0x85C12073, 0x401A449F,
+ 0x56C16AA6, 0x4ED3AA62, 0x363F7706, 0x1BFEDF72, 0x429B023D, 0x37D0D724, 0xD00A1248, 0xDB0FEAD3, 0x49F1C09B,
+ 0x075372C9, 0x80991B7B, 0x25D479D8, 0xF6E8DEF7, 0xE3FE501A, 0xB6794C3B, 0x976CE0BD, 0x04C006BA, 0xC1A94FB6,
+ 0x409F60C4, 0x5E5C9EC2, 0x196A2463, 0x68FB6FAF, 0x3E6C53B5, 0x1339B2EB, 0x3B52EC6F, 0x6DFC511F, 0x9B30952C,
+ 0xCC814544, 0xAF5EBD09, 0xBEE3D004, 0xDE334AFD, 0x660F2807, 0x192E4BB3, 0xC0CBA857, 0x45C8740F, 0xD20B5F39,
+ 0xB9D3FBDB, 0x5579C0BD, 0x1A60320A, 0xD6A100C6, 0x402C7279, 0x679F25FE, 0xFB1FA3CC, 0x8EA5E9F8, 0xDB3222F8,
+ 0x3C7516DF, 0xFD616B15, 0x2F501EC8, 0xAD0552AB, 0x323DB5FA, 0xFD238760, 0x53317B48, 0x3E00DF82, 0x9E5C57BB,
+ 0xCA6F8CA0, 0x1A87562E, 0xDF1769DB, 0xD542A8F6, 0x287EFFC3, 0xAC6732C6, 0x8C4F5573, 0x695B27B0, 0xBBCA58C8,
+ 0xE1FFA35D, 0xB8F011A0, 0x10FA3D98, 0xFD2183B8, 0x4AFCB56C, 0x2DD1D35B, 0x9A53E479, 0xB6F84565, 0xD28E49BC,
+ 0x4BFB9790, 0xE1DDF2DA, 0xA4CB7E33, 0x62FB1341, 0xCEE4C6E8, 0xEF20CADA, 0x36774C01, 0xD07E9EFE, 0x2BF11FB4,
+ 0x95DBDA4D, 0xAE909198, 0xEAAD8E71, 0x6B93D5A0, 0xD08ED1D0, 0xAFC725E0, 0x8E3C5B2F, 0x8E7594B7, 0x8FF6E2FB,
+ 0xF2122B64, 0x8888B812, 0x900DF01C, 0x4FAD5EA0, 0x688FC31C, 0xD1CFF191, 0xB3A8C1AD, 0x2F2F2218, 0xBE0E1777,
+ 0xEA752DFE, 0x8B021FA1, 0xE5A0CC0F, 0xB56F74E8, 0x18ACF3D6, 0xCE89E299, 0xB4A84FE0, 0xFD13E0B7, 0x7CC43B81,
+ 0xD2ADA8D9, 0x165FA266, 0x80957705, 0x93CC7314, 0x211A1477, 0xE6AD2065, 0x77B5FA86, 0xC75442F5, 0xFB9D35CF,
+ 0xEBCDAF0C, 0x7B3E89A0, 0xD6411BD3, 0xAE1E7E49, 0x00250E2D, 0x2071B35E, 0x226800BB, 0x57B8E0AF, 0x2464369B,
+ 0xF009B91E, 0x5563911D, 0x59DFA6AA, 0x78C14389, 0xD95A537F, 0x207D5BA2, 0x02E5B9C5, 0x83260376, 0x6295CFA9,
+ 0x11C81968, 0x4E734A41, 0xB3472DCA, 0x7B14A94A, 0x1B510052, 0x9A532915, 0xD60F573F, 0xBC9BC6E4, 0x2B60A476,
+ 0x81E67400, 0x08BA6FB5, 0x571BE91F, 0xF296EC6B, 0x2A0DD915, 0xB6636521, 0xE7B9F9B6, 0xFF34052E, 0xC5855664,
+ 0x53B02D5D, 0xA99F8FA1, 0x08BA4799, 0x6E85076A },
+
+ KS1 = { 0x4B7A70E9, 0xB5B32944, 0xDB75092E, 0xC4192623, 0xAD6EA6B0, 0x49A7DF7D, 0x9CEE60B8, 0x8FEDB266, 0xECAA8C71,
+ 0x699A17FF, 0x5664526C, 0xC2B19EE1, 0x193602A5, 0x75094C29, 0xA0591340, 0xE4183A3E, 0x3F54989A, 0x5B429D65,
+ 0x6B8FE4D6, 0x99F73FD6, 0xA1D29C07, 0xEFE830F5, 0x4D2D38E6, 0xF0255DC1, 0x4CDD2086, 0x8470EB26, 0x6382E9C6,
+ 0x021ECC5E, 0x09686B3F, 0x3EBAEFC9, 0x3C971814, 0x6B6A70A1, 0x687F3584, 0x52A0E286, 0xB79C5305, 0xAA500737,
+ 0x3E07841C, 0x7FDEAE5C, 0x8E7D44EC, 0x5716F2B8, 0xB03ADA37, 0xF0500C0D, 0xF01C1F04, 0x0200B3FF, 0xAE0CF51A,
+ 0x3CB574B2, 0x25837A58, 0xDC0921BD, 0xD19113F9, 0x7CA92FF6, 0x94324773, 0x22F54701, 0x3AE5E581, 0x37C2DADC,
+ 0xC8B57634, 0x9AF3DDA7, 0xA9446146, 0x0FD0030E, 0xECC8C73E, 0xA4751E41, 0xE238CD99, 0x3BEA0E2F, 0x3280BBA1,
+ 0x183EB331, 0x4E548B38, 0x4F6DB908, 0x6F420D03, 0xF60A04BF, 0x2CB81290, 0x24977C79, 0x5679B072, 0xBCAF89AF,
+ 0xDE9A771F, 0xD9930810, 0xB38BAE12, 0xDCCF3F2E, 0x5512721F, 0x2E6B7124, 0x501ADDE6, 0x9F84CD87, 0x7A584718,
+ 0x7408DA17, 0xBC9F9ABC, 0xE94B7D8C, 0xEC7AEC3A, 0xDB851DFA, 0x63094366, 0xC464C3D2, 0xEF1C1847, 0x3215D908,
+ 0xDD433B37, 0x24C2BA16, 0x12A14D43, 0x2A65C451, 0x50940002, 0x133AE4DD, 0x71DFF89E, 0x10314E55, 0x81AC77D6,
+ 0x5F11199B, 0x043556F1, 0xD7A3C76B, 0x3C11183B, 0x5924A509, 0xF28FE6ED, 0x97F1FBFA, 0x9EBABF2C, 0x1E153C6E,
+ 0x86E34570, 0xEAE96FB1, 0x860E5E0A, 0x5A3E2AB3, 0x771FE71C, 0x4E3D06FA, 0x2965DCB9, 0x99E71D0F, 0x803E89D6,
+ 0x5266C825, 0x2E4CC978, 0x9C10B36A, 0xC6150EBA, 0x94E2EA78, 0xA5FC3C53, 0x1E0A2DF4, 0xF2F74EA7, 0x361D2B3D,
+ 0x1939260F, 0x19C27960, 0x5223A708, 0xF71312B6, 0xEBADFE6E, 0xEAC31F66, 0xE3BC4595, 0xA67BC883, 0xB17F37D1,
+ 0x018CFF28, 0xC332DDEF, 0xBE6C5AA5, 0x65582185, 0x68AB9802, 0xEECEA50F, 0xDB2F953B, 0x2AEF7DAD, 0x5B6E2F84,
+ 0x1521B628, 0x29076170, 0xECDD4775, 0x619F1510, 0x13CCA830, 0xEB61BD96, 0x0334FE1E, 0xAA0363CF, 0xB5735C90,
+ 0x4C70A239, 0xD59E9E0B, 0xCBAADE14, 0xEECC86BC, 0x60622CA7, 0x9CAB5CAB, 0xB2F3846E, 0x648B1EAF, 0x19BDF0CA,
+ 0xA02369B9, 0x655ABB50, 0x40685A32, 0x3C2AB4B3, 0x319EE9D5, 0xC021B8F7, 0x9B540B19, 0x875FA099, 0x95F7997E,
+ 0x623D7DA8, 0xF837889A, 0x97E32D77, 0x11ED935F, 0x16681281, 0x0E358829, 0xC7E61FD6, 0x96DEDFA1, 0x7858BA99,
+ 0x57F584A5, 0x1B227263, 0x9B83C3FF, 0x1AC24696, 0xCDB30AEB, 0x532E3054, 0x8FD948E4, 0x6DBC3128, 0x58EBF2EF,
+ 0x34C6FFEA, 0xFE28ED61, 0xEE7C3C73, 0x5D4A14D9, 0xE864B7E3, 0x42105D14, 0x203E13E0, 0x45EEE2B6, 0xA3AAABEA,
+ 0xDB6C4F15, 0xFACB4FD0, 0xC742F442, 0xEF6ABBB5, 0x654F3B1D, 0x41CD2105, 0xD81E799E, 0x86854DC7, 0xE44B476A,
+ 0x3D816250, 0xCF62A1F2, 0x5B8D2646, 0xFC8883A0, 0xC1C7B6A3, 0x7F1524C3, 0x69CB7492, 0x47848A0B, 0x5692B285,
+ 0x095BBF00, 0xAD19489D, 0x1462B174, 0x23820E00, 0x58428D2A, 0x0C55F5EA, 0x1DADF43E, 0x233F7061, 0x3372F092,
+ 0x8D937E41, 0xD65FECF1, 0x6C223BDB, 0x7CDE3759, 0xCBEE7460, 0x4085F2A7, 0xCE77326E, 0xA6078084, 0x19F8509E,
+ 0xE8EFD855, 0x61D99735, 0xA969A7AA, 0xC50C06C2, 0x5A04ABFC, 0x800BCADC, 0x9E447A2E, 0xC3453484, 0xFDD56705,
+ 0x0E1E9EC9, 0xDB73DBD3, 0x105588CD, 0x675FDA79, 0xE3674340, 0xC5C43465, 0x713E38D8, 0x3D28F89E, 0xF16DFF20,
+ 0x153E21E7, 0x8FB03D4A, 0xE6E39F2B, 0xDB83ADF7 },
+
+ KS2 = { 0xE93D5A68, 0x948140F7, 0xF64C261C, 0x94692934, 0x411520F7, 0x7602D4F7, 0xBCF46B2E, 0xD4A20068, 0xD4082471,
+ 0x3320F46A, 0x43B7D4B7, 0x500061AF, 0x1E39F62E, 0x97244546, 0x14214F74, 0xBF8B8840, 0x4D95FC1D, 0x96B591AF,
+ 0x70F4DDD3, 0x66A02F45, 0xBFBC09EC, 0x03BD9785, 0x7FAC6DD0, 0x31CB8504, 0x96EB27B3, 0x55FD3941, 0xDA2547E6,
+ 0xABCA0A9A, 0x28507825, 0x530429F4, 0x0A2C86DA, 0xE9B66DFB, 0x68DC1462, 0xD7486900, 0x680EC0A4, 0x27A18DEE,
+ 0x4F3FFEA2, 0xE887AD8C, 0xB58CE006, 0x7AF4D6B6, 0xAACE1E7C, 0xD3375FEC, 0xCE78A399, 0x406B2A42, 0x20FE9E35,
+ 0xD9F385B9, 0xEE39D7AB, 0x3B124E8B, 0x1DC9FAF7, 0x4B6D1856, 0x26A36631, 0xEAE397B2, 0x3A6EFA74, 0xDD5B4332,
+ 0x6841E7F7, 0xCA7820FB, 0xFB0AF54E, 0xD8FEB397, 0x454056AC, 0xBA489527, 0x55533A3A, 0x20838D87, 0xFE6BA9B7,
+ 0xD096954B, 0x55A867BC, 0xA1159A58, 0xCCA92963, 0x99E1DB33, 0xA62A4A56, 0x3F3125F9, 0x5EF47E1C, 0x9029317C,
+ 0xFDF8E802, 0x04272F70, 0x80BB155C, 0x05282CE3, 0x95C11548, 0xE4C66D22, 0x48C1133F, 0xC70F86DC, 0x07F9C9EE,
+ 0x41041F0F, 0x404779A4, 0x5D886E17, 0x325F51EB, 0xD59BC0D1, 0xF2BCC18F, 0x41113564, 0x257B7834, 0x602A9C60,
+ 0xDFF8E8A3, 0x1F636C1B, 0x0E12B4C2, 0x02E1329E, 0xAF664FD1, 0xCAD18115, 0x6B2395E0, 0x333E92E1, 0x3B240B62,
+ 0xEEBEB922, 0x85B2A20E, 0xE6BA0D99, 0xDE720C8C, 0x2DA2F728, 0xD0127845, 0x95B794FD, 0x647D0862, 0xE7CCF5F0,
+ 0x5449A36F, 0x877D48FA, 0xC39DFD27, 0xF33E8D1E, 0x0A476341, 0x992EFF74, 0x3A6F6EAB, 0xF4F8FD37, 0xA812DC60,
+ 0xA1EBDDF8, 0x991BE14C, 0xDB6E6B0D, 0xC67B5510, 0x6D672C37, 0x2765D43B, 0xDCD0E804, 0xF1290DC7, 0xCC00FFA3,
+ 0xB5390F92, 0x690FED0B, 0x667B9FFB, 0xCEDB7D9C, 0xA091CF0B, 0xD9155EA3, 0xBB132F88, 0x515BAD24, 0x7B9479BF,
+ 0x763BD6EB, 0x37392EB3, 0xCC115979, 0x8026E297, 0xF42E312D, 0x6842ADA7, 0xC66A2B3B, 0x12754CCC, 0x782EF11C,
+ 0x6A124237, 0xB79251E7, 0x06A1BBE6, 0x4BFB6350, 0x1A6B1018, 0x11CAEDFA, 0x3D25BDD8, 0xE2E1C3C9, 0x44421659,
+ 0x0A121386, 0xD90CEC6E, 0xD5ABEA2A, 0x64AF674E, 0xDA86A85F, 0xBEBFE988, 0x64E4C3FE, 0x9DBC8057, 0xF0F7C086,
+ 0x60787BF8, 0x6003604D, 0xD1FD8346, 0xF6381FB0, 0x7745AE04, 0xD736FCCC, 0x83426B33, 0xF01EAB71, 0xB0804187,
+ 0x3C005E5F, 0x77A057BE, 0xBDE8AE24, 0x55464299, 0xBF582E61, 0x4E58F48F, 0xF2DDFDA2, 0xF474EF38, 0x8789BDC2,
+ 0x5366F9C3, 0xC8B38E74, 0xB475F255, 0x46FCD9B9, 0x7AEB2661, 0x8B1DDF84, 0x846A0E79, 0x915F95E2, 0x466E598E,
+ 0x20B45770, 0x8CD55591, 0xC902DE4C, 0xB90BACE1, 0xBB8205D0, 0x11A86248, 0x7574A99E, 0xB77F19B6, 0xE0A9DC09,
+ 0x662D09A1, 0xC4324633, 0xE85A1F02, 0x09F0BE8C, 0x4A99A025, 0x1D6EFE10, 0x1AB93D1D, 0x0BA5A4DF, 0xA186F20F,
+ 0x2868F169, 0xDCB7DA83, 0x573906FE, 0xA1E2CE9B, 0x4FCD7F52, 0x50115E01, 0xA70683FA, 0xA002B5C4, 0x0DE6D027,
+ 0x9AF88C27, 0x773F8641, 0xC3604C06, 0x61A806B5, 0xF0177A28, 0xC0F586E0, 0x006058AA, 0x30DC7D62, 0x11E69ED7,
+ 0x2338EA63, 0x53C2DD94, 0xC2C21634, 0xBBCBEE56, 0x90BCB6DE, 0xEBFC7DA1, 0xCE591D76, 0x6F05E409, 0x4B7C0188,
+ 0x39720A3D, 0x7C927C24, 0x86E3725F, 0x724D9DB9, 0x1AC15BB4, 0xD39EB8FC, 0xED545578, 0x08FCA5B5, 0xD83D7CD3,
+ 0x4DAD0FC4, 0x1E50EF5E, 0xB161E6F8, 0xA28514D9, 0x6C51133C, 0x6FD5C7E7, 0x56E14EC4, 0x362ABFCE, 0xDDC6C837,
+ 0xD79A3234, 0x92638212, 0x670EFA8E, 0x406000E0 },
+
+ KS3 = { 0x3A39CE37, 0xD3FAF5CF, 0xABC27737, 0x5AC52D1B, 0x5CB0679E, 0x4FA33742, 0xD3822740, 0x99BC9BBE, 0xD5118E9D,
+ 0xBF0F7315, 0xD62D1C7E, 0xC700C47B, 0xB78C1B6B, 0x21A19045, 0xB26EB1BE, 0x6A366EB4, 0x5748AB2F, 0xBC946E79,
+ 0xC6A376D2, 0x6549C2C8, 0x530FF8EE, 0x468DDE7D, 0xD5730A1D, 0x4CD04DC6, 0x2939BBDB, 0xA9BA4650, 0xAC9526E8,
+ 0xBE5EE304, 0xA1FAD5F0, 0x6A2D519A, 0x63EF8CE2, 0x9A86EE22, 0xC089C2B8, 0x43242EF6, 0xA51E03AA, 0x9CF2D0A4,
+ 0x83C061BA, 0x9BE96A4D, 0x8FE51550, 0xBA645BD6, 0x2826A2F9, 0xA73A3AE1, 0x4BA99586, 0xEF5562E9, 0xC72FEFD3,
+ 0xF752F7DA, 0x3F046F69, 0x77FA0A59, 0x80E4A915, 0x87B08601, 0x9B09E6AD, 0x3B3EE593, 0xE990FD5A, 0x9E34D797,
+ 0x2CF0B7D9, 0x022B8B51, 0x96D5AC3A, 0x017DA67D, 0xD1CF3ED6, 0x7C7D2D28, 0x1F9F25CF, 0xADF2B89B, 0x5AD6B472,
+ 0x5A88F54C, 0xE029AC71, 0xE019A5E6, 0x47B0ACFD, 0xED93FA9B, 0xE8D3C48D, 0x283B57CC, 0xF8D56629, 0x79132E28,
+ 0x785F0191, 0xED756055, 0xF7960E44, 0xE3D35E8C, 0x15056DD4, 0x88F46DBA, 0x03A16125, 0x0564F0BD, 0xC3EB9E15,
+ 0x3C9057A2, 0x97271AEC, 0xA93A072A, 0x1B3F6D9B, 0x1E6321F5, 0xF59C66FB, 0x26DCF319, 0x7533D928, 0xB155FDF5,
+ 0x03563482, 0x8ABA3CBB, 0x28517711, 0xC20AD9F8, 0xABCC5167, 0xCCAD925F, 0x4DE81751, 0x3830DC8E, 0x379D5862,
+ 0x9320F991, 0xEA7A90C2, 0xFB3E7BCE, 0x5121CE64, 0x774FBE32, 0xA8B6E37E, 0xC3293D46, 0x48DE5369, 0x6413E680,
+ 0xA2AE0810, 0xDD6DB224, 0x69852DFD, 0x09072166, 0xB39A460A, 0x6445C0DD, 0x586CDECF, 0x1C20C8AE, 0x5BBEF7DD,
+ 0x1B588D40, 0xCCD2017F, 0x6BB4E3BB, 0xDDA26A7E, 0x3A59FF45, 0x3E350A44, 0xBCB4CDD5, 0x72EACEA8, 0xFA6484BB,
+ 0x8D6612AE, 0xBF3C6F47, 0xD29BE463, 0x542F5D9E, 0xAEC2771B, 0xF64E6370, 0x740E0D8D, 0xE75B1357, 0xF8721671,
+ 0xAF537D5D, 0x4040CB08, 0x4EB4E2CC, 0x34D2466A, 0x0115AF84, 0xE1B00428, 0x95983A1D, 0x06B89FB4, 0xCE6EA048,
+ 0x6F3F3B82, 0x3520AB82, 0x011A1D4B, 0x277227F8, 0x611560B1, 0xE7933FDC, 0xBB3A792B, 0x344525BD, 0xA08839E1,
+ 0x51CE794B, 0x2F32C9B7, 0xA01FBAC9, 0xE01CC87E, 0xBCC7D1F6, 0xCF0111C3, 0xA1E8AAC7, 0x1A908749, 0xD44FBD9A,
+ 0xD0DADECB, 0xD50ADA38, 0x0339C32A, 0xC6913667, 0x8DF9317C, 0xE0B12B4F, 0xF79E59B7, 0x43F5BB3A, 0xF2D519FF,
+ 0x27D9459C, 0xBF97222C, 0x15E6FC2A, 0x0F91FC71, 0x9B941525, 0xFAE59361, 0xCEB69CEB, 0xC2A86459, 0x12BAA8D1,
+ 0xB6C1075E, 0xE3056A0C, 0x10D25065, 0xCB03A442, 0xE0EC6E0E, 0x1698DB3B, 0x4C98A0BE, 0x3278E964, 0x9F1F9532,
+ 0xE0D392DF, 0xD3A0342B, 0x8971F21E, 0x1B0A7441, 0x4BA3348C, 0xC5BE7120, 0xC37632D8, 0xDF359F8D, 0x9B992F2E,
+ 0xE60B6F47, 0x0FE3F11D, 0xE54CDA54, 0x1EDAD891, 0xCE6279CF, 0xCD3E7E6F, 0x1618B166, 0xFD2C1D05, 0x848FD2C5,
+ 0xF6FB2299, 0xF523F357, 0xA6327623, 0x93A83531, 0x56CCCD02, 0xACF08162, 0x5A75EBB5, 0x6E163697, 0x88D273CC,
+ 0xDE966292, 0x81B949D0, 0x4C50901B, 0x71C65614, 0xE6C6C7BD, 0x327A140A, 0x45E1D006, 0xC3F27B9A, 0xC9AA53FD,
+ 0x62A80F00, 0xBB25BFE2, 0x35BDD2F6, 0x71126905, 0xB2040222, 0xB6CBCF7C, 0xCD769C2B, 0x53113EC0, 0x1640E3D3,
+ 0x38ABBD60, 0x2547ADF0, 0xBA38209C, 0xF746CE76, 0x77AFA1C5, 0x20756060, 0x85CBFE4E, 0x8AE88DD8, 0x7AAAF9B0,
+ 0x4CF9AA7E, 0x1948C25C, 0x02FB8A8C, 0x01C36AE4, 0xD6EBE1F9, 0x90D4F869, 0xA65CDEA0, 0x3F09252D, 0xC208E69F,
+ 0xB74E6132, 0xCE77E25B, 0x578FDFE3, 0x3AC372E6 };
+
+ // ====================================
+ // Useful constants
+ // ====================================
+
+ private static final int ROUNDS = 16;
+ private static final int BLOCK_SIZE = 8; // bytes = 64 bits
+ private static final int SBOX_SK = 256;
+ private static final int P_SZ = ROUNDS + 2;
+
+ private final int[] S0, S1, S2, S3; // the s-boxes
+ private final int[] P; // the p-array
+
+ private boolean doEncrypt = false;
+
+ private byte[] workingKey = null;
+
+ public BlowFish()
+ {
+ S0 = new int[SBOX_SK];
+ S1 = new int[SBOX_SK];
+ S2 = new int[SBOX_SK];
+ S3 = new int[SBOX_SK];
+ P = new int[P_SZ];
+ }
+
+ /**
+ * initialise a Blowfish cipher.
+ *
+ * @param encrypting
+ * whether or not we are for encryption.
+ * @param key
+ * the key required to set up the cipher.
+ * @exception IllegalArgumentException
+ * if the params argument is inappropriate.
+ */
+ public void init(boolean encrypting, byte[] key)
+ {
+ this.doEncrypt = encrypting;
+ this.workingKey = key;
+ setKey(this.workingKey);
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Blowfish";
+ }
+
+ public final void transformBlock(byte[] in, int inOff, byte[] out, int outOff)
+ {
+ if (workingKey == null)
+ {
+ throw new IllegalStateException("Blowfish not initialised");
+ }
+
+ if (doEncrypt)
+ {
+ encryptBlock(in, inOff, out, outOff);
+ }
+ else
+ {
+ decryptBlock(in, inOff, out, outOff);
+ }
+ }
+
+ public void reset()
+ {
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ // ==================================
+ // Private Implementation
+ // ==================================
+
+ private int F(int x)
+ {
+ return (((S0[(x >>> 24)] + S1[(x >>> 16) & 0xff]) ^ S2[(x >>> 8) & 0xff]) + S3[x & 0xff]);
+ }
+
+ /**
+ * apply the encryption cycle to each value pair in the table.
+ */
+ private void processTable(int xl, int xr, int[] table)
+ {
+ int size = table.length;
+
+ for (int s = 0; s < size; s += 2)
+ {
+ xl ^= P[0];
+
+ for (int i = 1; i < ROUNDS; i += 2)
+ {
+ xr ^= F(xl) ^ P[i];
+ xl ^= F(xr) ^ P[i + 1];
+ }
+
+ xr ^= P[ROUNDS + 1];
+
+ table[s] = xr;
+ table[s + 1] = xl;
+
+ xr = xl; // end of cycle swap
+ xl = table[s];
+ }
+ }
+
+ private void setKey(byte[] key)
+ {
+ /*
+ * - comments are from _Applied Crypto_, Schneier, p338 please be
+ * careful comparing the two, AC numbers the arrays from 1, the enclosed
+ * code from 0.
+ *
+ * (1) Initialise the S-boxes and the P-array, with a fixed string This
+ * string contains the hexadecimal digits of pi (3.141...)
+ */
+ System.arraycopy(KS0, 0, S0, 0, SBOX_SK);
+ System.arraycopy(KS1, 0, S1, 0, SBOX_SK);
+ System.arraycopy(KS2, 0, S2, 0, SBOX_SK);
+ System.arraycopy(KS3, 0, S3, 0, SBOX_SK);
+
+ System.arraycopy(KP, 0, P, 0, P_SZ);
+
+ /*
+ * (2) Now, XOR P[0] with the first 32 bits of the key, XOR P[1] with
+ * the second 32-bits of the key, and so on for all bits of the key (up
+ * to P[17]). Repeatedly cycle through the key bits until the entire
+ * P-array has been XOR-ed with the key bits
+ */
+ int keyLength = key.length;
+ int keyIndex = 0;
+
+ for (int i = 0; i < P_SZ; i++)
+ {
+ // get the 32 bits of the key, in 4 * 8 bit chunks
+ int data = 0x0000000;
+ for (int j = 0; j < 4; j++)
+ {
+ // create a 32 bit block
+ data = (data << 8) | (key[keyIndex++] & 0xff);
+
+ // wrap when we get to the end of the key
+ if (keyIndex >= keyLength)
+ {
+ keyIndex = 0;
+ }
+ }
+ // XOR the newly created 32 bit chunk onto the P-array
+ P[i] ^= data;
+ }
+
+ /*
+ * (3) Encrypt the all-zero string with the Blowfish algorithm, using
+ * the subkeys described in (1) and (2)
+ *
+ * (4) Replace P1 and P2 with the output of step (3)
+ *
+ * (5) Encrypt the output of step(3) using the Blowfish algorithm, with
+ * the modified subkeys.
+ *
+ * (6) Replace P3 and P4 with the output of step (5)
+ *
+ * (7) Continue the process, replacing all elements of the P-array and
+ * then all four S-boxes in order, with the output of the continuously
+ * changing Blowfish algorithm
+ */
+
+ processTable(0, 0, P);
+ processTable(P[P_SZ - 2], P[P_SZ - 1], S0);
+ processTable(S0[SBOX_SK - 2], S0[SBOX_SK - 1], S1);
+ processTable(S1[SBOX_SK - 2], S1[SBOX_SK - 1], S2);
+ processTable(S2[SBOX_SK - 2], S2[SBOX_SK - 1], S3);
+ }
+
+ /**
+ * Encrypt the given input starting at the given offset and place the result
+ * in the provided buffer starting at the given offset. The input will be an
+ * exact multiple of our blocksize.
+ */
+ private void encryptBlock(byte[] src, int srcIndex, byte[] dst, int dstIndex)
+ {
+ int xl = BytesTo32bits(src, srcIndex);
+ int xr = BytesTo32bits(src, srcIndex + 4);
+
+ xl ^= P[0];
+
+ for (int i = 1; i < ROUNDS; i += 2)
+ {
+ xr ^= F(xl) ^ P[i];
+ xl ^= F(xr) ^ P[i + 1];
+ }
+
+ xr ^= P[ROUNDS + 1];
+
+ Bits32ToBytes(xr, dst, dstIndex);
+ Bits32ToBytes(xl, dst, dstIndex + 4);
+ }
+
+ /**
+ * Decrypt the given input starting at the given offset and place the result
+ * in the provided buffer starting at the given offset. The input will be an
+ * exact multiple of our blocksize.
+ */
+ private void decryptBlock(byte[] src, int srcIndex, byte[] dst, int dstIndex)
+ {
+ int xl = BytesTo32bits(src, srcIndex);
+ int xr = BytesTo32bits(src, srcIndex + 4);
+
+ xl ^= P[ROUNDS + 1];
+
+ for (int i = ROUNDS; i > 0; i -= 2)
+ {
+ xr ^= F(xl) ^ P[i];
+ xl ^= F(xr) ^ P[i - 1];
+ }
+
+ xr ^= P[0];
+
+ Bits32ToBytes(xr, dst, dstIndex);
+ Bits32ToBytes(xl, dst, dstIndex + 4);
+ }
+
+ private int BytesTo32bits(byte[] b, int i)
+ {
+ return ((b[i] & 0xff) << 24) | ((b[i + 1] & 0xff) << 16) | ((b[i + 2] & 0xff) << 8) | ((b[i + 3] & 0xff));
+ }
+
+ private void Bits32ToBytes(int in, byte[] b, int offset)
+ {
+ b[offset + 3] = (byte) in;
+ b[offset + 2] = (byte) (in >> 8);
+ b[offset + 1] = (byte) (in >> 16);
+ b[offset] = (byte) (in >> 24);
+ }
+
+}
diff --git a/src/com/trilead/ssh2/crypto/cipher/CBCMode.java b/src/com/trilead/ssh2/crypto/cipher/CBCMode.java
index 60889f0..0ae51b3 100644
--- a/src/com/trilead/ssh2/crypto/cipher/CBCMode.java
+++ b/src/com/trilead/ssh2/crypto/cipher/CBCMode.java
@@ -1,78 +1,78 @@
-package com.trilead.ssh2.crypto.cipher;
-
-/**
- * CBCMode.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: CBCMode.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class CBCMode implements BlockCipher
-{
- BlockCipher tc;
- int blockSize;
- boolean doEncrypt;
-
- byte[] cbc_vector;
- byte[] tmp_vector;
-
- public void init(boolean forEncryption, byte[] key)
- {
- }
-
- public CBCMode(BlockCipher tc, byte[] iv, boolean doEncrypt)
- throws IllegalArgumentException
- {
- this.tc = tc;
- this.blockSize = tc.getBlockSize();
- this.doEncrypt = doEncrypt;
-
- if (this.blockSize != iv.length)
- throw new IllegalArgumentException("IV must be " + blockSize
- + " bytes long! (currently " + iv.length + ")");
-
- this.cbc_vector = new byte[blockSize];
- this.tmp_vector = new byte[blockSize];
- System.arraycopy(iv, 0, cbc_vector, 0, blockSize);
- }
-
- public int getBlockSize()
- {
- return blockSize;
- }
-
- private void encryptBlock(byte[] src, int srcoff, byte[] dst, int dstoff)
- {
- for (int i = 0; i < blockSize; i++)
- cbc_vector[i] ^= src[srcoff + i];
-
- tc.transformBlock(cbc_vector, 0, dst, dstoff);
-
- System.arraycopy(dst, dstoff, cbc_vector, 0, blockSize);
- }
-
- private void decryptBlock(byte[] src, int srcoff, byte[] dst, int dstoff)
- {
- /* Assume the worst, src and dst are overlapping... */
-
- System.arraycopy(src, srcoff, tmp_vector, 0, blockSize);
-
- tc.transformBlock(src, srcoff, dst, dstoff);
-
- for (int i = 0; i < blockSize; i++)
- dst[dstoff + i] ^= cbc_vector[i];
-
- /* ...that is why we need a tmp buffer. */
-
- byte[] swap = cbc_vector;
- cbc_vector = tmp_vector;
- tmp_vector = swap;
- }
-
- public void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff)
- {
- if (doEncrypt)
- encryptBlock(src, srcoff, dst, dstoff);
- else
- decryptBlock(src, srcoff, dst, dstoff);
- }
-}
+package com.trilead.ssh2.crypto.cipher;
+
+/**
+ * CBCMode.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: CBCMode.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class CBCMode implements BlockCipher
+{
+ BlockCipher tc;
+ int blockSize;
+ boolean doEncrypt;
+
+ byte[] cbc_vector;
+ byte[] tmp_vector;
+
+ public void init(boolean forEncryption, byte[] key)
+ {
+ }
+
+ public CBCMode(BlockCipher tc, byte[] iv, boolean doEncrypt)
+ throws IllegalArgumentException
+ {
+ this.tc = tc;
+ this.blockSize = tc.getBlockSize();
+ this.doEncrypt = doEncrypt;
+
+ if (this.blockSize != iv.length)
+ throw new IllegalArgumentException("IV must be " + blockSize
+ + " bytes long! (currently " + iv.length + ")");
+
+ this.cbc_vector = new byte[blockSize];
+ this.tmp_vector = new byte[blockSize];
+ System.arraycopy(iv, 0, cbc_vector, 0, blockSize);
+ }
+
+ public int getBlockSize()
+ {
+ return blockSize;
+ }
+
+ private void encryptBlock(byte[] src, int srcoff, byte[] dst, int dstoff)
+ {
+ for (int i = 0; i < blockSize; i++)
+ cbc_vector[i] ^= src[srcoff + i];
+
+ tc.transformBlock(cbc_vector, 0, dst, dstoff);
+
+ System.arraycopy(dst, dstoff, cbc_vector, 0, blockSize);
+ }
+
+ private void decryptBlock(byte[] src, int srcoff, byte[] dst, int dstoff)
+ {
+ /* Assume the worst, src and dst are overlapping... */
+
+ System.arraycopy(src, srcoff, tmp_vector, 0, blockSize);
+
+ tc.transformBlock(src, srcoff, dst, dstoff);
+
+ for (int i = 0; i < blockSize; i++)
+ dst[dstoff + i] ^= cbc_vector[i];
+
+ /* ...that is why we need a tmp buffer. */
+
+ byte[] swap = cbc_vector;
+ cbc_vector = tmp_vector;
+ tmp_vector = swap;
+ }
+
+ public void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff)
+ {
+ if (doEncrypt)
+ encryptBlock(src, srcoff, dst, dstoff);
+ else
+ decryptBlock(src, srcoff, dst, dstoff);
+ }
+}
diff --git a/src/com/trilead/ssh2/crypto/cipher/CTRMode.java b/src/com/trilead/ssh2/crypto/cipher/CTRMode.java
index d456095..8541c8d 100644
--- a/src/com/trilead/ssh2/crypto/cipher/CTRMode.java
+++ b/src/com/trilead/ssh2/crypto/cipher/CTRMode.java
@@ -1,62 +1,62 @@
-
-package com.trilead.ssh2.crypto.cipher;
-
-/**
- * This is CTR mode as described in draft-ietf-secsh-newmodes-XY.txt
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: CTRMode.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class CTRMode implements BlockCipher
-{
- byte[] X;
- byte[] Xenc;
-
- BlockCipher bc;
- int blockSize;
- boolean doEncrypt;
-
- int count = 0;
-
- public void init(boolean forEncryption, byte[] key)
- {
- }
-
- public CTRMode(BlockCipher tc, byte[] iv, boolean doEnc) throws IllegalArgumentException
- {
- bc = tc;
- blockSize = bc.getBlockSize();
- doEncrypt = doEnc;
-
- if (blockSize != iv.length)
- throw new IllegalArgumentException("IV must be " + blockSize + " bytes long! (currently " + iv.length + ")");
-
- X = new byte[blockSize];
- Xenc = new byte[blockSize];
-
- System.arraycopy(iv, 0, X, 0, blockSize);
- }
-
- public final int getBlockSize()
- {
- return blockSize;
- }
-
- public final void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff)
- {
- bc.transformBlock(X, 0, Xenc, 0);
-
- for (int i = 0; i < blockSize; i++)
- {
- dst[dstoff + i] = (byte) (src[srcoff + i] ^ Xenc[i]);
- }
-
- for (int i = (blockSize - 1); i >= 0; i--)
- {
- X[i]++;
- if (X[i] != 0)
- break;
-
- }
- }
-}
+
+package com.trilead.ssh2.crypto.cipher;
+
+/**
+ * This is CTR mode as described in draft-ietf-secsh-newmodes-XY.txt
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: CTRMode.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class CTRMode implements BlockCipher
+{
+ byte[] X;
+ byte[] Xenc;
+
+ BlockCipher bc;
+ int blockSize;
+ boolean doEncrypt;
+
+ int count = 0;
+
+ public void init(boolean forEncryption, byte[] key)
+ {
+ }
+
+ public CTRMode(BlockCipher tc, byte[] iv, boolean doEnc) throws IllegalArgumentException
+ {
+ bc = tc;
+ blockSize = bc.getBlockSize();
+ doEncrypt = doEnc;
+
+ if (blockSize != iv.length)
+ throw new IllegalArgumentException("IV must be " + blockSize + " bytes long! (currently " + iv.length + ")");
+
+ X = new byte[blockSize];
+ Xenc = new byte[blockSize];
+
+ System.arraycopy(iv, 0, X, 0, blockSize);
+ }
+
+ public final int getBlockSize()
+ {
+ return blockSize;
+ }
+
+ public final void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff)
+ {
+ bc.transformBlock(X, 0, Xenc, 0);
+
+ for (int i = 0; i < blockSize; i++)
+ {
+ dst[dstoff + i] = (byte) (src[srcoff + i] ^ Xenc[i]);
+ }
+
+ for (int i = (blockSize - 1); i >= 0; i--)
+ {
+ X[i]++;
+ if (X[i] != 0)
+ break;
+
+ }
+ }
+}
diff --git a/src/com/trilead/ssh2/crypto/cipher/CipherInputStream.java b/src/com/trilead/ssh2/crypto/cipher/CipherInputStream.java
index 34af1e7..c9055ab 100644
--- a/src/com/trilead/ssh2/crypto/cipher/CipherInputStream.java
+++ b/src/com/trilead/ssh2/crypto/cipher/CipherInputStream.java
@@ -1,144 +1,144 @@
-
-package com.trilead.ssh2.crypto.cipher;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * CipherInputStream.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: CipherInputStream.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class CipherInputStream
-{
- BlockCipher currentCipher;
- InputStream bi;
- byte[] buffer;
- byte[] enc;
- int blockSize;
- int pos;
-
- /*
- * We cannot use java.io.BufferedInputStream, since that is not available in
- * J2ME. Everything could be improved alot here.
- */
-
- final int BUFF_SIZE = 2048;
- byte[] input_buffer = new byte[BUFF_SIZE];
- int input_buffer_pos = 0;
- int input_buffer_size = 0;
-
- public CipherInputStream(BlockCipher tc, InputStream bi)
- {
- this.bi = bi;
- changeCipher(tc);
- }
-
- private int fill_buffer() throws IOException
- {
- input_buffer_pos = 0;
- input_buffer_size = bi.read(input_buffer, 0, BUFF_SIZE);
- return input_buffer_size;
- }
-
- private int internal_read(byte[] b, int off, int len) throws IOException
- {
- if (input_buffer_size < 0)
- return -1;
-
- if (input_buffer_pos >= input_buffer_size)
- {
- if (fill_buffer() <= 0)
- return -1;
- }
-
- int avail = input_buffer_size - input_buffer_pos;
- int thiscopy = (len > avail) ? avail : len;
-
- System.arraycopy(input_buffer, input_buffer_pos, b, off, thiscopy);
- input_buffer_pos += thiscopy;
-
- return thiscopy;
- }
-
- public void changeCipher(BlockCipher bc)
- {
- this.currentCipher = bc;
- blockSize = bc.getBlockSize();
- buffer = new byte[blockSize];
- enc = new byte[blockSize];
- pos = blockSize;
- }
-
- private void getBlock() throws IOException
- {
- int n = 0;
- while (n < blockSize)
- {
- int len = internal_read(enc, n, blockSize - n);
- if (len < 0)
- throw new IOException("Cannot read full block, EOF reached.");
- n += len;
- }
-
- try
- {
- currentCipher.transformBlock(enc, 0, buffer, 0);
- }
- catch (Exception e)
- {
- throw new IOException("Error while decrypting block.");
- }
- pos = 0;
- }
-
- public int read(byte[] dst) throws IOException
- {
- return read(dst, 0, dst.length);
- }
-
- public int read(byte[] dst, int off, int len) throws IOException
- {
- int count = 0;
-
- while (len > 0)
- {
- if (pos >= blockSize)
- getBlock();
-
- int avail = blockSize - pos;
- int copy = Math.min(avail, len);
- System.arraycopy(buffer, pos, dst, off, copy);
- pos += copy;
- off += copy;
- len -= copy;
- count += copy;
- }
- return count;
- }
-
- public int read() throws IOException
- {
- if (pos >= blockSize)
- {
- getBlock();
- }
- return buffer[pos++] & 0xff;
- }
-
- public int readPlain(byte[] b, int off, int len) throws IOException
- {
- if (pos != blockSize)
- throw new IOException("Cannot read plain since crypto buffer is not aligned.");
- int n = 0;
- while (n < len)
- {
- int cnt = internal_read(b, off + n, len - n);
- if (cnt < 0)
- throw new IOException("Cannot fill buffer, EOF reached.");
- n += cnt;
- }
- return n;
- }
-}
+
+package com.trilead.ssh2.crypto.cipher;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * CipherInputStream.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: CipherInputStream.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class CipherInputStream
+{
+ BlockCipher currentCipher;
+ InputStream bi;
+ byte[] buffer;
+ byte[] enc;
+ int blockSize;
+ int pos;
+
+ /*
+ * We cannot use java.io.BufferedInputStream, since that is not available in
+ * J2ME. Everything could be improved alot here.
+ */
+
+ final int BUFF_SIZE = 2048;
+ byte[] input_buffer = new byte[BUFF_SIZE];
+ int input_buffer_pos = 0;
+ int input_buffer_size = 0;
+
+ public CipherInputStream(BlockCipher tc, InputStream bi)
+ {
+ this.bi = bi;
+ changeCipher(tc);
+ }
+
+ private int fill_buffer() throws IOException
+ {
+ input_buffer_pos = 0;
+ input_buffer_size = bi.read(input_buffer, 0, BUFF_SIZE);
+ return input_buffer_size;
+ }
+
+ private int internal_read(byte[] b, int off, int len) throws IOException
+ {
+ if (input_buffer_size < 0)
+ return -1;
+
+ if (input_buffer_pos >= input_buffer_size)
+ {
+ if (fill_buffer() <= 0)
+ return -1;
+ }
+
+ int avail = input_buffer_size - input_buffer_pos;
+ int thiscopy = (len > avail) ? avail : len;
+
+ System.arraycopy(input_buffer, input_buffer_pos, b, off, thiscopy);
+ input_buffer_pos += thiscopy;
+
+ return thiscopy;
+ }
+
+ public void changeCipher(BlockCipher bc)
+ {
+ this.currentCipher = bc;
+ blockSize = bc.getBlockSize();
+ buffer = new byte[blockSize];
+ enc = new byte[blockSize];
+ pos = blockSize;
+ }
+
+ private void getBlock() throws IOException
+ {
+ int n = 0;
+ while (n < blockSize)
+ {
+ int len = internal_read(enc, n, blockSize - n);
+ if (len < 0)
+ throw new IOException("Cannot read full block, EOF reached.");
+ n += len;
+ }
+
+ try
+ {
+ currentCipher.transformBlock(enc, 0, buffer, 0);
+ }
+ catch (Exception e)
+ {
+ throw new IOException("Error while decrypting block.");
+ }
+ pos = 0;
+ }
+
+ public int read(byte[] dst) throws IOException
+ {
+ return read(dst, 0, dst.length);
+ }
+
+ public int read(byte[] dst, int off, int len) throws IOException
+ {
+ int count = 0;
+
+ while (len > 0)
+ {
+ if (pos >= blockSize)
+ getBlock();
+
+ int avail = blockSize - pos;
+ int copy = Math.min(avail, len);
+ System.arraycopy(buffer, pos, dst, off, copy);
+ pos += copy;
+ off += copy;
+ len -= copy;
+ count += copy;
+ }
+ return count;
+ }
+
+ public int read() throws IOException
+ {
+ if (pos >= blockSize)
+ {
+ getBlock();
+ }
+ return buffer[pos++] & 0xff;
+ }
+
+ public int readPlain(byte[] b, int off, int len) throws IOException
+ {
+ if (pos != blockSize)
+ throw new IOException("Cannot read plain since crypto buffer is not aligned.");
+ int n = 0;
+ while (n < len)
+ {
+ int cnt = internal_read(b, off + n, len - n);
+ if (cnt < 0)
+ throw new IOException("Cannot fill buffer, EOF reached.");
+ n += cnt;
+ }
+ return n;
+ }
+}
diff --git a/src/com/trilead/ssh2/crypto/cipher/CipherOutputStream.java b/src/com/trilead/ssh2/crypto/cipher/CipherOutputStream.java
index 9db88c2..cf0db4a 100644
--- a/src/com/trilead/ssh2/crypto/cipher/CipherOutputStream.java
+++ b/src/com/trilead/ssh2/crypto/cipher/CipherOutputStream.java
@@ -1,142 +1,142 @@
-
-package com.trilead.ssh2.crypto.cipher;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * CipherOutputStream.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: CipherOutputStream.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class CipherOutputStream
-{
- BlockCipher currentCipher;
- OutputStream bo;
- byte[] buffer;
- byte[] enc;
- int blockSize;
- int pos;
-
- /*
- * We cannot use java.io.BufferedOutputStream, since that is not available
- * in J2ME. Everything could be improved here alot.
- */
-
- final int BUFF_SIZE = 2048;
- byte[] out_buffer = new byte[BUFF_SIZE];
- int out_buffer_pos = 0;
-
- public CipherOutputStream(BlockCipher tc, OutputStream bo)
- {
- this.bo = bo;
- changeCipher(tc);
- }
-
- private void internal_write(byte[] src, int off, int len) throws IOException
- {
- while (len > 0)
- {
- int space = BUFF_SIZE - out_buffer_pos;
- int copy = (len > space) ? space : len;
-
- System.arraycopy(src, off, out_buffer, out_buffer_pos, copy);
-
- off += copy;
- out_buffer_pos += copy;
- len -= copy;
-
- if (out_buffer_pos >= BUFF_SIZE)
- {
- bo.write(out_buffer, 0, BUFF_SIZE);
- out_buffer_pos = 0;
- }
- }
- }
-
- private void internal_write(int b) throws IOException
- {
- out_buffer[out_buffer_pos++] = (byte) b;
- if (out_buffer_pos >= BUFF_SIZE)
- {
- bo.write(out_buffer, 0, BUFF_SIZE);
- out_buffer_pos = 0;
- }
- }
-
- public void flush() throws IOException
- {
- if (pos != 0)
- throw new IOException("FATAL: cannot flush since crypto buffer is not aligned.");
-
- if (out_buffer_pos > 0)
- {
- bo.write(out_buffer, 0, out_buffer_pos);
- out_buffer_pos = 0;
- }
- bo.flush();
- }
-
- public void changeCipher(BlockCipher bc)
- {
- this.currentCipher = bc;
- blockSize = bc.getBlockSize();
- buffer = new byte[blockSize];
- enc = new byte[blockSize];
- pos = 0;
- }
-
- private void writeBlock() throws IOException
- {
- try
- {
- currentCipher.transformBlock(buffer, 0, enc, 0);
- }
- catch (Exception e)
- {
- throw (IOException) new IOException("Error while decrypting block.").initCause(e);
- }
-
- internal_write(enc, 0, blockSize);
- pos = 0;
- }
-
- public void write(byte[] src, int off, int len) throws IOException
- {
- while (len > 0)
- {
- int avail = blockSize - pos;
- int copy = Math.min(avail, len);
-
- System.arraycopy(src, off, buffer, pos, copy);
- pos += copy;
- off += copy;
- len -= copy;
-
- if (pos >= blockSize)
- writeBlock();
- }
- }
-
- public void write(int b) throws IOException
- {
- buffer[pos++] = (byte) b;
- if (pos >= blockSize)
- writeBlock();
- }
-
- public void writePlain(int b) throws IOException
- {
- if (pos != 0)
- throw new IOException("Cannot write plain since crypto buffer is not aligned.");
- internal_write(b);
- }
-
- public void writePlain(byte[] b, int off, int len) throws IOException
- {
- if (pos != 0)
- throw new IOException("Cannot write plain since crypto buffer is not aligned.");
- internal_write(b, off, len);
- }
-}
+
+package com.trilead.ssh2.crypto.cipher;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * CipherOutputStream.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: CipherOutputStream.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class CipherOutputStream
+{
+ BlockCipher currentCipher;
+ OutputStream bo;
+ byte[] buffer;
+ byte[] enc;
+ int blockSize;
+ int pos;
+
+ /*
+ * We cannot use java.io.BufferedOutputStream, since that is not available
+ * in J2ME. Everything could be improved here alot.
+ */
+
+ final int BUFF_SIZE = 2048;
+ byte[] out_buffer = new byte[BUFF_SIZE];
+ int out_buffer_pos = 0;
+
+ public CipherOutputStream(BlockCipher tc, OutputStream bo)
+ {
+ this.bo = bo;
+ changeCipher(tc);
+ }
+
+ private void internal_write(byte[] src, int off, int len) throws IOException
+ {
+ while (len > 0)
+ {
+ int space = BUFF_SIZE - out_buffer_pos;
+ int copy = (len > space) ? space : len;
+
+ System.arraycopy(src, off, out_buffer, out_buffer_pos, copy);
+
+ off += copy;
+ out_buffer_pos += copy;
+ len -= copy;
+
+ if (out_buffer_pos >= BUFF_SIZE)
+ {
+ bo.write(out_buffer, 0, BUFF_SIZE);
+ out_buffer_pos = 0;
+ }
+ }
+ }
+
+ private void internal_write(int b) throws IOException
+ {
+ out_buffer[out_buffer_pos++] = (byte) b;
+ if (out_buffer_pos >= BUFF_SIZE)
+ {
+ bo.write(out_buffer, 0, BUFF_SIZE);
+ out_buffer_pos = 0;
+ }
+ }
+
+ public void flush() throws IOException
+ {
+ if (pos != 0)
+ throw new IOException("FATAL: cannot flush since crypto buffer is not aligned.");
+
+ if (out_buffer_pos > 0)
+ {
+ bo.write(out_buffer, 0, out_buffer_pos);
+ out_buffer_pos = 0;
+ }
+ bo.flush();
+ }
+
+ public void changeCipher(BlockCipher bc)
+ {
+ this.currentCipher = bc;
+ blockSize = bc.getBlockSize();
+ buffer = new byte[blockSize];
+ enc = new byte[blockSize];
+ pos = 0;
+ }
+
+ private void writeBlock() throws IOException
+ {
+ try
+ {
+ currentCipher.transformBlock(buffer, 0, enc, 0);
+ }
+ catch (Exception e)
+ {
+ throw (IOException) new IOException("Error while decrypting block.").initCause(e);
+ }
+
+ internal_write(enc, 0, blockSize);
+ pos = 0;
+ }
+
+ public void write(byte[] src, int off, int len) throws IOException
+ {
+ while (len > 0)
+ {
+ int avail = blockSize - pos;
+ int copy = Math.min(avail, len);
+
+ System.arraycopy(src, off, buffer, pos, copy);
+ pos += copy;
+ off += copy;
+ len -= copy;
+
+ if (pos >= blockSize)
+ writeBlock();
+ }
+ }
+
+ public void write(int b) throws IOException
+ {
+ buffer[pos++] = (byte) b;
+ if (pos >= blockSize)
+ writeBlock();
+ }
+
+ public void writePlain(int b) throws IOException
+ {
+ if (pos != 0)
+ throw new IOException("Cannot write plain since crypto buffer is not aligned.");
+ internal_write(b);
+ }
+
+ public void writePlain(byte[] b, int off, int len) throws IOException
+ {
+ if (pos != 0)
+ throw new IOException("Cannot write plain since crypto buffer is not aligned.");
+ internal_write(b, off, len);
+ }
+}
diff --git a/src/com/trilead/ssh2/crypto/cipher/DES.java b/src/com/trilead/ssh2/crypto/cipher/DES.java
index 20480d3..6588459 100644
--- a/src/com/trilead/ssh2/crypto/cipher/DES.java
+++ b/src/com/trilead/ssh2/crypto/cipher/DES.java
@@ -1,373 +1,373 @@
-
-package com.trilead.ssh2.crypto.cipher;
-
-/*
- This file is based on the 3DES implementation from the Bouncy Castle Crypto package.
- Their licence file states the following:
-
- Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle
- (http://www.bouncycastle.org)
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
- */
-
-/**
- * DES.
- *
- * @author See comments in the source file
- * @version $Id: DES.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- *
- */
-public class DES implements BlockCipher
-{
- private int[] workingKey = null;
-
- /**
- * standard constructor.
- */
- public DES()
- {
- }
-
- /**
- * initialise a DES cipher.
- *
- * @param encrypting
- * whether or not we are for encryption.
- * @param key
- * the parameters required to set up the cipher.
- * @exception IllegalArgumentException
- * if the params argument is inappropriate.
- */
- public void init(boolean encrypting, byte[] key)
- {
- this.workingKey = generateWorkingKey(encrypting, key, 0);
- }
-
- public String getAlgorithmName()
- {
- return "DES";
- }
-
- public int getBlockSize()
- {
- return 8;
- }
-
- public void transformBlock(byte[] in, int inOff, byte[] out, int outOff)
- {
- if (workingKey == null)
- {
- throw new IllegalStateException("DES engine not initialised!");
- }
-
- desFunc(workingKey, in, inOff, out, outOff);
- }
-
- public void reset()
- {
- }
-
- /**
- * what follows is mainly taken from "Applied Cryptography", by Bruce
- * Schneier, however it also bears great resemblance to Richard
- * Outerbridge's D3DES...
- */
-
- static short[] Df_Key = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32,
- 0x10, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67 };
-
- static short[] bytebit = { 0200, 0100, 040, 020, 010, 04, 02, 01 };
-
- static int[] bigbyte = { 0x800000, 0x400000, 0x200000, 0x100000, 0x80000, 0x40000, 0x20000, 0x10000, 0x8000,
- 0x4000, 0x2000, 0x1000, 0x800, 0x400, 0x200, 0x100, 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1 };
-
- /*
- * Use the key schedule specified in the Standard (ANSI X3.92-1981).
- */
-
- static byte[] pc1 = { 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2,
- 59, 51, 43, 35, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 60, 52, 44, 36, 28, 20, 12,
- 4, 27, 19, 11, 3 };
-
- static byte[] totrot = { 1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28 };
-
- static byte[] pc2 = { 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1, 40,
- 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47, 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 };
-
- static int[] SP1 = { 0x01010400, 0x00000000, 0x00010000, 0x01010404, 0x01010004, 0x00010404, 0x00000004,
- 0x00010000, 0x00000400, 0x01010400, 0x01010404, 0x00000400, 0x01000404, 0x01010004, 0x01000000, 0x00000004,
- 0x00000404, 0x01000400, 0x01000400, 0x00010400, 0x00010400, 0x01010000, 0x01010000, 0x01000404, 0x00010004,
- 0x01000004, 0x01000004, 0x00010004, 0x00000000, 0x00000404, 0x00010404, 0x01000000, 0x00010000, 0x01010404,
- 0x00000004, 0x01010000, 0x01010400, 0x01000000, 0x01000000, 0x00000400, 0x01010004, 0x00010000, 0x00010400,
- 0x01000004, 0x00000400, 0x00000004, 0x01000404, 0x00010404, 0x01010404, 0x00010004, 0x01010000, 0x01000404,
- 0x01000004, 0x00000404, 0x00010404, 0x01010400, 0x00000404, 0x01000400, 0x01000400, 0x00000000, 0x00010004,
- 0x00010400, 0x00000000, 0x01010004 };
-
- static int[] SP2 = { 0x80108020, 0x80008000, 0x00008000, 0x00108020, 0x00100000, 0x00000020, 0x80100020,
- 0x80008020, 0x80000020, 0x80108020, 0x80108000, 0x80000000, 0x80008000, 0x00100000, 0x00000020, 0x80100020,
- 0x00108000, 0x00100020, 0x80008020, 0x00000000, 0x80000000, 0x00008000, 0x00108020, 0x80100000, 0x00100020,
- 0x80000020, 0x00000000, 0x00108000, 0x00008020, 0x80108000, 0x80100000, 0x00008020, 0x00000000, 0x00108020,
- 0x80100020, 0x00100000, 0x80008020, 0x80100000, 0x80108000, 0x00008000, 0x80100000, 0x80008000, 0x00000020,
- 0x80108020, 0x00108020, 0x00000020, 0x00008000, 0x80000000, 0x00008020, 0x80108000, 0x00100000, 0x80000020,
- 0x00100020, 0x80008020, 0x80000020, 0x00100020, 0x00108000, 0x00000000, 0x80008000, 0x00008020, 0x80000000,
- 0x80100020, 0x80108020, 0x00108000 };
-
- static int[] SP3 = { 0x00000208, 0x08020200, 0x00000000, 0x08020008, 0x08000200, 0x00000000, 0x00020208,
- 0x08000200, 0x00020008, 0x08000008, 0x08000008, 0x00020000, 0x08020208, 0x00020008, 0x08020000, 0x00000208,
- 0x08000000, 0x00000008, 0x08020200, 0x00000200, 0x00020200, 0x08020000, 0x08020008, 0x00020208, 0x08000208,
- 0x00020200, 0x00020000, 0x08000208, 0x00000008, 0x08020208, 0x00000200, 0x08000000, 0x08020200, 0x08000000,
- 0x00020008, 0x00000208, 0x00020000, 0x08020200, 0x08000200, 0x00000000, 0x00000200, 0x00020008, 0x08020208,
- 0x08000200, 0x08000008, 0x00000200, 0x00000000, 0x08020008, 0x08000208, 0x00020000, 0x08000000, 0x08020208,
- 0x00000008, 0x00020208, 0x00020200, 0x08000008, 0x08020000, 0x08000208, 0x00000208, 0x08020000, 0x00020208,
- 0x00000008, 0x08020008, 0x00020200 };
-
- static int[] SP4 = { 0x00802001, 0x00002081, 0x00002081, 0x00000080, 0x00802080, 0x00800081, 0x00800001,
- 0x00002001, 0x00000000, 0x00802000, 0x00802000, 0x00802081, 0x00000081, 0x00000000, 0x00800080, 0x00800001,
- 0x00000001, 0x00002000, 0x00800000, 0x00802001, 0x00000080, 0x00800000, 0x00002001, 0x00002080, 0x00800081,
- 0x00000001, 0x00002080, 0x00800080, 0x00002000, 0x00802080, 0x00802081, 0x00000081, 0x00800080, 0x00800001,
- 0x00802000, 0x00802081, 0x00000081, 0x00000000, 0x00000000, 0x00802000, 0x00002080, 0x00800080, 0x00800081,
- 0x00000001, 0x00802001, 0x00002081, 0x00002081, 0x00000080, 0x00802081, 0x00000081, 0x00000001, 0x00002000,
- 0x00800001, 0x00002001, 0x00802080, 0x00800081, 0x00002001, 0x00002080, 0x00800000, 0x00802001, 0x00000080,
- 0x00800000, 0x00002000, 0x00802080 };
-
- static int[] SP5 = { 0x00000100, 0x02080100, 0x02080000, 0x42000100, 0x00080000, 0x00000100, 0x40000000,
- 0x02080000, 0x40080100, 0x00080000, 0x02000100, 0x40080100, 0x42000100, 0x42080000, 0x00080100, 0x40000000,
- 0x02000000, 0x40080000, 0x40080000, 0x00000000, 0x40000100, 0x42080100, 0x42080100, 0x02000100, 0x42080000,
- 0x40000100, 0x00000000, 0x42000000, 0x02080100, 0x02000000, 0x42000000, 0x00080100, 0x00080000, 0x42000100,
- 0x00000100, 0x02000000, 0x40000000, 0x02080000, 0x42000100, 0x40080100, 0x02000100, 0x40000000, 0x42080000,
- 0x02080100, 0x40080100, 0x00000100, 0x02000000, 0x42080000, 0x42080100, 0x00080100, 0x42000000, 0x42080100,
- 0x02080000, 0x00000000, 0x40080000, 0x42000000, 0x00080100, 0x02000100, 0x40000100, 0x00080000, 0x00000000,
- 0x40080000, 0x02080100, 0x40000100 };
-
- static int[] SP6 = { 0x20000010, 0x20400000, 0x00004000, 0x20404010, 0x20400000, 0x00000010, 0x20404010,
- 0x00400000, 0x20004000, 0x00404010, 0x00400000, 0x20000010, 0x00400010, 0x20004000, 0x20000000, 0x00004010,
- 0x00000000, 0x00400010, 0x20004010, 0x00004000, 0x00404000, 0x20004010, 0x00000010, 0x20400010, 0x20400010,
- 0x00000000, 0x00404010, 0x20404000, 0x00004010, 0x00404000, 0x20404000, 0x20000000, 0x20004000, 0x00000010,
- 0x20400010, 0x00404000, 0x20404010, 0x00400000, 0x00004010, 0x20000010, 0x00400000, 0x20004000, 0x20000000,
- 0x00004010, 0x20000010, 0x20404010, 0x00404000, 0x20400000, 0x00404010, 0x20404000, 0x00000000, 0x20400010,
- 0x00000010, 0x00004000, 0x20400000, 0x00404010, 0x00004000, 0x00400010, 0x20004010, 0x00000000, 0x20404000,
- 0x20000000, 0x00400010, 0x20004010 };
-
- static int[] SP7 = { 0x00200000, 0x04200002, 0x04000802, 0x00000000, 0x00000800, 0x04000802, 0x00200802,
- 0x04200800, 0x04200802, 0x00200000, 0x00000000, 0x04000002, 0x00000002, 0x04000000, 0x04200002, 0x00000802,
- 0x04000800, 0x00200802, 0x00200002, 0x04000800, 0x04000002, 0x04200000, 0x04200800, 0x00200002, 0x04200000,
- 0x00000800, 0x00000802, 0x04200802, 0x00200800, 0x00000002, 0x04000000, 0x00200800, 0x04000000, 0x00200800,
- 0x00200000, 0x04000802, 0x04000802, 0x04200002, 0x04200002, 0x00000002, 0x00200002, 0x04000000, 0x04000800,
- 0x00200000, 0x04200800, 0x00000802, 0x00200802, 0x04200800, 0x00000802, 0x04000002, 0x04200802, 0x04200000,
- 0x00200800, 0x00000000, 0x00000002, 0x04200802, 0x00000000, 0x00200802, 0x04200000, 0x00000800, 0x04000002,
- 0x04000800, 0x00000800, 0x00200002 };
-
- static int[] SP8 = { 0x10001040, 0x00001000, 0x00040000, 0x10041040, 0x10000000, 0x10001040, 0x00000040,
- 0x10000000, 0x00040040, 0x10040000, 0x10041040, 0x00041000, 0x10041000, 0x00041040, 0x00001000, 0x00000040,
- 0x10040000, 0x10000040, 0x10001000, 0x00001040, 0x00041000, 0x00040040, 0x10040040, 0x10041000, 0x00001040,
- 0x00000000, 0x00000000, 0x10040040, 0x10000040, 0x10001000, 0x00041040, 0x00040000, 0x00041040, 0x00040000,
- 0x10041000, 0x00001000, 0x00000040, 0x10040040, 0x00001000, 0x00041040, 0x10001000, 0x00000040, 0x10000040,
- 0x10040000, 0x10040040, 0x10000000, 0x00040000, 0x10001040, 0x00000000, 0x10041040, 0x00040040, 0x10000040,
- 0x10040000, 0x10001000, 0x10001040, 0x00000000, 0x10041040, 0x00041000, 0x00041000, 0x00001040, 0x00001040,
- 0x00040040, 0x10000000, 0x10041000 };
-
- /**
- * generate an integer based working key based on our secret key and what we
- * processing we are planning to do.
- *
- * Acknowledgements for this routine go to James Gillogly & Phil Karn.
- * (whoever, and wherever they are!).
- */
- protected int[] generateWorkingKey(boolean encrypting, byte[] key, int off)
- {
- int[] newKey = new int[32];
- boolean[] pc1m = new boolean[56], pcr = new boolean[56];
-
- for (int j = 0; j < 56; j++)
- {
- int l = pc1[j];
-
- pc1m[j] = ((key[off + (l >>> 3)] & bytebit[l & 07]) != 0);
- }
-
- for (int i = 0; i < 16; i++)
- {
- int l, m, n;
-
- if (encrypting)
- {
- m = i << 1;
- }
- else
- {
- m = (15 - i) << 1;
- }
-
- n = m + 1;
- newKey[m] = newKey[n] = 0;
-
- for (int j = 0; j < 28; j++)
- {
- l = j + totrot[i];
- if (l < 28)
- {
- pcr[j] = pc1m[l];
- }
- else
- {
- pcr[j] = pc1m[l - 28];
- }
- }
-
- for (int j = 28; j < 56; j++)
- {
- l = j + totrot[i];
- if (l < 56)
- {
- pcr[j] = pc1m[l];
- }
- else
- {
- pcr[j] = pc1m[l - 28];
- }
- }
-
- for (int j = 0; j < 24; j++)
- {
- if (pcr[pc2[j]])
- {
- newKey[m] |= bigbyte[j];
- }
-
- if (pcr[pc2[j + 24]])
- {
- newKey[n] |= bigbyte[j];
- }
- }
- }
-
- //
- // store the processed key
- //
- for (int i = 0; i != 32; i += 2)
- {
- int i1, i2;
-
- i1 = newKey[i];
- i2 = newKey[i + 1];
-
- newKey[i] = ((i1 & 0x00fc0000) << 6) | ((i1 & 0x00000fc0) << 10) | ((i2 & 0x00fc0000) >>> 10)
- | ((i2 & 0x00000fc0) >>> 6);
-
- newKey[i + 1] = ((i1 & 0x0003f000) << 12) | ((i1 & 0x0000003f) << 16) | ((i2 & 0x0003f000) >>> 4)
- | (i2 & 0x0000003f);
- }
-
- return newKey;
- }
-
- /**
- * the DES engine.
- */
- protected void desFunc(int[] wKey, byte[] in, int inOff, byte[] out, int outOff)
- {
- int work, right, left;
-
- left = (in[inOff + 0] & 0xff) << 24;
- left |= (in[inOff + 1] & 0xff) << 16;
- left |= (in[inOff + 2] & 0xff) << 8;
- left |= (in[inOff + 3] & 0xff);
-
- right = (in[inOff + 4] & 0xff) << 24;
- right |= (in[inOff + 5] & 0xff) << 16;
- right |= (in[inOff + 6] & 0xff) << 8;
- right |= (in[inOff + 7] & 0xff);
-
- work = ((left >>> 4) ^ right) & 0x0f0f0f0f;
- right ^= work;
- left ^= (work << 4);
- work = ((left >>> 16) ^ right) & 0x0000ffff;
- right ^= work;
- left ^= (work << 16);
- work = ((right >>> 2) ^ left) & 0x33333333;
- left ^= work;
- right ^= (work << 2);
- work = ((right >>> 8) ^ left) & 0x00ff00ff;
- left ^= work;
- right ^= (work << 8);
- right = ((right << 1) | ((right >>> 31) & 1)) & 0xffffffff;
- work = (left ^ right) & 0xaaaaaaaa;
- left ^= work;
- right ^= work;
- left = ((left << 1) | ((left >>> 31) & 1)) & 0xffffffff;
-
- for (int round = 0; round < 8; round++)
- {
- int fval;
-
- work = (right << 28) | (right >>> 4);
- work ^= wKey[round * 4 + 0];
- fval = SP7[work & 0x3f];
- fval |= SP5[(work >>> 8) & 0x3f];
- fval |= SP3[(work >>> 16) & 0x3f];
- fval |= SP1[(work >>> 24) & 0x3f];
- work = right ^ wKey[round * 4 + 1];
- fval |= SP8[work & 0x3f];
- fval |= SP6[(work >>> 8) & 0x3f];
- fval |= SP4[(work >>> 16) & 0x3f];
- fval |= SP2[(work >>> 24) & 0x3f];
- left ^= fval;
- work = (left << 28) | (left >>> 4);
- work ^= wKey[round * 4 + 2];
- fval = SP7[work & 0x3f];
- fval |= SP5[(work >>> 8) & 0x3f];
- fval |= SP3[(work >>> 16) & 0x3f];
- fval |= SP1[(work >>> 24) & 0x3f];
- work = left ^ wKey[round * 4 + 3];
- fval |= SP8[work & 0x3f];
- fval |= SP6[(work >>> 8) & 0x3f];
- fval |= SP4[(work >>> 16) & 0x3f];
- fval |= SP2[(work >>> 24) & 0x3f];
- right ^= fval;
- }
-
- right = (right << 31) | (right >>> 1);
- work = (left ^ right) & 0xaaaaaaaa;
- left ^= work;
- right ^= work;
- left = (left << 31) | (left >>> 1);
- work = ((left >>> 8) ^ right) & 0x00ff00ff;
- right ^= work;
- left ^= (work << 8);
- work = ((left >>> 2) ^ right) & 0x33333333;
- right ^= work;
- left ^= (work << 2);
- work = ((right >>> 16) ^ left) & 0x0000ffff;
- left ^= work;
- right ^= (work << 16);
- work = ((right >>> 4) ^ left) & 0x0f0f0f0f;
- left ^= work;
- right ^= (work << 4);
-
- out[outOff + 0] = (byte) ((right >>> 24) & 0xff);
- out[outOff + 1] = (byte) ((right >>> 16) & 0xff);
- out[outOff + 2] = (byte) ((right >>> 8) & 0xff);
- out[outOff + 3] = (byte) (right & 0xff);
- out[outOff + 4] = (byte) ((left >>> 24) & 0xff);
- out[outOff + 5] = (byte) ((left >>> 16) & 0xff);
- out[outOff + 6] = (byte) ((left >>> 8) & 0xff);
- out[outOff + 7] = (byte) (left & 0xff);
- }
-}
+
+package com.trilead.ssh2.crypto.cipher;
+
+/*
+ This file is based on the 3DES implementation from the Bouncy Castle Crypto package.
+ Their licence file states the following:
+
+ Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle
+ (http://www.bouncycastle.org)
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ */
+
+/**
+ * DES.
+ *
+ * @author See comments in the source file
+ * @version $Id: DES.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ *
+ */
+public class DES implements BlockCipher
+{
+ private int[] workingKey = null;
+
+ /**
+ * standard constructor.
+ */
+ public DES()
+ {
+ }
+
+ /**
+ * initialise a DES cipher.
+ *
+ * @param encrypting
+ * whether or not we are for encryption.
+ * @param key
+ * the parameters required to set up the cipher.
+ * @exception IllegalArgumentException
+ * if the params argument is inappropriate.
+ */
+ public void init(boolean encrypting, byte[] key)
+ {
+ this.workingKey = generateWorkingKey(encrypting, key, 0);
+ }
+
+ public String getAlgorithmName()
+ {
+ return "DES";
+ }
+
+ public int getBlockSize()
+ {
+ return 8;
+ }
+
+ public void transformBlock(byte[] in, int inOff, byte[] out, int outOff)
+ {
+ if (workingKey == null)
+ {
+ throw new IllegalStateException("DES engine not initialised!");
+ }
+
+ desFunc(workingKey, in, inOff, out, outOff);
+ }
+
+ public void reset()
+ {
+ }
+
+ /**
+ * what follows is mainly taken from "Applied Cryptography", by Bruce
+ * Schneier, however it also bears great resemblance to Richard
+ * Outerbridge's D3DES...
+ */
+
+ static short[] Df_Key = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32,
+ 0x10, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67 };
+
+ static short[] bytebit = { 0200, 0100, 040, 020, 010, 04, 02, 01 };
+
+ static int[] bigbyte = { 0x800000, 0x400000, 0x200000, 0x100000, 0x80000, 0x40000, 0x20000, 0x10000, 0x8000,
+ 0x4000, 0x2000, 0x1000, 0x800, 0x400, 0x200, 0x100, 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1 };
+
+ /*
+ * Use the key schedule specified in the Standard (ANSI X3.92-1981).
+ */
+
+ static byte[] pc1 = { 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2,
+ 59, 51, 43, 35, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 60, 52, 44, 36, 28, 20, 12,
+ 4, 27, 19, 11, 3 };
+
+ static byte[] totrot = { 1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28 };
+
+ static byte[] pc2 = { 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1, 40,
+ 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47, 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 };
+
+ static int[] SP1 = { 0x01010400, 0x00000000, 0x00010000, 0x01010404, 0x01010004, 0x00010404, 0x00000004,
+ 0x00010000, 0x00000400, 0x01010400, 0x01010404, 0x00000400, 0x01000404, 0x01010004, 0x01000000, 0x00000004,
+ 0x00000404, 0x01000400, 0x01000400, 0x00010400, 0x00010400, 0x01010000, 0x01010000, 0x01000404, 0x00010004,
+ 0x01000004, 0x01000004, 0x00010004, 0x00000000, 0x00000404, 0x00010404, 0x01000000, 0x00010000, 0x01010404,
+ 0x00000004, 0x01010000, 0x01010400, 0x01000000, 0x01000000, 0x00000400, 0x01010004, 0x00010000, 0x00010400,
+ 0x01000004, 0x00000400, 0x00000004, 0x01000404, 0x00010404, 0x01010404, 0x00010004, 0x01010000, 0x01000404,
+ 0x01000004, 0x00000404, 0x00010404, 0x01010400, 0x00000404, 0x01000400, 0x01000400, 0x00000000, 0x00010004,
+ 0x00010400, 0x00000000, 0x01010004 };
+
+ static int[] SP2 = { 0x80108020, 0x80008000, 0x00008000, 0x00108020, 0x00100000, 0x00000020, 0x80100020,
+ 0x80008020, 0x80000020, 0x80108020, 0x80108000, 0x80000000, 0x80008000, 0x00100000, 0x00000020, 0x80100020,
+ 0x00108000, 0x00100020, 0x80008020, 0x00000000, 0x80000000, 0x00008000, 0x00108020, 0x80100000, 0x00100020,
+ 0x80000020, 0x00000000, 0x00108000, 0x00008020, 0x80108000, 0x80100000, 0x00008020, 0x00000000, 0x00108020,
+ 0x80100020, 0x00100000, 0x80008020, 0x80100000, 0x80108000, 0x00008000, 0x80100000, 0x80008000, 0x00000020,
+ 0x80108020, 0x00108020, 0x00000020, 0x00008000, 0x80000000, 0x00008020, 0x80108000, 0x00100000, 0x80000020,
+ 0x00100020, 0x80008020, 0x80000020, 0x00100020, 0x00108000, 0x00000000, 0x80008000, 0x00008020, 0x80000000,
+ 0x80100020, 0x80108020, 0x00108000 };
+
+ static int[] SP3 = { 0x00000208, 0x08020200, 0x00000000, 0x08020008, 0x08000200, 0x00000000, 0x00020208,
+ 0x08000200, 0x00020008, 0x08000008, 0x08000008, 0x00020000, 0x08020208, 0x00020008, 0x08020000, 0x00000208,
+ 0x08000000, 0x00000008, 0x08020200, 0x00000200, 0x00020200, 0x08020000, 0x08020008, 0x00020208, 0x08000208,
+ 0x00020200, 0x00020000, 0x08000208, 0x00000008, 0x08020208, 0x00000200, 0x08000000, 0x08020200, 0x08000000,
+ 0x00020008, 0x00000208, 0x00020000, 0x08020200, 0x08000200, 0x00000000, 0x00000200, 0x00020008, 0x08020208,
+ 0x08000200, 0x08000008, 0x00000200, 0x00000000, 0x08020008, 0x08000208, 0x00020000, 0x08000000, 0x08020208,
+ 0x00000008, 0x00020208, 0x00020200, 0x08000008, 0x08020000, 0x08000208, 0x00000208, 0x08020000, 0x00020208,
+ 0x00000008, 0x08020008, 0x00020200 };
+
+ static int[] SP4 = { 0x00802001, 0x00002081, 0x00002081, 0x00000080, 0x00802080, 0x00800081, 0x00800001,
+ 0x00002001, 0x00000000, 0x00802000, 0x00802000, 0x00802081, 0x00000081, 0x00000000, 0x00800080, 0x00800001,
+ 0x00000001, 0x00002000, 0x00800000, 0x00802001, 0x00000080, 0x00800000, 0x00002001, 0x00002080, 0x00800081,
+ 0x00000001, 0x00002080, 0x00800080, 0x00002000, 0x00802080, 0x00802081, 0x00000081, 0x00800080, 0x00800001,
+ 0x00802000, 0x00802081, 0x00000081, 0x00000000, 0x00000000, 0x00802000, 0x00002080, 0x00800080, 0x00800081,
+ 0x00000001, 0x00802001, 0x00002081, 0x00002081, 0x00000080, 0x00802081, 0x00000081, 0x00000001, 0x00002000,
+ 0x00800001, 0x00002001, 0x00802080, 0x00800081, 0x00002001, 0x00002080, 0x00800000, 0x00802001, 0x00000080,
+ 0x00800000, 0x00002000, 0x00802080 };
+
+ static int[] SP5 = { 0x00000100, 0x02080100, 0x02080000, 0x42000100, 0x00080000, 0x00000100, 0x40000000,
+ 0x02080000, 0x40080100, 0x00080000, 0x02000100, 0x40080100, 0x42000100, 0x42080000, 0x00080100, 0x40000000,
+ 0x02000000, 0x40080000, 0x40080000, 0x00000000, 0x40000100, 0x42080100, 0x42080100, 0x02000100, 0x42080000,
+ 0x40000100, 0x00000000, 0x42000000, 0x02080100, 0x02000000, 0x42000000, 0x00080100, 0x00080000, 0x42000100,
+ 0x00000100, 0x02000000, 0x40000000, 0x02080000, 0x42000100, 0x40080100, 0x02000100, 0x40000000, 0x42080000,
+ 0x02080100, 0x40080100, 0x00000100, 0x02000000, 0x42080000, 0x42080100, 0x00080100, 0x42000000, 0x42080100,
+ 0x02080000, 0x00000000, 0x40080000, 0x42000000, 0x00080100, 0x02000100, 0x40000100, 0x00080000, 0x00000000,
+ 0x40080000, 0x02080100, 0x40000100 };
+
+ static int[] SP6 = { 0x20000010, 0x20400000, 0x00004000, 0x20404010, 0x20400000, 0x00000010, 0x20404010,
+ 0x00400000, 0x20004000, 0x00404010, 0x00400000, 0x20000010, 0x00400010, 0x20004000, 0x20000000, 0x00004010,
+ 0x00000000, 0x00400010, 0x20004010, 0x00004000, 0x00404000, 0x20004010, 0x00000010, 0x20400010, 0x20400010,
+ 0x00000000, 0x00404010, 0x20404000, 0x00004010, 0x00404000, 0x20404000, 0x20000000, 0x20004000, 0x00000010,
+ 0x20400010, 0x00404000, 0x20404010, 0x00400000, 0x00004010, 0x20000010, 0x00400000, 0x20004000, 0x20000000,
+ 0x00004010, 0x20000010, 0x20404010, 0x00404000, 0x20400000, 0x00404010, 0x20404000, 0x00000000, 0x20400010,
+ 0x00000010, 0x00004000, 0x20400000, 0x00404010, 0x00004000, 0x00400010, 0x20004010, 0x00000000, 0x20404000,
+ 0x20000000, 0x00400010, 0x20004010 };
+
+ static int[] SP7 = { 0x00200000, 0x04200002, 0x04000802, 0x00000000, 0x00000800, 0x04000802, 0x00200802,
+ 0x04200800, 0x04200802, 0x00200000, 0x00000000, 0x04000002, 0x00000002, 0x04000000, 0x04200002, 0x00000802,
+ 0x04000800, 0x00200802, 0x00200002, 0x04000800, 0x04000002, 0x04200000, 0x04200800, 0x00200002, 0x04200000,
+ 0x00000800, 0x00000802, 0x04200802, 0x00200800, 0x00000002, 0x04000000, 0x00200800, 0x04000000, 0x00200800,
+ 0x00200000, 0x04000802, 0x04000802, 0x04200002, 0x04200002, 0x00000002, 0x00200002, 0x04000000, 0x04000800,
+ 0x00200000, 0x04200800, 0x00000802, 0x00200802, 0x04200800, 0x00000802, 0x04000002, 0x04200802, 0x04200000,
+ 0x00200800, 0x00000000, 0x00000002, 0x04200802, 0x00000000, 0x00200802, 0x04200000, 0x00000800, 0x04000002,
+ 0x04000800, 0x00000800, 0x00200002 };
+
+ static int[] SP8 = { 0x10001040, 0x00001000, 0x00040000, 0x10041040, 0x10000000, 0x10001040, 0x00000040,
+ 0x10000000, 0x00040040, 0x10040000, 0x10041040, 0x00041000, 0x10041000, 0x00041040, 0x00001000, 0x00000040,
+ 0x10040000, 0x10000040, 0x10001000, 0x00001040, 0x00041000, 0x00040040, 0x10040040, 0x10041000, 0x00001040,
+ 0x00000000, 0x00000000, 0x10040040, 0x10000040, 0x10001000, 0x00041040, 0x00040000, 0x00041040, 0x00040000,
+ 0x10041000, 0x00001000, 0x00000040, 0x10040040, 0x00001000, 0x00041040, 0x10001000, 0x00000040, 0x10000040,
+ 0x10040000, 0x10040040, 0x10000000, 0x00040000, 0x10001040, 0x00000000, 0x10041040, 0x00040040, 0x10000040,
+ 0x10040000, 0x10001000, 0x10001040, 0x00000000, 0x10041040, 0x00041000, 0x00041000, 0x00001040, 0x00001040,
+ 0x00040040, 0x10000000, 0x10041000 };
+
+ /**
+ * generate an integer based working key based on our secret key and what we
+ * processing we are planning to do.
+ *
+ * Acknowledgements for this routine go to James Gillogly & Phil Karn.
+ * (whoever, and wherever they are!).
+ */
+ protected int[] generateWorkingKey(boolean encrypting, byte[] key, int off)
+ {
+ int[] newKey = new int[32];
+ boolean[] pc1m = new boolean[56], pcr = new boolean[56];
+
+ for (int j = 0; j < 56; j++)
+ {
+ int l = pc1[j];
+
+ pc1m[j] = ((key[off + (l >>> 3)] & bytebit[l & 07]) != 0);
+ }
+
+ for (int i = 0; i < 16; i++)
+ {
+ int l, m, n;
+
+ if (encrypting)
+ {
+ m = i << 1;
+ }
+ else
+ {
+ m = (15 - i) << 1;
+ }
+
+ n = m + 1;
+ newKey[m] = newKey[n] = 0;
+
+ for (int j = 0; j < 28; j++)
+ {
+ l = j + totrot[i];
+ if (l < 28)
+ {
+ pcr[j] = pc1m[l];
+ }
+ else
+ {
+ pcr[j] = pc1m[l - 28];
+ }
+ }
+
+ for (int j = 28; j < 56; j++)
+ {
+ l = j + totrot[i];
+ if (l < 56)
+ {
+ pcr[j] = pc1m[l];
+ }
+ else
+ {
+ pcr[j] = pc1m[l - 28];
+ }
+ }
+
+ for (int j = 0; j < 24; j++)
+ {
+ if (pcr[pc2[j]])
+ {
+ newKey[m] |= bigbyte[j];
+ }
+
+ if (pcr[pc2[j + 24]])
+ {
+ newKey[n] |= bigbyte[j];
+ }
+ }
+ }
+
+ //
+ // store the processed key
+ //
+ for (int i = 0; i != 32; i += 2)
+ {
+ int i1, i2;
+
+ i1 = newKey[i];
+ i2 = newKey[i + 1];
+
+ newKey[i] = ((i1 & 0x00fc0000) << 6) | ((i1 & 0x00000fc0) << 10) | ((i2 & 0x00fc0000) >>> 10)
+ | ((i2 & 0x00000fc0) >>> 6);
+
+ newKey[i + 1] = ((i1 & 0x0003f000) << 12) | ((i1 & 0x0000003f) << 16) | ((i2 & 0x0003f000) >>> 4)
+ | (i2 & 0x0000003f);
+ }
+
+ return newKey;
+ }
+
+ /**
+ * the DES engine.
+ */
+ protected void desFunc(int[] wKey, byte[] in, int inOff, byte[] out, int outOff)
+ {
+ int work, right, left;
+
+ left = (in[inOff + 0] & 0xff) << 24;
+ left |= (in[inOff + 1] & 0xff) << 16;
+ left |= (in[inOff + 2] & 0xff) << 8;
+ left |= (in[inOff + 3] & 0xff);
+
+ right = (in[inOff + 4] & 0xff) << 24;
+ right |= (in[inOff + 5] & 0xff) << 16;
+ right |= (in[inOff + 6] & 0xff) << 8;
+ right |= (in[inOff + 7] & 0xff);
+
+ work = ((left >>> 4) ^ right) & 0x0f0f0f0f;
+ right ^= work;
+ left ^= (work << 4);
+ work = ((left >>> 16) ^ right) & 0x0000ffff;
+ right ^= work;
+ left ^= (work << 16);
+ work = ((right >>> 2) ^ left) & 0x33333333;
+ left ^= work;
+ right ^= (work << 2);
+ work = ((right >>> 8) ^ left) & 0x00ff00ff;
+ left ^= work;
+ right ^= (work << 8);
+ right = ((right << 1) | ((right >>> 31) & 1)) & 0xffffffff;
+ work = (left ^ right) & 0xaaaaaaaa;
+ left ^= work;
+ right ^= work;
+ left = ((left << 1) | ((left >>> 31) & 1)) & 0xffffffff;
+
+ for (int round = 0; round < 8; round++)
+ {
+ int fval;
+
+ work = (right << 28) | (right >>> 4);
+ work ^= wKey[round * 4 + 0];
+ fval = SP7[work & 0x3f];
+ fval |= SP5[(work >>> 8) & 0x3f];
+ fval |= SP3[(work >>> 16) & 0x3f];
+ fval |= SP1[(work >>> 24) & 0x3f];
+ work = right ^ wKey[round * 4 + 1];
+ fval |= SP8[work & 0x3f];
+ fval |= SP6[(work >>> 8) & 0x3f];
+ fval |= SP4[(work >>> 16) & 0x3f];
+ fval |= SP2[(work >>> 24) & 0x3f];
+ left ^= fval;
+ work = (left << 28) | (left >>> 4);
+ work ^= wKey[round * 4 + 2];
+ fval = SP7[work & 0x3f];
+ fval |= SP5[(work >>> 8) & 0x3f];
+ fval |= SP3[(work >>> 16) & 0x3f];
+ fval |= SP1[(work >>> 24) & 0x3f];
+ work = left ^ wKey[round * 4 + 3];
+ fval |= SP8[work & 0x3f];
+ fval |= SP6[(work >>> 8) & 0x3f];
+ fval |= SP4[(work >>> 16) & 0x3f];
+ fval |= SP2[(work >>> 24) & 0x3f];
+ right ^= fval;
+ }
+
+ right = (right << 31) | (right >>> 1);
+ work = (left ^ right) & 0xaaaaaaaa;
+ left ^= work;
+ right ^= work;
+ left = (left << 31) | (left >>> 1);
+ work = ((left >>> 8) ^ right) & 0x00ff00ff;
+ right ^= work;
+ left ^= (work << 8);
+ work = ((left >>> 2) ^ right) & 0x33333333;
+ right ^= work;
+ left ^= (work << 2);
+ work = ((right >>> 16) ^ left) & 0x0000ffff;
+ left ^= work;
+ right ^= (work << 16);
+ work = ((right >>> 4) ^ left) & 0x0f0f0f0f;
+ left ^= work;
+ right ^= (work << 4);
+
+ out[outOff + 0] = (byte) ((right >>> 24) & 0xff);
+ out[outOff + 1] = (byte) ((right >>> 16) & 0xff);
+ out[outOff + 2] = (byte) ((right >>> 8) & 0xff);
+ out[outOff + 3] = (byte) (right & 0xff);
+ out[outOff + 4] = (byte) ((left >>> 24) & 0xff);
+ out[outOff + 5] = (byte) ((left >>> 16) & 0xff);
+ out[outOff + 6] = (byte) ((left >>> 8) & 0xff);
+ out[outOff + 7] = (byte) (left & 0xff);
+ }
+}
diff --git a/src/com/trilead/ssh2/crypto/cipher/DESede.java b/src/com/trilead/ssh2/crypto/cipher/DESede.java
index 6c0d797..f47a636 100644
--- a/src/com/trilead/ssh2/crypto/cipher/DESede.java
+++ b/src/com/trilead/ssh2/crypto/cipher/DESede.java
@@ -1,105 +1,105 @@
-
-package com.trilead.ssh2.crypto.cipher;
-
-/*
- This file was shamelessly taken (and modified) from the Bouncy Castle Crypto package.
- Their licence file states the following:
-
- Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle
- (http://www.bouncycastle.org)
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
- */
-
-/**
- * DESede.
- *
- * @author See comments in the source file
- * @version $Id: DESede.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- *
- */
-public class DESede extends DES
-{
- private int[] key1 = null;
- private int[] key2 = null;
- private int[] key3 = null;
-
- private boolean encrypt;
-
- /**
- * standard constructor.
- */
- public DESede()
- {
- }
-
- /**
- * initialise a DES cipher.
- *
- * @param encrypting
- * whether or not we are for encryption.
- * @param key
- * the parameters required to set up the cipher.
- * @exception IllegalArgumentException
- * if the params argument is inappropriate.
- */
- public void init(boolean encrypting, byte[] key)
- {
- key1 = generateWorkingKey(encrypting, key, 0);
- key2 = generateWorkingKey(!encrypting, key, 8);
- key3 = generateWorkingKey(encrypting, key, 16);
-
- encrypt = encrypting;
- }
-
- public String getAlgorithmName()
- {
- return "DESede";
- }
-
- public int getBlockSize()
- {
- return 8;
- }
-
- public void transformBlock(byte[] in, int inOff, byte[] out, int outOff)
- {
- if (key1 == null)
- {
- throw new IllegalStateException("DESede engine not initialised!");
- }
-
- if (encrypt)
- {
- desFunc(key1, in, inOff, out, outOff);
- desFunc(key2, out, outOff, out, outOff);
- desFunc(key3, out, outOff, out, outOff);
- }
- else
- {
- desFunc(key3, in, inOff, out, outOff);
- desFunc(key2, out, outOff, out, outOff);
- desFunc(key1, out, outOff, out, outOff);
- }
- }
-
- public void reset()
- {
- }
-}
+
+package com.trilead.ssh2.crypto.cipher;
+
+/*
+ This file was shamelessly taken (and modified) from the Bouncy Castle Crypto package.
+ Their licence file states the following:
+
+ Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle
+ (http://www.bouncycastle.org)
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ */
+
+/**
+ * DESede.
+ *
+ * @author See comments in the source file
+ * @version $Id: DESede.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ *
+ */
+public class DESede extends DES
+{
+ private int[] key1 = null;
+ private int[] key2 = null;
+ private int[] key3 = null;
+
+ private boolean encrypt;
+
+ /**
+ * standard constructor.
+ */
+ public DESede()
+ {
+ }
+
+ /**
+ * initialise a DES cipher.
+ *
+ * @param encrypting
+ * whether or not we are for encryption.
+ * @param key
+ * the parameters required to set up the cipher.
+ * @exception IllegalArgumentException
+ * if the params argument is inappropriate.
+ */
+ public void init(boolean encrypting, byte[] key)
+ {
+ key1 = generateWorkingKey(encrypting, key, 0);
+ key2 = generateWorkingKey(!encrypting, key, 8);
+ key3 = generateWorkingKey(encrypting, key, 16);
+
+ encrypt = encrypting;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "DESede";
+ }
+
+ public int getBlockSize()
+ {
+ return 8;
+ }
+
+ public void transformBlock(byte[] in, int inOff, byte[] out, int outOff)
+ {
+ if (key1 == null)
+ {
+ throw new IllegalStateException("DESede engine not initialised!");
+ }
+
+ if (encrypt)
+ {
+ desFunc(key1, in, inOff, out, outOff);
+ desFunc(key2, out, outOff, out, outOff);
+ desFunc(key3, out, outOff, out, outOff);
+ }
+ else
+ {
+ desFunc(key3, in, inOff, out, outOff);
+ desFunc(key2, out, outOff, out, outOff);
+ desFunc(key1, out, outOff, out, outOff);
+ }
+ }
+
+ public void reset()
+ {
+ }
+}
diff --git a/src/com/trilead/ssh2/crypto/cipher/NullCipher.java b/src/com/trilead/ssh2/crypto/cipher/NullCipher.java
index 5a03608..38f8215 100644
--- a/src/com/trilead/ssh2/crypto/cipher/NullCipher.java
+++ b/src/com/trilead/ssh2/crypto/cipher/NullCipher.java
@@ -1,35 +1,35 @@
-package com.trilead.ssh2.crypto.cipher;
-
-/**
- * NullCipher.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: NullCipher.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class NullCipher implements BlockCipher
-{
- private int blockSize = 8;
-
- public NullCipher()
- {
- }
-
- public NullCipher(int blockSize)
- {
- this.blockSize = blockSize;
- }
-
- public void init(boolean forEncryption, byte[] key)
- {
- }
-
- public int getBlockSize()
- {
- return blockSize;
- }
-
- public void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff)
- {
- System.arraycopy(src, srcoff, dst, dstoff, blockSize);
- }
-}
+package com.trilead.ssh2.crypto.cipher;
+
+/**
+ * NullCipher.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: NullCipher.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class NullCipher implements BlockCipher
+{
+ private int blockSize = 8;
+
+ public NullCipher()
+ {
+ }
+
+ public NullCipher(int blockSize)
+ {
+ this.blockSize = blockSize;
+ }
+
+ public void init(boolean forEncryption, byte[] key)
+ {
+ }
+
+ public int getBlockSize()
+ {
+ return blockSize;
+ }
+
+ public void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff)
+ {
+ System.arraycopy(src, srcoff, dst, dstoff, blockSize);
+ }
+}
diff --git a/src/com/trilead/ssh2/crypto/dh/DhExchange.java b/src/com/trilead/ssh2/crypto/dh/DhExchange.java
index 5622a72..3acde25 100644
--- a/src/com/trilead/ssh2/crypto/dh/DhExchange.java
+++ b/src/com/trilead/ssh2/crypto/dh/DhExchange.java
@@ -1,147 +1,132 @@
-
-package com.trilead.ssh2.crypto.dh;
-
-import java.io.UnsupportedEncodingException;
-import java.math.BigInteger;
-import java.security.SecureRandom;
-
-import com.trilead.ssh2.crypto.digest.HashForSSH2Types;
-import com.trilead.ssh2.log.Logger;
-
-
-/**
- * DhExchange.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: DhExchange.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
- */
-public class DhExchange
-{
- private static final Logger log = Logger.getLogger(DhExchange.class);
-
- /* Given by the standard */
-
- static final BigInteger p1, p14;
- static final BigInteger g;
-
- BigInteger p;
-
- /* Client public and private */
-
- BigInteger e;
- BigInteger x;
-
- /* Server public */
-
- BigInteger f;
-
- /* Shared secret */
-
- BigInteger k;
-
- static
- {
- final String p1_string = "17976931348623159077083915679378745319786029604875"
- + "60117064444236841971802161585193689478337958649255415021805654859805036464"
- + "40548199239100050792877003355816639229553136239076508735759914822574862575"
- + "00742530207744771258955095793777842444242661733472762929938766870920560605"
- + "0270810842907692932019128194467627007";
-
- final String p14_string = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129"
- + "024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0"
- + "A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB"
- + "6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A"
- + "163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208"
- + "552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36C"
- + "E3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF69558171"
- + "83995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF";
-
- p1 = new BigInteger(p1_string);
- p14 = new BigInteger(p14_string, 16);
- g = new BigInteger("2");
- }
-
- public DhExchange()
- {
- }
-
- public void init(int group, SecureRandom rnd)
- {
- k = null;
-
- if (group == 1)
- p = p1;
- else if (group == 14)
- p = p14;
- else
- throw new IllegalArgumentException("Unknown DH group " + group);
-
- x = new BigInteger(p.bitLength() - 1, rnd);
-
- e = g.modPow(x, p);
- }
-
- /**
- * @return Returns the e.
- * @throws IllegalStateException
- */
- public BigInteger getE()
- {
- if (e == null)
- throw new IllegalStateException("DhDsaExchange not initialized!");
-
- return e;
- }
-
- /**
- * @return Returns the shared secret k.
- * @throws IllegalStateException
- */
- public BigInteger getK()
- {
- if (k == null)
- throw new IllegalStateException("Shared secret not yet known, need f first!");
-
- return k;
- }
-
- /**
- * @param f
- */
- public void setF(BigInteger f)
- {
- if (e == null)
- throw new IllegalStateException("DhDsaExchange not initialized!");
-
- BigInteger zero = BigInteger.valueOf(0);
-
- if (zero.compareTo(f) >= 0 || p.compareTo(f) <= 0)
- throw new IllegalArgumentException("Invalid f specified!");
-
- this.f = f;
- this.k = f.modPow(x, p);
- }
-
- public byte[] calculateH(byte[] clientversion, byte[] serverversion, byte[] clientKexPayload,
- byte[] serverKexPayload, byte[] hostKey) throws UnsupportedEncodingException
- {
- HashForSSH2Types hash = new HashForSSH2Types("SHA1");
-
- if (log.isEnabled())
- {
- log.log(90, "Client: '" + new String(clientversion, "ISO-8859-1") + "'");
- log.log(90, "Server: '" + new String(serverversion, "ISO-8859-1") + "'");
- }
-
- hash.updateByteString(clientversion);
- hash.updateByteString(serverversion);
- hash.updateByteString(clientKexPayload);
- hash.updateByteString(serverKexPayload);
- hash.updateByteString(hostKey);
- hash.updateBigInt(e);
- hash.updateBigInt(f);
- hash.updateBigInt(k);
-
- return hash.getDigest();
- }
-}
+/**
+ *
+ */
+package com.trilead.ssh2.crypto.dh;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.InvalidKeySpecException;
+
+import javax.crypto.KeyAgreement;
+import javax.crypto.interfaces.DHPrivateKey;
+import javax.crypto.interfaces.DHPublicKey;
+import javax.crypto.spec.DHParameterSpec;
+import javax.crypto.spec.DHPublicKeySpec;
+
+/**
+ * @author kenny
+ *
+ */
+public class DhExchange extends GenericDhExchange {
+
+ /* Given by the standard */
+
+ private static final BigInteger P1 = new BigInteger(
+ "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+ + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
+ + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381"
+ + "FFFFFFFFFFFFFFFF", 16);
+
+ private static final BigInteger P14 = new BigInteger(
+ "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+ + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
+ + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
+ + "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"
+ + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
+ + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510"
+ + "15728E5A8AACAA68FFFFFFFFFFFFFFFF", 16);
+
+ private static final BigInteger G = BigInteger.valueOf(2);
+
+ /* Client public and private */
+
+ private DHPrivateKey clientPrivate;
+ private DHPublicKey clientPublic;
+
+ /* Server public */
+
+ private DHPublicKey serverPublic;
+
+ @Override
+ public void init(String name) throws IOException {
+ final DHParameterSpec spec;
+ if ("diffie-hellman-group1-sha1".equals(name)) {
+ spec = new DHParameterSpec(P1, G);
+ } else if ("diffie-hellman-group14-sha1".equals(name)) {
+ spec = new DHParameterSpec(P14, G);
+ } else {
+ throw new IllegalArgumentException("Unknown DH group " + name);
+ }
+
+ try {
+ KeyPairGenerator kpg = KeyPairGenerator.getInstance("DH");
+ kpg.initialize(spec);
+ KeyPair pair = kpg.generateKeyPair();
+ clientPrivate = (DHPrivateKey) pair.getPrivate();
+ clientPublic = (DHPublicKey) pair.getPublic();
+ } catch (NoSuchAlgorithmException e) {
+ throw (IOException) new IOException("No DH keypair generator").initCause(e);
+ } catch (InvalidAlgorithmParameterException e) {
+ throw (IOException) new IOException("Invalid DH parameters").initCause(e);
+ }
+ }
+
+ @Override
+ public byte[] getE() {
+ if (clientPublic == null)
+ throw new IllegalStateException("DhExchange not initialized!");
+
+ return clientPublic.getY().toByteArray();
+ }
+
+ @Override
+ protected byte[] getServerE() {
+ if (serverPublic == null)
+ throw new IllegalStateException("DhExchange not initialized!");
+
+ return serverPublic.getY().toByteArray();
+ }
+
+ @Override
+ public void setF(byte[] f) throws IOException {
+ if (clientPublic == null)
+ throw new IllegalStateException("DhExchange not initialized!");
+
+ final KeyAgreement ka;
+ try {
+ KeyFactory kf = KeyFactory.getInstance("DH");
+ DHParameterSpec params = clientPublic.getParams();
+ this.serverPublic = (DHPublicKey) kf.generatePublic(new DHPublicKeySpec(
+ new BigInteger(f), params.getP(), params.getG()));
+
+ ka = KeyAgreement.getInstance("DH");
+ ka.init(clientPrivate);
+ ka.doPhase(serverPublic, true);
+ } catch (NoSuchAlgorithmException e) {
+ throw (IOException) new IOException("No DH key agreement method").initCause(e);
+ } catch (InvalidKeyException e) {
+ throw (IOException) new IOException("Invalid DH key").initCause(e);
+ } catch (InvalidKeySpecException e) {
+ throw (IOException) new IOException("Invalid DH key").initCause(e);
+ }
+
+ sharedSecret = new BigInteger(ka.generateSecret());
+ }
+
+ @Override
+ public String getHashAlgo() {
+ return "SHA1";
+ }
+}
diff --git a/src/com/trilead/ssh2/crypto/dh/DhGroupExchange.java b/src/com/trilead/ssh2/crypto/dh/DhGroupExchange.java
index 8e798ee..a888950 100644
--- a/src/com/trilead/ssh2/crypto/dh/DhGroupExchange.java
+++ b/src/com/trilead/ssh2/crypto/dh/DhGroupExchange.java
@@ -1,113 +1,113 @@
-
-package com.trilead.ssh2.crypto.dh;
-
-import java.math.BigInteger;
-import java.security.SecureRandom;
-
-import com.trilead.ssh2.DHGexParameters;
-import com.trilead.ssh2.crypto.digest.HashForSSH2Types;
-
-
-/**
- * DhGroupExchange.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: DhGroupExchange.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public class DhGroupExchange
-{
- /* Given by the standard */
-
- private BigInteger p;
- private BigInteger g;
-
- /* Client public and private */
-
- private BigInteger e;
- private BigInteger x;
-
- /* Server public */
-
- private BigInteger f;
-
- /* Shared secret */
-
- private BigInteger k;
-
- public DhGroupExchange(BigInteger p, BigInteger g)
- {
- this.p = p;
- this.g = g;
- }
-
- public void init(SecureRandom rnd)
- {
- k = null;
-
- x = new BigInteger(p.bitLength() - 1, rnd);
- e = g.modPow(x, p);
- }
-
- /**
- * @return Returns the e.
- */
- public BigInteger getE()
- {
- if (e == null)
- throw new IllegalStateException("Not initialized!");
-
- return e;
- }
-
- /**
- * @return Returns the shared secret k.
- */
- public BigInteger getK()
- {
- if (k == null)
- throw new IllegalStateException("Shared secret not yet known, need f first!");
-
- return k;
- }
-
- /**
- * Sets f and calculates the shared secret.
- */
- public void setF(BigInteger f)
- {
- if (e == null)
- throw new IllegalStateException("Not initialized!");
-
- BigInteger zero = BigInteger.valueOf(0);
-
- if (zero.compareTo(f) >= 0 || p.compareTo(f) <= 0)
- throw new IllegalArgumentException("Invalid f specified!");
-
- this.f = f;
- this.k = f.modPow(x, p);
- }
-
- public byte[] calculateH(byte[] clientversion, byte[] serverversion, byte[] clientKexPayload,
- byte[] serverKexPayload, byte[] hostKey, DHGexParameters para)
- {
- HashForSSH2Types hash = new HashForSSH2Types("SHA1");
-
- hash.updateByteString(clientversion);
- hash.updateByteString(serverversion);
- hash.updateByteString(clientKexPayload);
- hash.updateByteString(serverKexPayload);
- hash.updateByteString(hostKey);
- if (para.getMin_group_len() > 0)
- hash.updateUINT32(para.getMin_group_len());
- hash.updateUINT32(para.getPref_group_len());
- if (para.getMax_group_len() > 0)
- hash.updateUINT32(para.getMax_group_len());
- hash.updateBigInt(p);
- hash.updateBigInt(g);
- hash.updateBigInt(e);
- hash.updateBigInt(f);
- hash.updateBigInt(k);
-
- return hash.getDigest();
- }
-}
+
+package com.trilead.ssh2.crypto.dh;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import com.trilead.ssh2.DHGexParameters;
+import com.trilead.ssh2.crypto.digest.HashForSSH2Types;
+
+
+/**
+ * DhGroupExchange.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: DhGroupExchange.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
+ */
+public class DhGroupExchange
+{
+ /* Given by the standard */
+
+ private BigInteger p;
+ private BigInteger g;
+
+ /* Client public and private */
+
+ private BigInteger e;
+ private BigInteger x;
+
+ /* Server public */
+
+ private BigInteger f;
+
+ /* Shared secret */
+
+ private BigInteger k;
+
+ public DhGroupExchange(BigInteger p, BigInteger g)
+ {
+ this.p = p;
+ this.g = g;
+ }
+
+ public void init(SecureRandom rnd)
+ {
+ k = null;
+
+ x = new BigInteger(p.bitLength() - 1, rnd);
+ e = g.modPow(x, p);
+ }
+
+ /**
+ * @return Returns the e.
+ */
+ public BigInteger getE()
+ {
+ if (e == null)
+ throw new IllegalStateException("Not initialized!");
+
+ return e;
+ }
+
+ /**
+ * @return Returns the shared secret k.
+ */
+ public BigInteger getK()
+ {
+ if (k == null)
+ throw new IllegalStateException("Shared secret not yet known, need f first!");
+
+ return k;
+ }
+
+ /**
+ * Sets f and calculates the shared secret.
+ */
+ public void setF(BigInteger f)
+ {
+ if (e == null)
+ throw new IllegalStateException("Not initialized!");
+
+ BigInteger zero = BigInteger.valueOf(0);
+
+ if (zero.compareTo(f) >= 0 || p.compareTo(f) <= 0)
+ throw new IllegalArgumentException("Invalid f specified!");
+
+ this.f = f;
+ this.k = f.modPow(x, p);
+ }
+
+ public byte[] calculateH(String hashAlgo, byte[] clientversion, byte[] serverversion,
+ byte[] clientKexPayload, byte[] serverKexPayload, byte[] hostKey, DHGexParameters para)
+ {
+ HashForSSH2Types hash = new HashForSSH2Types(hashAlgo);
+
+ hash.updateByteString(clientversion);
+ hash.updateByteString(serverversion);
+ hash.updateByteString(clientKexPayload);
+ hash.updateByteString(serverKexPayload);
+ hash.updateByteString(hostKey);
+ if (para.getMin_group_len() > 0)
+ hash.updateUINT32(para.getMin_group_len());
+ hash.updateUINT32(para.getPref_group_len());
+ if (para.getMax_group_len() > 0)
+ hash.updateUINT32(para.getMax_group_len());
+ hash.updateBigInt(p);
+ hash.updateBigInt(g);
+ hash.updateBigInt(e);
+ hash.updateBigInt(f);
+ hash.updateBigInt(k);
+
+ return hash.getDigest();
+ }
+}
diff --git a/src/com/trilead/ssh2/crypto/dh/EcDhExchange.java b/src/com/trilead/ssh2/crypto/dh/EcDhExchange.java
new file mode 100644
index 0000000..870a3b4
--- /dev/null
+++ b/src/com/trilead/ssh2/crypto/dh/EcDhExchange.java
@@ -0,0 +1,106 @@
+/**
+ *
+ */
+package com.trilead.ssh2.crypto.dh;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+
+import javax.crypto.KeyAgreement;
+
+import com.trilead.ssh2.signature.ECDSASHA2Verify;
+
+/**
+ * @author kenny
+ *
+ */
+public class EcDhExchange extends GenericDhExchange {
+ private ECPrivateKey clientPrivate;
+ private ECPublicKey clientPublic;
+ private ECPublicKey serverPublic;
+
+ @Override
+ public void init(String name) throws IOException {
+ final ECParameterSpec spec;
+
+ if ("ecdh-sha2-nistp256".equals(name)) {
+ spec = ECDSASHA2Verify.EllipticCurves.nistp256;
+ } else if ("ecdh-sha2-nistp384".equals(name)) {
+ spec = ECDSASHA2Verify.EllipticCurves.nistp384;
+ } else if ("ecdh-sha2-nistp521".equals(name)) {
+ spec = ECDSASHA2Verify.EllipticCurves.nistp521;
+ } else {
+ throw new IllegalArgumentException("Unknown EC curve " + name);
+ }
+
+ KeyPairGenerator kpg;
+ try {
+ kpg = KeyPairGenerator.getInstance("EC");
+ kpg.initialize(spec);
+ KeyPair pair = kpg.generateKeyPair();
+ clientPrivate = (ECPrivateKey) pair.getPrivate();
+ clientPublic = (ECPublicKey) pair.getPublic();
+ } catch (NoSuchAlgorithmException e) {
+ throw (IOException) new IOException("No DH keypair generator").initCause(e);
+ } catch (InvalidAlgorithmParameterException e) {
+ throw (IOException) new IOException("Invalid DH parameters").initCause(e);
+ }
+ }
+
+ @Override
+ public byte[] getE() {
+ return ECDSASHA2Verify.encodeECPoint(clientPublic.getW(), clientPublic.getParams()
+ .getCurve());
+ }
+
+ @Override
+ protected byte[] getServerE() {
+ return ECDSASHA2Verify.encodeECPoint(serverPublic.getW(), serverPublic.getParams()
+ .getCurve());
+ }
+
+ @Override
+ public void setF(byte[] f) throws IOException {
+
+ if (clientPublic == null)
+ throw new IllegalStateException("DhDsaExchange not initialized!");
+
+ final KeyAgreement ka;
+ try {
+ KeyFactory kf = KeyFactory.getInstance("EC");
+ ECParameterSpec params = clientPublic.getParams();
+ ECPoint serverPoint = ECDSASHA2Verify.decodeECPoint(f, params.getCurve());
+ this.serverPublic = (ECPublicKey) kf.generatePublic(new ECPublicKeySpec(serverPoint,
+ params));
+
+ ka = KeyAgreement.getInstance("ECDH");
+ ka.init(clientPrivate);
+ ka.doPhase(serverPublic, true);
+ } catch (NoSuchAlgorithmException e) {
+ throw (IOException) new IOException("No ECDH key agreement method").initCause(e);
+ } catch (InvalidKeyException e) {
+ throw (IOException) new IOException("Invalid ECDH key").initCause(e);
+ } catch (InvalidKeySpecException e) {
+ throw (IOException) new IOException("Invalid ECDH key").initCause(e);
+ }
+
+ sharedSecret = new BigInteger(1, ka.generateSecret());
+ }
+
+ @Override
+ public String getHashAlgo() {
+ return ECDSASHA2Verify.getDigestAlgorithmForParams(clientPublic.getParams());
+ }
+}
diff --git a/src/com/trilead/ssh2/crypto/dh/GenericDhExchange.java b/src/com/trilead/ssh2/crypto/dh/GenericDhExchange.java
new file mode 100644
index 0000000..039ff75
--- /dev/null
+++ b/src/com/trilead/ssh2/crypto/dh/GenericDhExchange.java
@@ -0,0 +1,93 @@
+
+package com.trilead.ssh2.crypto.dh;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.math.BigInteger;
+
+import com.trilead.ssh2.crypto.digest.HashForSSH2Types;
+import com.trilead.ssh2.log.Logger;
+
+
+/**
+ * DhExchange.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: DhExchange.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
+ */
+public abstract class GenericDhExchange
+{
+ private static final Logger log = Logger.getLogger(GenericDhExchange.class);
+
+ /* Shared secret */
+
+ BigInteger sharedSecret;
+
+ protected GenericDhExchange()
+ {
+ }
+
+ public static GenericDhExchange getInstance(String algo) {
+ if (algo.startsWith("ecdh-sha2-")) {
+ return new EcDhExchange();
+ } else {
+ return new DhExchange();
+ }
+ }
+
+ public abstract void init(String name) throws IOException;
+
+ /**
+ * @return Returns the e (public value)
+ * @throws IllegalStateException
+ */
+ public abstract byte[] getE();
+
+ /**
+ * @return Returns the server's e (public value)
+ * @throws IllegalStateException
+ */
+ protected abstract byte[] getServerE();
+
+ /**
+ * @return Returns the shared secret k.
+ * @throws IllegalStateException
+ */
+ public BigInteger getK()
+ {
+ if (sharedSecret == null)
+ throw new IllegalStateException("Shared secret not yet known, need f first!");
+
+ return sharedSecret;
+ }
+
+ /**
+ * @param f
+ */
+ public abstract void setF(byte[] f) throws IOException;
+
+ public byte[] calculateH(byte[] clientversion, byte[] serverversion, byte[] clientKexPayload,
+ byte[] serverKexPayload, byte[] hostKey) throws UnsupportedEncodingException
+ {
+ HashForSSH2Types hash = new HashForSSH2Types(getHashAlgo());
+
+ if (log.isEnabled())
+ {
+ log.log(90, "Client: '" + new String(clientversion, "ISO-8859-1") + "'");
+ log.log(90, "Server: '" + new String(serverversion, "ISO-8859-1") + "'");
+ }
+
+ hash.updateByteString(clientversion);
+ hash.updateByteString(serverversion);
+ hash.updateByteString(clientKexPayload);
+ hash.updateByteString(serverKexPayload);
+ hash.updateByteString(hostKey);
+ hash.updateByteString(getE());
+ hash.updateByteString(getServerE());
+ hash.updateBigInt(sharedSecret);
+
+ return hash.getDigest();
+ }
+
+ public abstract String getHashAlgo();
+}
diff --git a/src/com/trilead/ssh2/crypto/digest/Digest.java b/src/com/trilead/ssh2/crypto/digest/Digest.java
deleted file mode 100644
index 6ed969c..0000000
--- a/src/com/trilead/ssh2/crypto/digest/Digest.java
+++ /dev/null
@@ -1,25 +0,0 @@
-
-package com.trilead.ssh2.crypto.digest;
-
-/**
- * Digest.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: Digest.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public interface Digest
-{
- public int getDigestLength();
-
- public void update(byte b);
-
- public void update(byte[] b);
-
- public void update(byte b[], int off, int len);
-
- public void reset();
-
- public void digest(byte[] out);
-
- public void digest(byte[] out, int off);
-}
diff --git a/src/com/trilead/ssh2/crypto/digest/HMAC.java b/src/com/trilead/ssh2/crypto/digest/HMAC.java
deleted file mode 100644
index 8d52758..0000000
--- a/src/com/trilead/ssh2/crypto/digest/HMAC.java
+++ /dev/null
@@ -1,95 +0,0 @@
-
-package com.trilead.ssh2.crypto.digest;
-
-/**
- * HMAC.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: HMAC.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public final class HMAC implements Digest
-{
- Digest md;
- byte[] k_xor_ipad;
- byte[] k_xor_opad;
-
- byte[] tmp;
-
- int size;
-
- public HMAC(Digest md, byte[] key, int size)
- {
- this.md = md;
- this.size = size;
-
- tmp = new byte[md.getDigestLength()];
-
- final int BLOCKSIZE = 64;
-
- k_xor_ipad = new byte[BLOCKSIZE];
- k_xor_opad = new byte[BLOCKSIZE];
-
- if (key.length > BLOCKSIZE)
- {
- md.reset();
- md.update(key);
- md.digest(tmp);
- key = tmp;
- }
-
- System.arraycopy(key, 0, k_xor_ipad, 0, key.length);
- System.arraycopy(key, 0, k_xor_opad, 0, key.length);
-
- for (int i = 0; i < BLOCKSIZE; i++)
- {
- k_xor_ipad[i] ^= 0x36;
- k_xor_opad[i] ^= 0x5C;
- }
- md.update(k_xor_ipad);
- }
-
- public final int getDigestLength()
- {
- return size;
- }
-
- public final void update(byte b)
- {
- md.update(b);
- }
-
- public final void update(byte[] b)
- {
- md.update(b);
- }
-
- public final void update(byte[] b, int off, int len)
- {
- md.update(b, off, len);
- }
-
- public final void reset()
- {
- md.reset();
- md.update(k_xor_ipad);
- }
-
- public final void digest(byte[] out)
- {
- digest(out, 0);
- }
-
- public final void digest(byte[] out, int off)
- {
- md.digest(tmp);
-
- md.update(k_xor_opad);
- md.update(tmp);
-
- md.digest(tmp);
-
- System.arraycopy(tmp, 0, out, off, size);
-
- md.update(k_xor_ipad);
- }
-}
diff --git a/src/com/trilead/ssh2/crypto/digest/HashForSSH2Types.java b/src/com/trilead/ssh2/crypto/digest/HashForSSH2Types.java
index df84952..6b0d6e3 100644
--- a/src/com/trilead/ssh2/crypto/digest/HashForSSH2Types.java
+++ b/src/com/trilead/ssh2/crypto/digest/HashForSSH2Types.java
@@ -1,93 +1,91 @@
-
-package com.trilead.ssh2.crypto.digest;
-
-import java.math.BigInteger;
-
-/**
- * HashForSSH2Types.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: HashForSSH2Types.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public class HashForSSH2Types
-{
- Digest md;
-
- public HashForSSH2Types(Digest md)
- {
- this.md = md;
- }
-
- public HashForSSH2Types(String type)
- {
- if (type.equals("SHA1"))
- {
- md = new SHA1();
- }
- else if (type.equals("MD5"))
- {
- md = new MD5();
- }
- else
- throw new IllegalArgumentException("Unknown algorithm " + type);
- }
-
- public void updateByte(byte b)
- {
- /* HACK - to test it with J2ME */
- byte[] tmp = new byte[1];
- tmp[0] = b;
- md.update(tmp);
- }
-
- public void updateBytes(byte[] b)
- {
- md.update(b);
- }
-
- public void updateUINT32(int v)
- {
- md.update((byte) (v >> 24));
- md.update((byte) (v >> 16));
- md.update((byte) (v >> 8));
- md.update((byte) (v));
- }
-
- public void updateByteString(byte[] b)
- {
- updateUINT32(b.length);
- updateBytes(b);
- }
-
- public void updateBigInt(BigInteger b)
- {
- updateByteString(b.toByteArray());
- }
-
- public void reset()
- {
- md.reset();
- }
-
- public int getDigestLength()
- {
- return md.getDigestLength();
- }
-
- public byte[] getDigest()
- {
- byte[] tmp = new byte[md.getDigestLength()];
- getDigest(tmp);
- return tmp;
- }
-
- public void getDigest(byte[] out)
- {
- getDigest(out, 0);
- }
-
- public void getDigest(byte[] out, int off)
- {
- md.digest(out, off);
- }
-}
+
+package com.trilead.ssh2.crypto.digest;
+
+import java.math.BigInteger;
+import java.security.DigestException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * HashForSSH2Types.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: HashForSSH2Types.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
+ */
+public class HashForSSH2Types
+{
+ MessageDigest md;
+
+ public HashForSSH2Types(String type)
+ {
+ try {
+ md = MessageDigest.getInstance(type);
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException("Unsupported algorithm " + type);
+ }
+ }
+
+ public void updateByte(byte b)
+ {
+ /* HACK - to test it with J2ME */
+ byte[] tmp = new byte[1];
+ tmp[0] = b;
+ md.update(tmp);
+ }
+
+ public void updateBytes(byte[] b)
+ {
+ md.update(b);
+ }
+
+ public void updateUINT32(int v)
+ {
+ md.update((byte) (v >> 24));
+ md.update((byte) (v >> 16));
+ md.update((byte) (v >> 8));
+ md.update((byte) (v));
+ }
+
+ public void updateByteString(byte[] b)
+ {
+ updateUINT32(b.length);
+ updateBytes(b);
+ }
+
+ public void updateBigInt(BigInteger b)
+ {
+ updateByteString(b.toByteArray());
+ }
+
+ public void reset()
+ {
+ md.reset();
+ }
+
+ public int getDigestLength()
+ {
+ return md.getDigestLength();
+ }
+
+ public byte[] getDigest()
+ {
+ byte[] tmp = new byte[md.getDigestLength()];
+ getDigest(tmp);
+ return tmp;
+ }
+
+ public void getDigest(byte[] out)
+ {
+ getDigest(out, 0);
+ }
+
+ public void getDigest(byte[] out, int off)
+ {
+ try {
+ md.digest(out, off, out.length - off);
+ } catch (DigestException e) {
+ // TODO is this right?!
+ throw new RuntimeException("Unable to digest", e);
+ }
+ }
+}
diff --git a/src/com/trilead/ssh2/crypto/digest/MAC.java b/src/com/trilead/ssh2/crypto/digest/MAC.java
index 0433b63..561599c 100644
--- a/src/com/trilead/ssh2/crypto/digest/MAC.java
+++ b/src/com/trilead/ssh2/crypto/digest/MAC.java
@@ -1,88 +1,157 @@
-
-package com.trilead.ssh2.crypto.digest;
-
-/**
- * MAC.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: MAC.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public final class MAC
-{
- Digest mac;
- int size;
-
- public final static String[] getMacList()
- {
- /* Higher Priority First */
-
- return new String[] { "hmac-sha1-96", "hmac-sha1", "hmac-md5-96", "hmac-md5" };
- }
-
- public final static void checkMacList(String[] macs)
- {
- for (int i = 0; i < macs.length; i++)
- getKeyLen(macs[i]);
- }
-
- public final static int getKeyLen(String type)
- {
- if (type.equals("hmac-sha1"))
- return 20;
- if (type.equals("hmac-sha1-96"))
- return 20;
- if (type.equals("hmac-md5"))
- return 16;
- if (type.equals("hmac-md5-96"))
- return 16;
- throw new IllegalArgumentException("Unkown algorithm " + type);
- }
-
- public MAC(String type, byte[] key)
- {
- if (type.equals("hmac-sha1"))
- {
- mac = new HMAC(new SHA1(), key, 20);
- }
- else if (type.equals("hmac-sha1-96"))
- {
- mac = new HMAC(new SHA1(), key, 12);
- }
- else if (type.equals("hmac-md5"))
- {
- mac = new HMAC(new MD5(), key, 16);
- }
- else if (type.equals("hmac-md5-96"))
- {
- mac = new HMAC(new MD5(), key, 12);
- }
- else
- throw new IllegalArgumentException("Unkown algorithm " + type);
-
- size = mac.getDigestLength();
- }
-
- public final void initMac(int seq)
- {
- mac.reset();
- mac.update((byte) (seq >> 24));
- mac.update((byte) (seq >> 16));
- mac.update((byte) (seq >> 8));
- mac.update((byte) (seq));
- }
-
- public final void update(byte[] packetdata, int off, int len)
- {
- mac.update(packetdata, off, len);
- }
-
- public final void getMac(byte[] out, int off)
- {
- mac.digest(out, off);
- }
-
- public final int size()
- {
- return size;
- }
-}
+
+package com.trilead.ssh2.crypto.digest;
+
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+import javax.crypto.Mac;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * MAC.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: MAC.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
+ */
+public final class MAC
+{
+ /**
+ * From http://tools.ietf.org/html/rfc4253
+ */
+ private static final String HMAC_MD5 = "hmac-md5";
+
+ /**
+ * From http://tools.ietf.org/html/rfc4253
+ */
+ private static final String HMAC_MD5_96 = "hmac-md5-96";
+
+ /**
+ * From http://tools.ietf.org/html/rfc4253
+ */
+ private static final String HMAC_SHA1 = "hmac-sha1";
+
+ /**
+ * From http://tools.ietf.org/html/rfc4253
+ */
+ private static final String HMAC_SHA1_96 = "hmac-sha1-96";
+
+ /**
+ * From http://tools.ietf.org/html/rfc6668
+ */
+ private static final String HMAC_SHA2_256 = "hmac-sha2-256";
+
+ /**
+ * From http://tools.ietf.org/html/rfc6668
+ */
+ private static final String HMAC_SHA2_512 = "hmac-sha2-512";
+
+ Mac mac;
+ int outSize;
+ int macSize;
+ byte[] buffer;
+
+ /* Higher Priority First */
+ private static final String[] MAC_LIST = {
+ HMAC_SHA2_256, HMAC_SHA2_512,
+ HMAC_SHA1_96, HMAC_SHA1, HMAC_MD5_96, HMAC_MD5
+ };
+
+ public final static String[] getMacList()
+ {
+ return MAC_LIST;
+ }
+
+ public final static void checkMacList(String[] macs)
+ {
+ for (int i = 0; i < macs.length; i++)
+ getKeyLen(macs[i]);
+ }
+
+ public final static int getKeyLen(String type)
+ {
+ if (HMAC_SHA1.equals(type) || HMAC_SHA1_96.equals(type))
+ return 20;
+ if (HMAC_MD5.equals(type) || HMAC_MD5_96.equals(type))
+ return 16;
+ if (HMAC_SHA2_256.equals(type))
+ return 32;
+ if (HMAC_SHA2_512.equals(type))
+ return 64;
+ throw new IllegalArgumentException("Unkown algorithm " + type);
+ }
+
+ public MAC(String type, byte[] key)
+ {
+ try {
+ if (HMAC_SHA1.equals(type) || HMAC_SHA1_96.equals(type))
+ {
+ mac = Mac.getInstance("HmacSHA1");
+ }
+ else if (HMAC_MD5.equals(type) || HMAC_MD5_96.equals(type))
+ {
+ mac = Mac.getInstance("HmacMD5");
+ }
+ else if (HMAC_SHA2_256.equals(type))
+ {
+ mac = Mac.getInstance("HmacSHA256");
+ }
+ else if (HMAC_SHA2_512.equals(type))
+ {
+ mac = Mac.getInstance("HmacSHA512");
+ }
+ else
+ throw new IllegalArgumentException("Unkown algorithm " + type);
+ } catch (NoSuchAlgorithmException e) {
+ throw new IllegalArgumentException("Unknown algorithm " + type, e);
+ }
+
+ macSize = mac.getMacLength();
+ if (type.endsWith("-96")) {
+ outSize = 12;
+ buffer = new byte[macSize];
+ } else {
+ outSize = macSize;
+ buffer = null;
+ }
+
+ try {
+ mac.init(new SecretKeySpec(key, type));
+ } catch (InvalidKeyException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ public final void initMac(int seq)
+ {
+ mac.reset();
+ mac.update((byte) (seq >> 24));
+ mac.update((byte) (seq >> 16));
+ mac.update((byte) (seq >> 8));
+ mac.update((byte) (seq));
+ }
+
+ public final void update(byte[] packetdata, int off, int len)
+ {
+ mac.update(packetdata, off, len);
+ }
+
+ public final void getMac(byte[] out, int off)
+ {
+ try {
+ if (buffer != null) {
+ mac.doFinal(buffer, 0);
+ System.arraycopy(buffer, 0, out, off, out.length - off);
+ } else {
+ mac.doFinal(out, off);
+ }
+ } catch (ShortBufferException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ public final int size()
+ {
+ return outSize;
+ }
+}
diff --git a/src/com/trilead/ssh2/crypto/digest/MD5.java b/src/com/trilead/ssh2/crypto/digest/MD5.java
deleted file mode 100644
index 567f926..0000000
--- a/src/com/trilead/ssh2/crypto/digest/MD5.java
+++ /dev/null
@@ -1,268 +0,0 @@
-
-package com.trilead.ssh2.crypto.digest;
-
-/**
- * MD5. Based on the example code in RFC 1321. Optimized (...a little).
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: MD5.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-
-/*
- * The following disclaimer has been copied from RFC 1321:
- *
- * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights
- * reserved.
- *
- * License to copy and use this software is granted provided that it is
- * identified as the "RSA Data Security, Inc. MD5 Message-Digest Algorithm" in
- * all material mentioning or referencing this software or this function.
- *
- * License is also granted to make and use derivative works provided that such
- * works are identified as "derived from the RSA Data Security, Inc. MD5
- * Message-Digest Algorithm" in all material mentioning or referencing the
- * derived work.
- *
- * RSA Data Security, Inc. makes no representations concerning either the
- * merchantability of this software or the suitability of this software for any
- * particular purpose. It is provided "as is" without express or implied
- * warranty of any kind.
- *
- * These notices must be retained in any copies of any part of this
- * documentation and/or software.
- *
- */
-
-public final class MD5 implements Digest
-{
- private int state0, state1, state2, state3;
- private long count;
- private final byte[] block = new byte[64];
- private final int x[] = new int[16];
-
- private static final byte[] padding = new byte[] { (byte) 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
-
- public MD5()
- {
- reset();
- }
-
- private static final int FF(int a, int b, int c, int d, int x, int s, int ac)
- {
- a += ((b & c) | ((~b) & d)) + x + ac;
- return ((a << s) | (a >>> (32 - s))) + b;
- }
-
- private static final int GG(int a, int b, int c, int d, int x, int s, int ac)
- {
- a += ((b & d) | (c & (~d))) + x + ac;
- return ((a << s) | (a >>> (32 - s))) + b;
- }
-
- private static final int HH(int a, int b, int c, int d, int x, int s, int ac)
- {
- a += (b ^ c ^ d) + x + ac;
- return ((a << s) | (a >>> (32 - s))) + b;
- }
-
- private static final int II(int a, int b, int c, int d, int x, int s, int ac)
- {
- a += (c ^ (b | (~d))) + x + ac;
- return ((a << s) | (a >>> (32 - s))) + b;
- }
-
- private static final void encode(byte[] dst, int dstoff, int word)
- {
- dst[dstoff] = (byte) (word);
- dst[dstoff + 1] = (byte) (word >> 8);
- dst[dstoff + 2] = (byte) (word >> 16);
- dst[dstoff + 3] = (byte) (word >> 24);
- }
-
- private final void transform(byte[] src, int pos)
- {
- int a = state0;
- int b = state1;
- int c = state2;
- int d = state3;
-
- for (int i = 0; i < 16; i++, pos += 4)
- {
- x[i] = (src[pos] & 0xff) | ((src[pos + 1] & 0xff) << 8) | ((src[pos + 2] & 0xff) << 16)
- | ((src[pos + 3] & 0xff) << 24);
- }
-
- /* Round 1 */
-
- a = FF(a, b, c, d, x[0], 7, 0xd76aa478); /* 1 */
- d = FF(d, a, b, c, x[1], 12, 0xe8c7b756); /* 2 */
- c = FF(c, d, a, b, x[2], 17, 0x242070db); /* 3 */
- b = FF(b, c, d, a, x[3], 22, 0xc1bdceee); /* 4 */
- a = FF(a, b, c, d, x[4], 7, 0xf57c0faf); /* 5 */
- d = FF(d, a, b, c, x[5], 12, 0x4787c62a); /* 6 */
- c = FF(c, d, a, b, x[6], 17, 0xa8304613); /* 7 */
- b = FF(b, c, d, a, x[7], 22, 0xfd469501); /* 8 */
- a = FF(a, b, c, d, x[8], 7, 0x698098d8); /* 9 */
- d = FF(d, a, b, c, x[9], 12, 0x8b44f7af); /* 10 */
- c = FF(c, d, a, b, x[10], 17, 0xffff5bb1); /* 11 */
- b = FF(b, c, d, a, x[11], 22, 0x895cd7be); /* 12 */
- a = FF(a, b, c, d, x[12], 7, 0x6b901122); /* 13 */
- d = FF(d, a, b, c, x[13], 12, 0xfd987193); /* 14 */
- c = FF(c, d, a, b, x[14], 17, 0xa679438e); /* 15 */
- b = FF(b, c, d, a, x[15], 22, 0x49b40821); /* 16 */
-
- /* Round 2 */
- a = GG(a, b, c, d, x[1], 5, 0xf61e2562); /* 17 */
- d = GG(d, a, b, c, x[6], 9, 0xc040b340); /* 18 */
- c = GG(c, d, a, b, x[11], 14, 0x265e5a51); /* 19 */
- b = GG(b, c, d, a, x[0], 20, 0xe9b6c7aa); /* 20 */
- a = GG(a, b, c, d, x[5], 5, 0xd62f105d); /* 21 */
- d = GG(d, a, b, c, x[10], 9, 0x2441453); /* 22 */
- c = GG(c, d, a, b, x[15], 14, 0xd8a1e681); /* 23 */
- b = GG(b, c, d, a, x[4], 20, 0xe7d3fbc8); /* 24 */
- a = GG(a, b, c, d, x[9], 5, 0x21e1cde6); /* 25 */
- d = GG(d, a, b, c, x[14], 9, 0xc33707d6); /* 26 */
- c = GG(c, d, a, b, x[3], 14, 0xf4d50d87); /* 27 */
- b = GG(b, c, d, a, x[8], 20, 0x455a14ed); /* 28 */
- a = GG(a, b, c, d, x[13], 5, 0xa9e3e905); /* 29 */
- d = GG(d, a, b, c, x[2], 9, 0xfcefa3f8); /* 30 */
- c = GG(c, d, a, b, x[7], 14, 0x676f02d9); /* 31 */
- b = GG(b, c, d, a, x[12], 20, 0x8d2a4c8a); /* 32 */
-
- /* Round 3 */
- a = HH(a, b, c, d, x[5], 4, 0xfffa3942); /* 33 */
- d = HH(d, a, b, c, x[8], 11, 0x8771f681); /* 34 */
- c = HH(c, d, a, b, x[11], 16, 0x6d9d6122); /* 35 */
- b = HH(b, c, d, a, x[14], 23, 0xfde5380c); /* 36 */
- a = HH(a, b, c, d, x[1], 4, 0xa4beea44); /* 37 */
- d = HH(d, a, b, c, x[4], 11, 0x4bdecfa9); /* 38 */
- c = HH(c, d, a, b, x[7], 16, 0xf6bb4b60); /* 39 */
- b = HH(b, c, d, a, x[10], 23, 0xbebfbc70); /* 40 */
- a = HH(a, b, c, d, x[13], 4, 0x289b7ec6); /* 41 */
- d = HH(d, a, b, c, x[0], 11, 0xeaa127fa); /* 42 */
- c = HH(c, d, a, b, x[3], 16, 0xd4ef3085); /* 43 */
- b = HH(b, c, d, a, x[6], 23, 0x4881d05); /* 44 */
- a = HH(a, b, c, d, x[9], 4, 0xd9d4d039); /* 45 */
- d = HH(d, a, b, c, x[12], 11, 0xe6db99e5); /* 46 */
- c = HH(c, d, a, b, x[15], 16, 0x1fa27cf8); /* 47 */
- b = HH(b, c, d, a, x[2], 23, 0xc4ac5665); /* 48 */
-
- /* Round 4 */
- a = II(a, b, c, d, x[0], 6, 0xf4292244); /* 49 */
- d = II(d, a, b, c, x[7], 10, 0x432aff97); /* 50 */
- c = II(c, d, a, b, x[14], 15, 0xab9423a7); /* 51 */
- b = II(b, c, d, a, x[5], 21, 0xfc93a039); /* 52 */
- a = II(a, b, c, d, x[12], 6, 0x655b59c3); /* 53 */
- d = II(d, a, b, c, x[3], 10, 0x8f0ccc92); /* 54 */
- c = II(c, d, a, b, x[10], 15, 0xffeff47d); /* 55 */
- b = II(b, c, d, a, x[1], 21, 0x85845dd1); /* 56 */
- a = II(a, b, c, d, x[8], 6, 0x6fa87e4f); /* 57 */
- d = II(d, a, b, c, x[15], 10, 0xfe2ce6e0); /* 58 */
- c = II(c, d, a, b, x[6], 15, 0xa3014314); /* 59 */
- b = II(b, c, d, a, x[13], 21, 0x4e0811a1); /* 60 */
- a = II(a, b, c, d, x[4], 6, 0xf7537e82); /* 61 */
- d = II(d, a, b, c, x[11], 10, 0xbd3af235); /* 62 */
- c = II(c, d, a, b, x[2], 15, 0x2ad7d2bb); /* 63 */
- b = II(b, c, d, a, x[9], 21, 0xeb86d391); /* 64 */
-
- state0 += a;
- state1 += b;
- state2 += c;
- state3 += d;
- }
-
- public final void reset()
- {
- count = 0;
-
- state0 = 0x67452301;
- state1 = 0xefcdab89;
- state2 = 0x98badcfe;
- state3 = 0x10325476;
-
- /* Clear traces in memory... */
-
- for (int i = 0; i < 16; i++)
- x[i] = 0;
- }
-
- public final void update(byte b)
- {
- final int space = 64 - ((int) (count & 0x3f));
-
- count++;
-
- block[64 - space] = b;
-
- if (space == 1)
- transform(block, 0);
- }
-
- public final void update(byte[] buff, int pos, int len)
- {
- int space = 64 - ((int) (count & 0x3f));
-
- count += len;
-
- while (len > 0)
- {
- if (len < space)
- {
- System.arraycopy(buff, pos, block, 64 - space, len);
- break;
- }
-
- if (space == 64)
- {
- transform(buff, pos);
- }
- else
- {
- System.arraycopy(buff, pos, block, 64 - space, space);
- transform(block, 0);
- }
-
- pos += space;
- len -= space;
- space = 64;
- }
- }
-
- public final void update(byte[] b)
- {
- update(b, 0, b.length);
- }
-
- public final void digest(byte[] dst, int pos)
- {
- byte[] bits = new byte[8];
-
- encode(bits, 0, (int) (count << 3));
- encode(bits, 4, (int) (count >> 29));
-
- int idx = (int) count & 0x3f;
- int padLen = (idx < 56) ? (56 - idx) : (120 - idx);
-
- update(padding, 0, padLen);
- update(bits, 0, 8);
-
- encode(dst, pos, state0);
- encode(dst, pos + 4, state1);
- encode(dst, pos + 8, state2);
- encode(dst, pos + 12, state3);
-
- reset();
- }
-
- public final void digest(byte[] dst)
- {
- digest(dst, 0);
- }
-
- public final int getDigestLength()
- {
- return 16;
- }
-}
diff --git a/src/com/trilead/ssh2/crypto/digest/SHA1.java b/src/com/trilead/ssh2/crypto/digest/SHA1.java
deleted file mode 100644
index bba4cc6..0000000
--- a/src/com/trilead/ssh2/crypto/digest/SHA1.java
+++ /dev/null
@@ -1,664 +0,0 @@
-
-package com.trilead.ssh2.crypto.digest;
-
-/**
- * SHA-1 implementation based on FIPS PUB 180-1.
- * Highly optimized.
- * <p>
- * (http://www.itl.nist.gov/fipspubs/fip180-1.htm)
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: SHA1.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public final class SHA1 implements Digest
-{
- private int H0, H1, H2, H3, H4;
-
- private final int[] w = new int[80];
- private int currentPos;
- private long currentLen;
-
- public SHA1()
- {
- reset();
- }
-
- public final int getDigestLength()
- {
- return 20;
- }
-
- public final void reset()
- {
- H0 = 0x67452301;
- H1 = 0xEFCDAB89;
- H2 = 0x98BADCFE;
- H3 = 0x10325476;
- H4 = 0xC3D2E1F0;
-
- currentPos = 0;
- currentLen = 0;
-
- /* In case of complete paranoia, we should also wipe out the
- * information contained in the w[] array */
- }
-
- public final void update(byte b[])
- {
- update(b, 0, b.length);
- }
-
- public final void update(byte b[], int off, int len)
- {
- if (len >= 4)
- {
- int idx = currentPos >> 2;
-
- switch (currentPos & 3)
- {
- case 0:
- w[idx] = (((b[off++] & 0xff) << 24) | ((b[off++] & 0xff) << 16) | ((b[off++] & 0xff) << 8) | (b[off++] & 0xff));
- len -= 4;
- currentPos += 4;
- currentLen += 32;
- if (currentPos == 64)
- {
- perform();
- currentPos = 0;
- }
- break;
- case 1:
- w[idx] = (w[idx] << 24) | (((b[off++] & 0xff) << 16) | ((b[off++] & 0xff) << 8) | (b[off++] & 0xff));
- len -= 3;
- currentPos += 3;
- currentLen += 24;
- if (currentPos == 64)
- {
- perform();
- currentPos = 0;
- }
- break;
- case 2:
- w[idx] = (w[idx] << 16) | (((b[off++] & 0xff) << 8) | (b[off++] & 0xff));
- len -= 2;
- currentPos += 2;
- currentLen += 16;
- if (currentPos == 64)
- {
- perform();
- currentPos = 0;
- }
- break;
- case 3:
- w[idx] = (w[idx] << 8) | (b[off++] & 0xff);
- len--;
- currentPos++;
- currentLen += 8;
- if (currentPos == 64)
- {
- perform();
- currentPos = 0;
- }
- break;
- }
-
- /* Now currentPos is a multiple of 4 - this is the place to be...*/
-
- while (len >= 8)
- {
- w[currentPos >> 2] = ((b[off++] & 0xff) << 24) | ((b[off++] & 0xff) << 16) | ((b[off++] & 0xff) << 8)
- | (b[off++] & 0xff);
- currentPos += 4;
-
- if (currentPos == 64)
- {
- perform();
- currentPos = 0;
- }
-
- w[currentPos >> 2] = ((b[off++] & 0xff) << 24) | ((b[off++] & 0xff) << 16) | ((b[off++] & 0xff) << 8)
- | (b[off++] & 0xff);
-
- currentPos += 4;
-
- if (currentPos == 64)
- {
- perform();
- currentPos = 0;
- }
-
- currentLen += 64;
- len -= 8;
- }
-
- while (len < 0) //(len >= 4)
- {
- w[currentPos >> 2] = ((b[off++] & 0xff) << 24) | ((b[off++] & 0xff) << 16) | ((b[off++] & 0xff) << 8)
- | (b[off++] & 0xff);
- len -= 4;
- currentPos += 4;
- currentLen += 32;
- if (currentPos == 64)
- {
- perform();
- currentPos = 0;
- }
- }
- }
-
- /* Remaining bytes (1-3) */
-
- while (len > 0)
- {
- /* Here is room for further improvements */
- int idx = currentPos >> 2;
- w[idx] = (w[idx] << 8) | (b[off++] & 0xff);
-
- currentLen += 8;
- currentPos++;
-
- if (currentPos == 64)
- {
- perform();
- currentPos = 0;
- }
- len--;
- }
- }
-
- public final void update(byte b)
- {
- int idx = currentPos >> 2;
- w[idx] = (w[idx] << 8) | (b & 0xff);
-
- currentLen += 8;
- currentPos++;
-
- if (currentPos == 64)
- {
- perform();
- currentPos = 0;
- }
- }
-
- private final void putInt(byte[] b, int pos, int val)
- {
- b[pos] = (byte) (val >> 24);
- b[pos + 1] = (byte) (val >> 16);
- b[pos + 2] = (byte) (val >> 8);
- b[pos + 3] = (byte) val;
- }
-
- public final void digest(byte[] out)
- {
- digest(out, 0);
- }
-
- public final void digest(byte[] out, int off)
- {
- /* Pad with a '1' and 7-31 zero bits... */
-
- int idx = currentPos >> 2;
- w[idx] = ((w[idx] << 8) | (0x80)) << ((3 - (currentPos & 3)) << 3);
-
- currentPos = (currentPos & ~3) + 4;
-
- if (currentPos == 64)
- {
- currentPos = 0;
- perform();
- }
- else if (currentPos == 60)
- {
- currentPos = 0;
- w[15] = 0;
- perform();
- }
-
- /* Now currentPos is a multiple of 4 and we can do the remaining
- * padding much more efficiently, furthermore we are sure
- * that currentPos <= 56.
- */
-
- for (int i = currentPos >> 2; i < 14; i++)
- w[i] = 0;
-
- w[14] = (int) (currentLen >> 32);
- w[15] = (int) currentLen;
-
- perform();
-
- putInt(out, off, H0);
- putInt(out, off + 4, H1);
- putInt(out, off + 8, H2);
- putInt(out, off + 12, H3);
- putInt(out, off + 16, H4);
-
- reset();
- }
-
- private final void perform()
- {
- for (int t = 16; t < 80; t++)
- {
- int x = w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16];
- w[t] = ((x << 1) | (x >>> 31));
- }
-
- int A = H0;
- int B = H1;
- int C = H2;
- int D = H3;
- int E = H4;
-
- /* Here we use variable substitution and loop unrolling
- *
- * === Original step:
- *
- * T = s5(A) + f(B,C,D) + E + w[0] + K;
- * E = D; D = C; C = s30(B); B = A; A = T;
- *
- * === Rewritten step:
- *
- * T = s5(A + f(B,C,D) + E + w[0] + K;
- * B = s30(B);
- * E = D; D = C; C = B; B = A; A = T;
- *
- * === Let's rewrite things, introducing new variables:
- *
- * E0 = E; D0 = D; C0 = C; B0 = B; A0 = A;
- *
- * T = s5(A0) + f(B0,C0,D0) + E0 + w[0] + K;
- * B0 = s30(B0);
- * E1 = D0; D1 = C0; C1 = B0; B1 = A0; A1 = T;
- *
- * T = s5(A1) + f(B1,C1,D1) + E1 + w[1] + K;
- * B1 = s30(B1);
- * E2 = D1; D2 = C1; C2 = B1; B2 = A1; A2 = T;
- *
- * E = E2; D = E2; C = C2; B = B2; A = A2;
- *
- * === No need for 'T', we can write into 'Ex' instead since
- * after the calculation of 'T' nobody is interested
- * in 'Ex' anymore.
- *
- * E0 = E; D0 = D; C0 = C; B0 = B; A0 = A;
- *
- * E0 = E0 + s5(A0) + f(B0,C0,D0) + w[0] + K;
- * B0 = s30(B0);
- * E1 = D0; D1 = C0; C1 = B0; B1 = A0; A1 = E0;
- *
- * E1 = E1 + s5(A1) + f(B1,C1,D1) + w[1] + K;
- * B1 = s30(B1);
- * E2 = D1; D2 = C1; C2 = B1; B2 = A1; A2 = E1;
- *
- * E = Ex; D = Ex; C = Cx; B = Bx; A = Ax;
- *
- * === Further optimization: get rid of the swap operations
- * Idea: instead of swapping the variables, swap the names of
- * the used variables in the next step:
- *
- * E0 = E; D0 = d; C0 = C; B0 = B; A0 = A;
- *
- * E0 = E0 + s5(A0) + f(B0,C0,D0) + w[0] + K;
- * B0 = s30(B0);
- * // E1 = D0; D1 = C0; C1 = B0; B1 = A0; A1 = E0;
- *
- * D0 = D0 + s5(E0) + f(A0,B0,C0) + w[1] + K;
- * A0 = s30(A0);
- * E2 = C0; D2 = B0; C2 = A0; B2 = E0; A2 = D0;
- *
- * E = E2; D = D2; C = C2; B = B2; A = A2;
- *
- * === OK, let's do this several times, also, directly
- * use A (instead of A0) and B,C,D,E.
- *
- * E = E + s5(A) + f(B,C,D) + w[0] + K;
- * B = s30(B);
- * // E1 = D; D1 = C; C1 = B; B1 = A; A1 = E;
- *
- * D = D + s5(E) + f(A,B,C) + w[1] + K;
- * A = s30(A);
- * // E2 = C; D2 = B; C2 = A; B2 = E; A2 = D;
- *
- * C = C + s5(D) + f(E,A,B) + w[2] + K;
- * E = s30(E);
- * // E3 = B; D3 = A; C3 = E; B3 = D; A3 = C;
- *
- * B = B + s5(C) + f(D,E,A) + w[3] + K;
- * D = s30(D);
- * // E4 = A; D4 = E; C4 = D; B4 = C; A4 = B;
- *
- * A = A + s5(B) + f(C,D,E) + w[4] + K;
- * C = s30(C);
- * // E5 = E; D5 = D; C5 = C; B5 = B; A5 = A;
- *
- * //E = E5; D = D5; C = C5; B = B5; A = A5;
- *
- * === Very nice, after 5 steps each variable
- * has the same contents as after 5 steps with
- * the original algorithm!
- *
- * We therefore can easily unroll each interval,
- * as the number of steps in each interval is a
- * multiple of 5 (20 steps per interval).
- */
-
- E += ((A << 5) | (A >>> 27)) + ((B & C) | ((~B) & D)) + w[0] + 0x5A827999;
- B = ((B << 30) | (B >>> 2));
-
- D += ((E << 5) | (E >>> 27)) + ((A & B) | ((~A) & C)) + w[1] + 0x5A827999;
- A = ((A << 30) | (A >>> 2));
-
- C += ((D << 5) | (D >>> 27)) + ((E & A) | ((~E) & B)) + w[2] + 0x5A827999;
- E = ((E << 30) | (E >>> 2));
-
- B += ((C << 5) | (C >>> 27)) + ((D & E) | ((~D) & A)) + w[3] + 0x5A827999;
- D = ((D << 30) | (D >>> 2));
-
- A += ((B << 5) | (B >>> 27)) + ((C & D) | ((~C) & E)) + w[4] + 0x5A827999;
- C = ((C << 30) | (C >>> 2));
-
- E += ((A << 5) | (A >>> 27)) + ((B & C) | ((~B) & D)) + w[5] + 0x5A827999;
- B = ((B << 30) | (B >>> 2));
-
- D += ((E << 5) | (E >>> 27)) + ((A & B) | ((~A) & C)) + w[6] + 0x5A827999;
- A = ((A << 30) | (A >>> 2));
-
- C += ((D << 5) | (D >>> 27)) + ((E & A) | ((~E) & B)) + w[7] + 0x5A827999;
- E = ((E << 30) | (E >>> 2));
-
- B += ((C << 5) | (C >>> 27)) + ((D & E) | ((~D) & A)) + w[8] + 0x5A827999;
- D = ((D << 30) | (D >>> 2));
-
- A += ((B << 5) | (B >>> 27)) + ((C & D) | ((~C) & E)) + w[9] + 0x5A827999;
- C = ((C << 30) | (C >>> 2));
-
- E += ((A << 5) | (A >>> 27)) + ((B & C) | ((~B) & D)) + w[10] + 0x5A827999;
- B = ((B << 30) | (B >>> 2));
-
- D += ((E << 5) | (E >>> 27)) + ((A & B) | ((~A) & C)) + w[11] + 0x5A827999;
- A = ((A << 30) | (A >>> 2));
-
- C += ((D << 5) | (D >>> 27)) + ((E & A) | ((~E) & B)) + w[12] + 0x5A827999;
- E = ((E << 30) | (E >>> 2));
-
- B += ((C << 5) | (C >>> 27)) + ((D & E) | ((~D) & A)) + w[13] + 0x5A827999;
- D = ((D << 30) | (D >>> 2));
-
- A += ((B << 5) | (B >>> 27)) + ((C & D) | ((~C) & E)) + w[14] + 0x5A827999;
- C = ((C << 30) | (C >>> 2));
-
- E += ((A << 5) | (A >>> 27)) + ((B & C) | ((~B) & D)) + w[15] + 0x5A827999;
- B = ((B << 30) | (B >>> 2));
-
- D += ((E << 5) | (E >>> 27)) + ((A & B) | ((~A) & C)) + w[16] + 0x5A827999;
- A = ((A << 30) | (A >>> 2));
-
- C += ((D << 5) | (D >>> 27)) + ((E & A) | ((~E) & B)) + w[17] + 0x5A827999;
- E = ((E << 30) | (E >>> 2));
-
- B += ((C << 5) | (C >>> 27)) + ((D & E) | ((~D) & A)) + w[18] + 0x5A827999;
- D = ((D << 30) | (D >>> 2));
-
- A += ((B << 5) | (B >>> 27)) + ((C & D) | ((~C) & E)) + w[19] + 0x5A827999;
- C = ((C << 30) | (C >>> 2));
-
- E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[20] + 0x6ED9EBA1;
- B = ((B << 30) | (B >>> 2));
-
- D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[21] + 0x6ED9EBA1;
- A = ((A << 30) | (A >>> 2));
-
- C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[22] + 0x6ED9EBA1;
- E = ((E << 30) | (E >>> 2));
-
- B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[23] + 0x6ED9EBA1;
- D = ((D << 30) | (D >>> 2));
-
- A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[24] + 0x6ED9EBA1;
- C = ((C << 30) | (C >>> 2));
-
- E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[25] + 0x6ED9EBA1;
- B = ((B << 30) | (B >>> 2));
-
- D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[26] + 0x6ED9EBA1;
- A = ((A << 30) | (A >>> 2));
-
- C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[27] + 0x6ED9EBA1;
- E = ((E << 30) | (E >>> 2));
-
- B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[28] + 0x6ED9EBA1;
- D = ((D << 30) | (D >>> 2));
-
- A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[29] + 0x6ED9EBA1;
- C = ((C << 30) | (C >>> 2));
-
- E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[30] + 0x6ED9EBA1;
- B = ((B << 30) | (B >>> 2));
-
- D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[31] + 0x6ED9EBA1;
- A = ((A << 30) | (A >>> 2));
-
- C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[32] + 0x6ED9EBA1;
- E = ((E << 30) | (E >>> 2));
-
- B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[33] + 0x6ED9EBA1;
- D = ((D << 30) | (D >>> 2));
-
- A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[34] + 0x6ED9EBA1;
- C = ((C << 30) | (C >>> 2));
-
- E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[35] + 0x6ED9EBA1;
- B = ((B << 30) | (B >>> 2));
-
- D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[36] + 0x6ED9EBA1;
- A = ((A << 30) | (A >>> 2));
-
- C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[37] + 0x6ED9EBA1;
- E = ((E << 30) | (E >>> 2));
-
- B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[38] + 0x6ED9EBA1;
- D = ((D << 30) | (D >>> 2));
-
- A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[39] + 0x6ED9EBA1;
- C = ((C << 30) | (C >>> 2));
-
- E += ((A << 5) | (A >>> 27)) + ((B & C) | (B & D) | (C & D)) + w[40] + 0x8F1BBCDC;
- B = ((B << 30) | (B >>> 2));
-
- D += ((E << 5) | (E >>> 27)) + ((A & B) | (A & C) | (B & C)) + w[41] + 0x8F1BBCDC;
- A = ((A << 30) | (A >>> 2));
-
- C += ((D << 5) | (D >>> 27)) + ((E & A) | (E & B) | (A & B)) + w[42] + 0x8F1BBCDC;
- E = ((E << 30) | (E >>> 2));
-
- B += ((C << 5) | (C >>> 27)) + ((D & E) | (D & A) | (E & A)) + w[43] + 0x8F1BBCDC;
- D = ((D << 30) | (D >>> 2));
-
- A += ((B << 5) | (B >>> 27)) + ((C & D) | (C & E) | (D & E)) + w[44] + 0x8F1BBCDC;
- C = ((C << 30) | (C >>> 2));
-
- E += ((A << 5) | (A >>> 27)) + ((B & C) | (B & D) | (C & D)) + w[45] + 0x8F1BBCDC;
- B = ((B << 30) | (B >>> 2));
-
- D += ((E << 5) | (E >>> 27)) + ((A & B) | (A & C) | (B & C)) + w[46] + 0x8F1BBCDC;
- A = ((A << 30) | (A >>> 2));
-
- C += ((D << 5) | (D >>> 27)) + ((E & A) | (E & B) | (A & B)) + w[47] + 0x8F1BBCDC;
- E = ((E << 30) | (E >>> 2));
-
- B += ((C << 5) | (C >>> 27)) + ((D & E) | (D & A) | (E & A)) + w[48] + 0x8F1BBCDC;
- D = ((D << 30) | (D >>> 2));
-
- A += ((B << 5) | (B >>> 27)) + ((C & D) | (C & E) | (D & E)) + w[49] + 0x8F1BBCDC;
- C = ((C << 30) | (C >>> 2));
-
- E += ((A << 5) | (A >>> 27)) + ((B & C) | (B & D) | (C & D)) + w[50] + 0x8F1BBCDC;
- B = ((B << 30) | (B >>> 2));
-
- D += ((E << 5) | (E >>> 27)) + ((A & B) | (A & C) | (B & C)) + w[51] + 0x8F1BBCDC;
- A = ((A << 30) | (A >>> 2));
-
- C += ((D << 5) | (D >>> 27)) + ((E & A) | (E & B) | (A & B)) + w[52] + 0x8F1BBCDC;
- E = ((E << 30) | (E >>> 2));
-
- B += ((C << 5) | (C >>> 27)) + ((D & E) | (D & A) | (E & A)) + w[53] + 0x8F1BBCDC;
- D = ((D << 30) | (D >>> 2));
-
- A += ((B << 5) | (B >>> 27)) + ((C & D) | (C & E) | (D & E)) + w[54] + 0x8F1BBCDC;
- C = ((C << 30) | (C >>> 2));
-
- E = E + ((A << 5) | (A >>> 27)) + ((B & C) | (B & D) | (C & D)) + w[55] + 0x8F1BBCDC;
- B = ((B << 30) | (B >>> 2));
-
- D += ((E << 5) | (E >>> 27)) + ((A & B) | (A & C) | (B & C)) + w[56] + 0x8F1BBCDC;
- A = ((A << 30) | (A >>> 2));
-
- C += ((D << 5) | (D >>> 27)) + ((E & A) | (E & B) | (A & B)) + w[57] + 0x8F1BBCDC;
- E = ((E << 30) | (E >>> 2));
-
- B += ((C << 5) | (C >>> 27)) + ((D & E) | (D & A) | (E & A)) + w[58] + 0x8F1BBCDC;
- D = ((D << 30) | (D >>> 2));
-
- A += ((B << 5) | (B >>> 27)) + ((C & D) | (C & E) | (D & E)) + w[59] + 0x8F1BBCDC;
- C = ((C << 30) | (C >>> 2));
-
- E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[60] + 0xCA62C1D6;
- B = ((B << 30) | (B >>> 2));
-
- D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[61] + 0xCA62C1D6;
- A = ((A << 30) | (A >>> 2));
-
- C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[62] + 0xCA62C1D6;
- E = ((E << 30) | (E >>> 2));
-
- B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[63] + 0xCA62C1D6;
- D = ((D << 30) | (D >>> 2));
-
- A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[64] + 0xCA62C1D6;
- C = ((C << 30) | (C >>> 2));
-
- E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[65] + 0xCA62C1D6;
- B = ((B << 30) | (B >>> 2));
-
- D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[66] + 0xCA62C1D6;
- A = ((A << 30) | (A >>> 2));
-
- C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[67] + 0xCA62C1D6;
- E = ((E << 30) | (E >>> 2));
-
- B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[68] + 0xCA62C1D6;
- D = ((D << 30) | (D >>> 2));
-
- A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[69] + 0xCA62C1D6;
- C = ((C << 30) | (C >>> 2));
-
- E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[70] + 0xCA62C1D6;
- B = ((B << 30) | (B >>> 2));
-
- D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[71] + 0xCA62C1D6;
- A = ((A << 30) | (A >>> 2));
-
- C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[72] + 0xCA62C1D6;
- E = ((E << 30) | (E >>> 2));
-
- B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[73] + 0xCA62C1D6;
- D = ((D << 30) | (D >>> 2));
-
- A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[74] + 0xCA62C1D6;
- C = ((C << 30) | (C >>> 2));
-
- E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[75] + 0xCA62C1D6;
- B = ((B << 30) | (B >>> 2));
-
- D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[76] + 0xCA62C1D6;
- A = ((A << 30) | (A >>> 2));
-
- C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[77] + 0xCA62C1D6;
- E = ((E << 30) | (E >>> 2));
-
- B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[78] + 0xCA62C1D6;
- D = ((D << 30) | (D >>> 2));
-
- A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[79] + 0xCA62C1D6;
- C = ((C << 30) | (C >>> 2));
-
- H0 += A;
- H1 += B;
- H2 += C;
- H3 += D;
- H4 += E;
-
- // debug(80, H0, H1, H2, H3, H4);
- }
-
- private static final String toHexString(byte[] b)
- {
- final String hexChar = "0123456789ABCDEF";
-
- StringBuffer sb = new StringBuffer();
- for (int i = 0; i < b.length; i++)
- {
- sb.append(hexChar.charAt((b[i] >> 4) & 0x0f));
- sb.append(hexChar.charAt(b[i] & 0x0f));
- }
- return sb.toString();
- }
-
- public static void main(String[] args)
- {
- SHA1 sha = new SHA1();
-
- byte[] dig1 = new byte[20];
- byte[] dig2 = new byte[20];
- byte[] dig3 = new byte[20];
-
- /*
- * We do not specify a charset name for getBytes(), since we assume that
- * the JVM's default encoder maps the _used_ ASCII characters exactly as
- * getBytes("US-ASCII") would do. (Ah, yes, too lazy to catch the
- * exception that can be thrown by getBytes("US-ASCII")). Note: This has
- * no effect on the SHA-1 implementation, this is just for the following
- * test code.
- */
-
- sha.update("abc".getBytes());
- sha.digest(dig1);
-
- sha.update("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq".getBytes());
- sha.digest(dig2);
-
- for (int i = 0; i < 1000000; i++)
- sha.update((byte) 'a');
- sha.digest(dig3);
-
- String dig1_res = toHexString(dig1);
- String dig2_res = toHexString(dig2);
- String dig3_res = toHexString(dig3);
-
- String dig1_ref = "A9993E364706816ABA3E25717850C26C9CD0D89D";
- String dig2_ref = "84983E441C3BD26EBAAE4AA1F95129E5E54670F1";
- String dig3_ref = "34AA973CD4C4DAA4F61EEB2BDBAD27316534016F";
-
- if (dig1_res.equals(dig1_ref))
- System.out.println("SHA-1 Test 1 OK.");
- else
- System.out.println("SHA-1 Test 1 FAILED.");
-
- if (dig2_res.equals(dig2_ref))
- System.out.println("SHA-1 Test 2 OK.");
- else
- System.out.println("SHA-1 Test 2 FAILED.");
-
- if (dig3_res.equals(dig3_ref))
- System.out.println("SHA-1 Test 3 OK.");
- else
- System.out.println("SHA-1 Test 3 FAILED.");
-
- if (dig3_res.equals(dig3_ref))
- System.out.println("SHA-1 Test 3 OK.");
- else
- System.out.println("SHA-1 Test 3 FAILED.");
- }
-}
diff --git a/src/com/trilead/ssh2/log/Logger.java b/src/com/trilead/ssh2/log/Logger.java
index fe388f7..20ab397 100644
--- a/src/com/trilead/ssh2/log/Logger.java
+++ b/src/com/trilead/ssh2/log/Logger.java
@@ -1,54 +1,54 @@
-
-package com.trilead.ssh2.log;
-
-import com.trilead.ssh2.DebugLogger;
-
-/**
- * Logger - a very simple logger, mainly used during development.
- * Is not based on log4j (to reduce external dependencies).
- * However, if needed, something like log4j could easily be
- * hooked in.
- * <p>
- * For speed reasons, the static variables are not protected
- * with semaphores. In other words, if you dynamicaly change the
- * logging settings, then some threads may still use the old setting.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: Logger.java,v 1.2 2008/03/03 07:01:36 cplattne Exp $
- */
-
-public class Logger
-{
- public static boolean enabled = false;
- public static DebugLogger logger = null;
-
- private String className;
-
- public final static Logger getLogger(Class x)
- {
- return new Logger(x);
- }
-
- public Logger(Class x)
- {
- this.className = x.getName();
- }
-
- public final boolean isEnabled()
- {
- return enabled;
- }
-
- public final void log(int level, String message)
- {
- if (!enabled)
- return;
-
- DebugLogger target = logger;
-
- if (target == null)
- return;
-
- target.log(level, className, message);
- }
-}
+
+package com.trilead.ssh2.log;
+
+import com.trilead.ssh2.DebugLogger;
+
+/**
+ * Logger - a very simple logger, mainly used during development.
+ * Is not based on log4j (to reduce external dependencies).
+ * However, if needed, something like log4j could easily be
+ * hooked in.
+ * <p>
+ * For speed reasons, the static variables are not protected
+ * with semaphores. In other words, if you dynamicaly change the
+ * logging settings, then some threads may still use the old setting.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: Logger.java,v 1.2 2008/03/03 07:01:36 cplattne Exp $
+ */
+
+public class Logger
+{
+ public static boolean enabled = false;
+ public static DebugLogger logger = null;
+
+ private String className;
+
+ public final static Logger getLogger(Class x)
+ {
+ return new Logger(x);
+ }
+
+ public Logger(Class x)
+ {
+ this.className = x.getName();
+ }
+
+ public final boolean isEnabled()
+ {
+ return enabled;
+ }
+
+ public final void log(int level, String message)
+ {
+ if (!enabled)
+ return;
+
+ DebugLogger target = logger;
+
+ if (target == null)
+ return;
+
+ target.log(level, className, message);
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketChannelOpenConfirmation.java b/src/com/trilead/ssh2/packets/PacketChannelOpenConfirmation.java
index 111f6a2..bd2ea3f 100644
--- a/src/com/trilead/ssh2/packets/PacketChannelOpenConfirmation.java
+++ b/src/com/trilead/ssh2/packets/PacketChannelOpenConfirmation.java
@@ -1,66 +1,66 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketChannelOpenConfirmation.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketChannelOpenConfirmation.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketChannelOpenConfirmation
-{
- byte[] payload;
-
- public int recipientChannelID;
- public int senderChannelID;
- public int initialWindowSize;
- public int maxPacketSize;
-
- public PacketChannelOpenConfirmation(int recipientChannelID, int senderChannelID, int initialWindowSize,
- int maxPacketSize)
- {
- this.recipientChannelID = recipientChannelID;
- this.senderChannelID = senderChannelID;
- this.initialWindowSize = initialWindowSize;
- this.maxPacketSize = maxPacketSize;
- }
-
- public PacketChannelOpenConfirmation(byte payload[], int off, int len) throws IOException
- {
- this.payload = new byte[len];
- System.arraycopy(payload, off, this.payload, 0, len);
-
- TypesReader tr = new TypesReader(payload, off, len);
-
- int packet_type = tr.readByte();
-
- if (packet_type != Packets.SSH_MSG_CHANNEL_OPEN_CONFIRMATION)
- throw new IOException(
- "This is not a SSH_MSG_CHANNEL_OPEN_CONFIRMATION! ("
- + packet_type + ")");
-
- recipientChannelID = tr.readUINT32();
- senderChannelID = tr.readUINT32();
- initialWindowSize = tr.readUINT32();
- maxPacketSize = tr.readUINT32();
-
- if (tr.remain() != 0)
- throw new IOException("Padding in SSH_MSG_CHANNEL_OPEN_CONFIRMATION packet!");
- }
-
- public byte[] getPayload()
- {
- if (payload == null)
- {
- TypesWriter tw = new TypesWriter();
- tw.writeByte(Packets.SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
- tw.writeUINT32(recipientChannelID);
- tw.writeUINT32(senderChannelID);
- tw.writeUINT32(initialWindowSize);
- tw.writeUINT32(maxPacketSize);
- payload = tw.getBytes();
- }
- return payload;
- }
-}
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+/**
+ * PacketChannelOpenConfirmation.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketChannelOpenConfirmation.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class PacketChannelOpenConfirmation
+{
+ byte[] payload;
+
+ public int recipientChannelID;
+ public int senderChannelID;
+ public int initialWindowSize;
+ public int maxPacketSize;
+
+ public PacketChannelOpenConfirmation(int recipientChannelID, int senderChannelID, int initialWindowSize,
+ int maxPacketSize)
+ {
+ this.recipientChannelID = recipientChannelID;
+ this.senderChannelID = senderChannelID;
+ this.initialWindowSize = initialWindowSize;
+ this.maxPacketSize = maxPacketSize;
+ }
+
+ public PacketChannelOpenConfirmation(byte payload[], int off, int len) throws IOException
+ {
+ this.payload = new byte[len];
+ System.arraycopy(payload, off, this.payload, 0, len);
+
+ TypesReader tr = new TypesReader(payload, off, len);
+
+ int packet_type = tr.readByte();
+
+ if (packet_type != Packets.SSH_MSG_CHANNEL_OPEN_CONFIRMATION)
+ throw new IOException(
+ "This is not a SSH_MSG_CHANNEL_OPEN_CONFIRMATION! ("
+ + packet_type + ")");
+
+ recipientChannelID = tr.readUINT32();
+ senderChannelID = tr.readUINT32();
+ initialWindowSize = tr.readUINT32();
+ maxPacketSize = tr.readUINT32();
+
+ if (tr.remain() != 0)
+ throw new IOException("Padding in SSH_MSG_CHANNEL_OPEN_CONFIRMATION packet!");
+ }
+
+ public byte[] getPayload()
+ {
+ if (payload == null)
+ {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
+ tw.writeUINT32(recipientChannelID);
+ tw.writeUINT32(senderChannelID);
+ tw.writeUINT32(initialWindowSize);
+ tw.writeUINT32(maxPacketSize);
+ payload = tw.getBytes();
+ }
+ return payload;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketChannelOpenFailure.java b/src/com/trilead/ssh2/packets/PacketChannelOpenFailure.java
index 5436ce3..1370355 100644
--- a/src/com/trilead/ssh2/packets/PacketChannelOpenFailure.java
+++ b/src/com/trilead/ssh2/packets/PacketChannelOpenFailure.java
@@ -1,66 +1,66 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketChannelOpenFailure.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketChannelOpenFailure.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketChannelOpenFailure
-{
- byte[] payload;
-
- public int recipientChannelID;
- public int reasonCode;
- public String description;
- public String languageTag;
-
- public PacketChannelOpenFailure(int recipientChannelID, int reasonCode, String description,
- String languageTag)
- {
- this.recipientChannelID = recipientChannelID;
- this.reasonCode = reasonCode;
- this.description = description;
- this.languageTag = languageTag;
- }
-
- public PacketChannelOpenFailure(byte payload[], int off, int len) throws IOException
- {
- this.payload = new byte[len];
- System.arraycopy(payload, off, this.payload, 0, len);
-
- TypesReader tr = new TypesReader(payload, off, len);
-
- int packet_type = tr.readByte();
-
- if (packet_type != Packets.SSH_MSG_CHANNEL_OPEN_FAILURE)
- throw new IOException(
- "This is not a SSH_MSG_CHANNEL_OPEN_FAILURE! ("
- + packet_type + ")");
-
- recipientChannelID = tr.readUINT32();
- reasonCode = tr.readUINT32();
- description = tr.readString();
- languageTag = tr.readString();
-
- if (tr.remain() != 0)
- throw new IOException("Padding in SSH_MSG_CHANNEL_OPEN_FAILURE packet!");
- }
-
- public byte[] getPayload()
- {
- if (payload == null)
- {
- TypesWriter tw = new TypesWriter();
- tw.writeByte(Packets.SSH_MSG_CHANNEL_OPEN_FAILURE);
- tw.writeUINT32(recipientChannelID);
- tw.writeUINT32(reasonCode);
- tw.writeString(description);
- tw.writeString(languageTag);
- payload = tw.getBytes();
- }
- return payload;
- }
-}
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+/**
+ * PacketChannelOpenFailure.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketChannelOpenFailure.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class PacketChannelOpenFailure
+{
+ byte[] payload;
+
+ public int recipientChannelID;
+ public int reasonCode;
+ public String description;
+ public String languageTag;
+
+ public PacketChannelOpenFailure(int recipientChannelID, int reasonCode, String description,
+ String languageTag)
+ {
+ this.recipientChannelID = recipientChannelID;
+ this.reasonCode = reasonCode;
+ this.description = description;
+ this.languageTag = languageTag;
+ }
+
+ public PacketChannelOpenFailure(byte payload[], int off, int len) throws IOException
+ {
+ this.payload = new byte[len];
+ System.arraycopy(payload, off, this.payload, 0, len);
+
+ TypesReader tr = new TypesReader(payload, off, len);
+
+ int packet_type = tr.readByte();
+
+ if (packet_type != Packets.SSH_MSG_CHANNEL_OPEN_FAILURE)
+ throw new IOException(
+ "This is not a SSH_MSG_CHANNEL_OPEN_FAILURE! ("
+ + packet_type + ")");
+
+ recipientChannelID = tr.readUINT32();
+ reasonCode = tr.readUINT32();
+ description = tr.readString();
+ languageTag = tr.readString();
+
+ if (tr.remain() != 0)
+ throw new IOException("Padding in SSH_MSG_CHANNEL_OPEN_FAILURE packet!");
+ }
+
+ public byte[] getPayload()
+ {
+ if (payload == null)
+ {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_CHANNEL_OPEN_FAILURE);
+ tw.writeUINT32(recipientChannelID);
+ tw.writeUINT32(reasonCode);
+ tw.writeString(description);
+ tw.writeString(languageTag);
+ payload = tw.getBytes();
+ }
+ return payload;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketChannelTrileadPing.java b/src/com/trilead/ssh2/packets/PacketChannelTrileadPing.java
index 18002aa..c337930 100644
--- a/src/com/trilead/ssh2/packets/PacketChannelTrileadPing.java
+++ b/src/com/trilead/ssh2/packets/PacketChannelTrileadPing.java
@@ -1,35 +1,35 @@
-
-package com.trilead.ssh2.packets;
-
-/**
- * PacketChannelTrileadPing.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketChannelTrileadPing.java,v 1.1 2008/03/03 07:01:36
- * cplattne Exp $
- */
-public class PacketChannelTrileadPing
-{
- byte[] payload;
-
- public int recipientChannelID;
-
- public PacketChannelTrileadPing(int recipientChannelID)
- {
- this.recipientChannelID = recipientChannelID;
- }
-
- public byte[] getPayload()
- {
- if (payload == null)
- {
- TypesWriter tw = new TypesWriter();
- tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
- tw.writeUINT32(recipientChannelID);
- tw.writeString("trilead-ping");
- tw.writeBoolean(true);
- payload = tw.getBytes();
- }
- return payload;
- }
-}
+
+package com.trilead.ssh2.packets;
+
+/**
+ * PacketChannelTrileadPing.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketChannelTrileadPing.java,v 1.1 2008/03/03 07:01:36
+ * cplattne Exp $
+ */
+public class PacketChannelTrileadPing
+{
+ byte[] payload;
+
+ public int recipientChannelID;
+
+ public PacketChannelTrileadPing(int recipientChannelID)
+ {
+ this.recipientChannelID = recipientChannelID;
+ }
+
+ public byte[] getPayload()
+ {
+ if (payload == null)
+ {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
+ tw.writeUINT32(recipientChannelID);
+ tw.writeString("trilead-ping");
+ tw.writeBoolean(true);
+ payload = tw.getBytes();
+ }
+ return payload;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketChannelWindowAdjust.java b/src/com/trilead/ssh2/packets/PacketChannelWindowAdjust.java
index bc0ac83..37ec081 100644
--- a/src/com/trilead/ssh2/packets/PacketChannelWindowAdjust.java
+++ b/src/com/trilead/ssh2/packets/PacketChannelWindowAdjust.java
@@ -1,57 +1,57 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketChannelWindowAdjust.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketChannelWindowAdjust.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketChannelWindowAdjust
-{
- byte[] payload;
-
- public int recipientChannelID;
- public int windowChange;
-
- public PacketChannelWindowAdjust(int recipientChannelID, int windowChange)
- {
- this.recipientChannelID = recipientChannelID;
- this.windowChange = windowChange;
- }
-
- public PacketChannelWindowAdjust(byte payload[], int off, int len) throws IOException
- {
- this.payload = new byte[len];
- System.arraycopy(payload, off, this.payload, 0, len);
-
- TypesReader tr = new TypesReader(payload, off, len);
-
- int packet_type = tr.readByte();
-
- if (packet_type != Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST)
- throw new IOException(
- "This is not a SSH_MSG_CHANNEL_WINDOW_ADJUST! ("
- + packet_type + ")");
-
- recipientChannelID = tr.readUINT32();
- windowChange = tr.readUINT32();
-
- if (tr.remain() != 0)
- throw new IOException("Padding in SSH_MSG_CHANNEL_WINDOW_ADJUST packet!");
- }
-
- public byte[] getPayload()
- {
- if (payload == null)
- {
- TypesWriter tw = new TypesWriter();
- tw.writeByte(Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST);
- tw.writeUINT32(recipientChannelID);
- tw.writeUINT32(windowChange);
- payload = tw.getBytes();
- }
- return payload;
- }
-}
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+/**
+ * PacketChannelWindowAdjust.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketChannelWindowAdjust.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class PacketChannelWindowAdjust
+{
+ byte[] payload;
+
+ public int recipientChannelID;
+ public int windowChange;
+
+ public PacketChannelWindowAdjust(int recipientChannelID, int windowChange)
+ {
+ this.recipientChannelID = recipientChannelID;
+ this.windowChange = windowChange;
+ }
+
+ public PacketChannelWindowAdjust(byte payload[], int off, int len) throws IOException
+ {
+ this.payload = new byte[len];
+ System.arraycopy(payload, off, this.payload, 0, len);
+
+ TypesReader tr = new TypesReader(payload, off, len);
+
+ int packet_type = tr.readByte();
+
+ if (packet_type != Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST)
+ throw new IOException(
+ "This is not a SSH_MSG_CHANNEL_WINDOW_ADJUST! ("
+ + packet_type + ")");
+
+ recipientChannelID = tr.readUINT32();
+ windowChange = tr.readUINT32();
+
+ if (tr.remain() != 0)
+ throw new IOException("Padding in SSH_MSG_CHANNEL_WINDOW_ADJUST packet!");
+ }
+
+ public byte[] getPayload()
+ {
+ if (payload == null)
+ {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST);
+ tw.writeUINT32(recipientChannelID);
+ tw.writeUINT32(windowChange);
+ payload = tw.getBytes();
+ }
+ return payload;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketDisconnect.java b/src/com/trilead/ssh2/packets/PacketDisconnect.java
index 1811fb3..50d6ec2 100644
--- a/src/com/trilead/ssh2/packets/PacketDisconnect.java
+++ b/src/com/trilead/ssh2/packets/PacketDisconnect.java
@@ -1,57 +1,57 @@
-
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketDisconnect.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketDisconnect.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
- */
-public class PacketDisconnect
-{
- byte[] payload;
-
- int reason;
- String desc;
- String lang;
-
- public PacketDisconnect(byte payload[], int off, int len) throws IOException
- {
- this.payload = new byte[len];
- System.arraycopy(payload, off, this.payload, 0, len);
-
- TypesReader tr = new TypesReader(payload, off, len);
-
- int packet_type = tr.readByte();
-
- if (packet_type != Packets.SSH_MSG_DISCONNECT)
- throw new IOException("This is not a Disconnect Packet! (" + packet_type + ")");
-
- reason = tr.readUINT32();
- desc = tr.readString();
- lang = tr.readString();
- }
-
- public PacketDisconnect(int reason, String desc, String lang)
- {
- this.reason = reason;
- this.desc = desc;
- this.lang = lang;
- }
-
- public byte[] getPayload()
- {
- if (payload == null)
- {
- TypesWriter tw = new TypesWriter();
- tw.writeByte(Packets.SSH_MSG_DISCONNECT);
- tw.writeUINT32(reason);
- tw.writeString(desc);
- tw.writeString(lang);
- payload = tw.getBytes();
- }
- return payload;
- }
-}
+
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+/**
+ * PacketDisconnect.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketDisconnect.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
+ */
+public class PacketDisconnect
+{
+ byte[] payload;
+
+ int reason;
+ String desc;
+ String lang;
+
+ public PacketDisconnect(byte payload[], int off, int len) throws IOException
+ {
+ this.payload = new byte[len];
+ System.arraycopy(payload, off, this.payload, 0, len);
+
+ TypesReader tr = new TypesReader(payload, off, len);
+
+ int packet_type = tr.readByte();
+
+ if (packet_type != Packets.SSH_MSG_DISCONNECT)
+ throw new IOException("This is not a Disconnect Packet! (" + packet_type + ")");
+
+ reason = tr.readUINT32();
+ desc = tr.readString();
+ lang = tr.readString();
+ }
+
+ public PacketDisconnect(int reason, String desc, String lang)
+ {
+ this.reason = reason;
+ this.desc = desc;
+ this.lang = lang;
+ }
+
+ public byte[] getPayload()
+ {
+ if (payload == null)
+ {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_DISCONNECT);
+ tw.writeUINT32(reason);
+ tw.writeString(desc);
+ tw.writeString(lang);
+ payload = tw.getBytes();
+ }
+ return payload;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketGlobalCancelForwardRequest.java b/src/com/trilead/ssh2/packets/PacketGlobalCancelForwardRequest.java
index bc54b2e..20bd558 100644
--- a/src/com/trilead/ssh2/packets/PacketGlobalCancelForwardRequest.java
+++ b/src/com/trilead/ssh2/packets/PacketGlobalCancelForwardRequest.java
@@ -1,42 +1,42 @@
-
-package com.trilead.ssh2.packets;
-
-/**
- * PacketGlobalCancelForwardRequest.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketGlobalCancelForwardRequest.java,v 1.1 2007/10/15 12:49:55
- * cplattne Exp $
- */
-public class PacketGlobalCancelForwardRequest
-{
- byte[] payload;
-
- public boolean wantReply;
- public String bindAddress;
- public int bindPort;
-
- public PacketGlobalCancelForwardRequest(boolean wantReply, String bindAddress, int bindPort)
- {
- this.wantReply = wantReply;
- this.bindAddress = bindAddress;
- this.bindPort = bindPort;
- }
-
- public byte[] getPayload()
- {
- if (payload == null)
- {
- TypesWriter tw = new TypesWriter();
- tw.writeByte(Packets.SSH_MSG_GLOBAL_REQUEST);
-
- tw.writeString("cancel-tcpip-forward");
- tw.writeBoolean(wantReply);
- tw.writeString(bindAddress);
- tw.writeUINT32(bindPort);
-
- payload = tw.getBytes();
- }
- return payload;
- }
-}
+
+package com.trilead.ssh2.packets;
+
+/**
+ * PacketGlobalCancelForwardRequest.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketGlobalCancelForwardRequest.java,v 1.1 2007/10/15 12:49:55
+ * cplattne Exp $
+ */
+public class PacketGlobalCancelForwardRequest
+{
+ byte[] payload;
+
+ public boolean wantReply;
+ public String bindAddress;
+ public int bindPort;
+
+ public PacketGlobalCancelForwardRequest(boolean wantReply, String bindAddress, int bindPort)
+ {
+ this.wantReply = wantReply;
+ this.bindAddress = bindAddress;
+ this.bindPort = bindPort;
+ }
+
+ public byte[] getPayload()
+ {
+ if (payload == null)
+ {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_GLOBAL_REQUEST);
+
+ tw.writeString("cancel-tcpip-forward");
+ tw.writeBoolean(wantReply);
+ tw.writeString(bindAddress);
+ tw.writeUINT32(bindPort);
+
+ payload = tw.getBytes();
+ }
+ return payload;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketGlobalForwardRequest.java b/src/com/trilead/ssh2/packets/PacketGlobalForwardRequest.java
index a97c4e3..55257e9 100644
--- a/src/com/trilead/ssh2/packets/PacketGlobalForwardRequest.java
+++ b/src/com/trilead/ssh2/packets/PacketGlobalForwardRequest.java
@@ -1,41 +1,41 @@
-
-package com.trilead.ssh2.packets;
-
-/**
- * PacketGlobalForwardRequest.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketGlobalForwardRequest.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketGlobalForwardRequest
-{
- byte[] payload;
-
- public boolean wantReply;
- public String bindAddress;
- public int bindPort;
-
- public PacketGlobalForwardRequest(boolean wantReply, String bindAddress, int bindPort)
- {
- this.wantReply = wantReply;
- this.bindAddress = bindAddress;
- this.bindPort = bindPort;
- }
-
- public byte[] getPayload()
- {
- if (payload == null)
- {
- TypesWriter tw = new TypesWriter();
- tw.writeByte(Packets.SSH_MSG_GLOBAL_REQUEST);
-
- tw.writeString("tcpip-forward");
- tw.writeBoolean(wantReply);
- tw.writeString(bindAddress);
- tw.writeUINT32(bindPort);
-
- payload = tw.getBytes();
- }
- return payload;
- }
-}
+
+package com.trilead.ssh2.packets;
+
+/**
+ * PacketGlobalForwardRequest.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketGlobalForwardRequest.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class PacketGlobalForwardRequest
+{
+ byte[] payload;
+
+ public boolean wantReply;
+ public String bindAddress;
+ public int bindPort;
+
+ public PacketGlobalForwardRequest(boolean wantReply, String bindAddress, int bindPort)
+ {
+ this.wantReply = wantReply;
+ this.bindAddress = bindAddress;
+ this.bindPort = bindPort;
+ }
+
+ public byte[] getPayload()
+ {
+ if (payload == null)
+ {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_GLOBAL_REQUEST);
+
+ tw.writeString("tcpip-forward");
+ tw.writeBoolean(wantReply);
+ tw.writeString(bindAddress);
+ tw.writeUINT32(bindPort);
+
+ payload = tw.getBytes();
+ }
+ return payload;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketGlobalTrileadPing.java b/src/com/trilead/ssh2/packets/PacketGlobalTrileadPing.java
index 5b9013d..3d8930e 100644
--- a/src/com/trilead/ssh2/packets/PacketGlobalTrileadPing.java
+++ b/src/com/trilead/ssh2/packets/PacketGlobalTrileadPing.java
@@ -1,32 +1,32 @@
-
-package com.trilead.ssh2.packets;
-
-/**
- * PacketGlobalTrileadPing.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketGlobalTrileadPing.java,v 1.1 2008/03/03 07:01:36 cplattne Exp $
- */
-public class PacketGlobalTrileadPing
-{
- byte[] payload;
-
- public PacketGlobalTrileadPing()
- {
- }
-
- public byte[] getPayload()
- {
- if (payload == null)
- {
- TypesWriter tw = new TypesWriter();
- tw.writeByte(Packets.SSH_MSG_GLOBAL_REQUEST);
-
- tw.writeString("trilead-ping");
- tw.writeBoolean(true);
-
- payload = tw.getBytes();
- }
- return payload;
- }
-}
+
+package com.trilead.ssh2.packets;
+
+/**
+ * PacketGlobalTrileadPing.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketGlobalTrileadPing.java,v 1.1 2008/03/03 07:01:36 cplattne Exp $
+ */
+public class PacketGlobalTrileadPing
+{
+ byte[] payload;
+
+ public PacketGlobalTrileadPing()
+ {
+ }
+
+ public byte[] getPayload()
+ {
+ if (payload == null)
+ {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_GLOBAL_REQUEST);
+
+ tw.writeString("trilead-ping");
+ tw.writeBoolean(true);
+
+ payload = tw.getBytes();
+ }
+ return payload;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketIgnore.java b/src/com/trilead/ssh2/packets/PacketIgnore.java
index 591ed49..2b4d917 100644
--- a/src/com/trilead/ssh2/packets/PacketIgnore.java
+++ b/src/com/trilead/ssh2/packets/PacketIgnore.java
@@ -1,59 +1,59 @@
-
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketIgnore.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketIgnore.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketIgnore
-{
- byte[] payload;
-
- byte[] data;
-
- public void setData(byte[] data)
- {
- this.data = data;
- payload = null;
- }
-
- public PacketIgnore()
- {
- }
-
- public PacketIgnore(byte payload[], int off, int len) throws IOException
- {
- this.payload = new byte[len];
- System.arraycopy(payload, off, this.payload, 0, len);
-
- TypesReader tr = new TypesReader(payload, off, len);
-
- int packet_type = tr.readByte();
-
- if (packet_type != Packets.SSH_MSG_IGNORE)
- throw new IOException("This is not a SSH_MSG_IGNORE packet! (" + packet_type + ")");
-
- /* Could parse String body */
- }
-
- public byte[] getPayload()
- {
- if (payload == null)
- {
- TypesWriter tw = new TypesWriter();
- tw.writeByte(Packets.SSH_MSG_IGNORE);
-
- if (data != null)
- tw.writeString(data, 0, data.length);
- else
- tw.writeString("");
-
- payload = tw.getBytes();
- }
- return payload;
- }
-}
+
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+/**
+ * PacketIgnore.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketIgnore.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class PacketIgnore
+{
+ byte[] payload;
+
+ byte[] data;
+
+ public void setData(byte[] data)
+ {
+ this.data = data;
+ payload = null;
+ }
+
+ public PacketIgnore()
+ {
+ }
+
+ public PacketIgnore(byte payload[], int off, int len) throws IOException
+ {
+ this.payload = new byte[len];
+ System.arraycopy(payload, off, this.payload, 0, len);
+
+ TypesReader tr = new TypesReader(payload, off, len);
+
+ int packet_type = tr.readByte();
+
+ if (packet_type != Packets.SSH_MSG_IGNORE)
+ throw new IOException("This is not a SSH_MSG_IGNORE packet! (" + packet_type + ")");
+
+ /* Could parse String body */
+ }
+
+ public byte[] getPayload()
+ {
+ if (payload == null)
+ {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_IGNORE);
+
+ if (data != null)
+ tw.writeString(data, 0, data.length);
+ else
+ tw.writeString("");
+
+ payload = tw.getBytes();
+ }
+ return payload;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketKexDHInit.java b/src/com/trilead/ssh2/packets/PacketKexDHInit.java
index 26e14f6..0092516 100644
--- a/src/com/trilead/ssh2/packets/PacketKexDHInit.java
+++ b/src/com/trilead/ssh2/packets/PacketKexDHInit.java
@@ -1,33 +1,31 @@
-package com.trilead.ssh2.packets;
-
-import java.math.BigInteger;
-
-/**
- * PacketKexDHInit.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketKexDHInit.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketKexDHInit
-{
- byte[] payload;
-
- BigInteger e;
-
- public PacketKexDHInit(BigInteger e)
- {
- this.e = e;
- }
-
- public byte[] getPayload()
- {
- if (payload == null)
- {
- TypesWriter tw = new TypesWriter();
- tw.writeByte(Packets.SSH_MSG_KEXDH_INIT);
- tw.writeMPInt(e);
- payload = tw.getBytes();
- }
- return payload;
- }
-}
+package com.trilead.ssh2.packets;
+
+/**
+ * PacketKexDHInit.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketKexDHInit.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class PacketKexDHInit
+{
+ byte[] payload;
+
+ byte[] publicKey;
+
+ public PacketKexDHInit(byte[] publicKey)
+ {
+ this.publicKey = publicKey;
+ }
+
+ public byte[] getPayload()
+ {
+ if (payload == null)
+ {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_KEXDH_INIT);
+ tw.writeString(publicKey, 0, publicKey.length);
+ payload = tw.getBytes();
+ }
+ return payload;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketKexDHReply.java b/src/com/trilead/ssh2/packets/PacketKexDHReply.java
index 0803ff9..51f2bda 100644
--- a/src/com/trilead/ssh2/packets/PacketKexDHReply.java
+++ b/src/com/trilead/ssh2/packets/PacketKexDHReply.java
@@ -1,55 +1,53 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-import java.math.BigInteger;
-
-/**
- * PacketKexDHReply.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketKexDHReply.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketKexDHReply
-{
- byte[] payload;
-
- byte[] hostKey;
- BigInteger f;
- byte[] signature;
-
- public PacketKexDHReply(byte payload[], int off, int len) throws IOException
- {
- this.payload = new byte[len];
- System.arraycopy(payload, off, this.payload, 0, len);
-
- TypesReader tr = new TypesReader(payload, off, len);
-
- int packet_type = tr.readByte();
-
- if (packet_type != Packets.SSH_MSG_KEXDH_REPLY)
- throw new IOException("This is not a SSH_MSG_KEXDH_REPLY! ("
- + packet_type + ")");
-
- hostKey = tr.readByteString();
- f = tr.readMPINT();
- signature = tr.readByteString();
-
- if (tr.remain() != 0) throw new IOException("PADDING IN SSH_MSG_KEXDH_REPLY!");
- }
-
- public BigInteger getF()
- {
- return f;
- }
-
- public byte[] getHostKey()
- {
- return hostKey;
- }
-
- public byte[] getSignature()
- {
- return signature;
- }
-}
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+/**
+ * PacketKexDHReply.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketKexDHReply.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class PacketKexDHReply
+{
+ byte[] payload;
+
+ byte[] hostKey;
+ byte[] publicKey;
+ byte[] signature;
+
+ public PacketKexDHReply(byte payload[], int off, int len) throws IOException
+ {
+ this.payload = new byte[len];
+ System.arraycopy(payload, off, this.payload, 0, len);
+
+ TypesReader tr = new TypesReader(payload, off, len);
+
+ int packet_type = tr.readByte();
+
+ if (packet_type != Packets.SSH_MSG_KEXDH_REPLY)
+ throw new IOException("This is not a SSH_MSG_KEXDH_REPLY! ("
+ + packet_type + ")");
+
+ hostKey = tr.readByteString();
+ publicKey = tr.readByteString();
+ signature = tr.readByteString();
+
+ if (tr.remain() != 0) throw new IOException("PADDING IN SSH_MSG_KEXDH_REPLY!");
+ }
+
+ public byte[] getF()
+ {
+ return publicKey;
+ }
+
+ public byte[] getHostKey()
+ {
+ return hostKey;
+ }
+
+ public byte[] getSignature()
+ {
+ return signature;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketKexDhGexGroup.java b/src/com/trilead/ssh2/packets/PacketKexDhGexGroup.java
index d531dad..db85b61 100644
--- a/src/com/trilead/ssh2/packets/PacketKexDhGexGroup.java
+++ b/src/com/trilead/ssh2/packets/PacketKexDhGexGroup.java
@@ -1,50 +1,50 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-import java.math.BigInteger;
-
-/**
- * PacketKexDhGexGroup.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketKexDhGexGroup.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketKexDhGexGroup
-{
- byte[] payload;
-
- BigInteger p;
- BigInteger g;
-
- public PacketKexDhGexGroup(byte payload[], int off, int len) throws IOException
- {
- this.payload = new byte[len];
- System.arraycopy(payload, off, this.payload, 0, len);
-
- TypesReader tr = new TypesReader(payload, off, len);
-
- int packet_type = tr.readByte();
-
- if (packet_type != Packets.SSH_MSG_KEX_DH_GEX_GROUP)
- throw new IllegalArgumentException(
- "This is not a SSH_MSG_KEX_DH_GEX_GROUP! (" + packet_type
- + ")");
-
- p = tr.readMPINT();
- g = tr.readMPINT();
-
- if (tr.remain() != 0)
- throw new IOException("PADDING IN SSH_MSG_KEX_DH_GEX_GROUP!");
- }
-
- public BigInteger getG()
- {
- return g;
- }
-
- public BigInteger getP()
- {
- return p;
- }
-}
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+import java.math.BigInteger;
+
+/**
+ * PacketKexDhGexGroup.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketKexDhGexGroup.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class PacketKexDhGexGroup
+{
+ byte[] payload;
+
+ BigInteger p;
+ BigInteger g;
+
+ public PacketKexDhGexGroup(byte payload[], int off, int len) throws IOException
+ {
+ this.payload = new byte[len];
+ System.arraycopy(payload, off, this.payload, 0, len);
+
+ TypesReader tr = new TypesReader(payload, off, len);
+
+ int packet_type = tr.readByte();
+
+ if (packet_type != Packets.SSH_MSG_KEX_DH_GEX_GROUP)
+ throw new IllegalArgumentException(
+ "This is not a SSH_MSG_KEX_DH_GEX_GROUP! (" + packet_type
+ + ")");
+
+ p = tr.readMPINT();
+ g = tr.readMPINT();
+
+ if (tr.remain() != 0)
+ throw new IOException("PADDING IN SSH_MSG_KEX_DH_GEX_GROUP!");
+ }
+
+ public BigInteger getG()
+ {
+ return g;
+ }
+
+ public BigInteger getP()
+ {
+ return p;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketKexDhGexInit.java b/src/com/trilead/ssh2/packets/PacketKexDhGexInit.java
index f8f0916..8b34230 100644
--- a/src/com/trilead/ssh2/packets/PacketKexDhGexInit.java
+++ b/src/com/trilead/ssh2/packets/PacketKexDhGexInit.java
@@ -1,33 +1,33 @@
-package com.trilead.ssh2.packets;
-
-import java.math.BigInteger;
-
-/**
- * PacketKexDhGexInit.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketKexDhGexInit.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketKexDhGexInit
-{
- byte[] payload;
-
- BigInteger e;
-
- public PacketKexDhGexInit(BigInteger e)
- {
- this.e = e;
- }
-
- public byte[] getPayload()
- {
- if (payload == null)
- {
- TypesWriter tw = new TypesWriter();
- tw.writeByte(Packets.SSH_MSG_KEX_DH_GEX_INIT);
- tw.writeMPInt(e);
- payload = tw.getBytes();
- }
- return payload;
- }
-}
+package com.trilead.ssh2.packets;
+
+import java.math.BigInteger;
+
+/**
+ * PacketKexDhGexInit.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketKexDhGexInit.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class PacketKexDhGexInit
+{
+ byte[] payload;
+
+ BigInteger e;
+
+ public PacketKexDhGexInit(BigInteger e)
+ {
+ this.e = e;
+ }
+
+ public byte[] getPayload()
+ {
+ if (payload == null)
+ {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_KEX_DH_GEX_INIT);
+ tw.writeMPInt(e);
+ payload = tw.getBytes();
+ }
+ return payload;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketKexDhGexReply.java b/src/com/trilead/ssh2/packets/PacketKexDhGexReply.java
index ae225fb..382b3b7 100644
--- a/src/com/trilead/ssh2/packets/PacketKexDhGexReply.java
+++ b/src/com/trilead/ssh2/packets/PacketKexDhGexReply.java
@@ -1,56 +1,56 @@
-
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-import java.math.BigInteger;
-
-/**
- * PacketKexDhGexReply.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketKexDhGexReply.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketKexDhGexReply
-{
- byte[] payload;
-
- byte[] hostKey;
- BigInteger f;
- byte[] signature;
-
- public PacketKexDhGexReply(byte payload[], int off, int len) throws IOException
- {
- this.payload = new byte[len];
- System.arraycopy(payload, off, this.payload, 0, len);
-
- TypesReader tr = new TypesReader(payload, off, len);
-
- int packet_type = tr.readByte();
-
- if (packet_type != Packets.SSH_MSG_KEX_DH_GEX_REPLY)
- throw new IOException("This is not a SSH_MSG_KEX_DH_GEX_REPLY! (" + packet_type + ")");
-
- hostKey = tr.readByteString();
- f = tr.readMPINT();
- signature = tr.readByteString();
-
- if (tr.remain() != 0)
- throw new IOException("PADDING IN SSH_MSG_KEX_DH_GEX_REPLY!");
- }
-
- public BigInteger getF()
- {
- return f;
- }
-
- public byte[] getHostKey()
- {
- return hostKey;
- }
-
- public byte[] getSignature()
- {
- return signature;
- }
-}
+
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+import java.math.BigInteger;
+
+/**
+ * PacketKexDhGexReply.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketKexDhGexReply.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class PacketKexDhGexReply
+{
+ byte[] payload;
+
+ byte[] hostKey;
+ BigInteger f;
+ byte[] signature;
+
+ public PacketKexDhGexReply(byte payload[], int off, int len) throws IOException
+ {
+ this.payload = new byte[len];
+ System.arraycopy(payload, off, this.payload, 0, len);
+
+ TypesReader tr = new TypesReader(payload, off, len);
+
+ int packet_type = tr.readByte();
+
+ if (packet_type != Packets.SSH_MSG_KEX_DH_GEX_REPLY)
+ throw new IOException("This is not a SSH_MSG_KEX_DH_GEX_REPLY! (" + packet_type + ")");
+
+ hostKey = tr.readByteString();
+ f = tr.readMPINT();
+ signature = tr.readByteString();
+
+ if (tr.remain() != 0)
+ throw new IOException("PADDING IN SSH_MSG_KEX_DH_GEX_REPLY!");
+ }
+
+ public BigInteger getF()
+ {
+ return f;
+ }
+
+ public byte[] getHostKey()
+ {
+ return hostKey;
+ }
+
+ public byte[] getSignature()
+ {
+ return signature;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketKexDhGexRequest.java b/src/com/trilead/ssh2/packets/PacketKexDhGexRequest.java
index 75fc2af..50369df 100644
--- a/src/com/trilead/ssh2/packets/PacketKexDhGexRequest.java
+++ b/src/com/trilead/ssh2/packets/PacketKexDhGexRequest.java
@@ -1,39 +1,39 @@
-package com.trilead.ssh2.packets;
-
-import com.trilead.ssh2.DHGexParameters;
-
-/**
- * PacketKexDhGexRequest.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketKexDhGexRequest.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketKexDhGexRequest
-{
- byte[] payload;
-
- int min;
- int n;
- int max;
-
- public PacketKexDhGexRequest(DHGexParameters para)
- {
- this.min = para.getMin_group_len();
- this.n = para.getPref_group_len();
- this.max = para.getMax_group_len();
- }
-
- public byte[] getPayload()
- {
- if (payload == null)
- {
- TypesWriter tw = new TypesWriter();
- tw.writeByte(Packets.SSH_MSG_KEX_DH_GEX_REQUEST);
- tw.writeUINT32(min);
- tw.writeUINT32(n);
- tw.writeUINT32(max);
- payload = tw.getBytes();
- }
- return payload;
- }
-}
+package com.trilead.ssh2.packets;
+
+import com.trilead.ssh2.DHGexParameters;
+
+/**
+ * PacketKexDhGexRequest.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketKexDhGexRequest.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class PacketKexDhGexRequest
+{
+ byte[] payload;
+
+ int min;
+ int n;
+ int max;
+
+ public PacketKexDhGexRequest(DHGexParameters para)
+ {
+ this.min = para.getMin_group_len();
+ this.n = para.getPref_group_len();
+ this.max = para.getMax_group_len();
+ }
+
+ public byte[] getPayload()
+ {
+ if (payload == null)
+ {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_KEX_DH_GEX_REQUEST);
+ tw.writeUINT32(min);
+ tw.writeUINT32(n);
+ tw.writeUINT32(max);
+ payload = tw.getBytes();
+ }
+ return payload;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketKexDhGexRequestOld.java b/src/com/trilead/ssh2/packets/PacketKexDhGexRequestOld.java
index d89ec02..327f379 100644
--- a/src/com/trilead/ssh2/packets/PacketKexDhGexRequestOld.java
+++ b/src/com/trilead/ssh2/packets/PacketKexDhGexRequestOld.java
@@ -1,34 +1,34 @@
-
-package com.trilead.ssh2.packets;
-
-import com.trilead.ssh2.DHGexParameters;
-
-/**
- * PacketKexDhGexRequestOld.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketKexDhGexRequestOld.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketKexDhGexRequestOld
-{
- byte[] payload;
-
- int n;
-
- public PacketKexDhGexRequestOld(DHGexParameters para)
- {
- this.n = para.getPref_group_len();
- }
-
- public byte[] getPayload()
- {
- if (payload == null)
- {
- TypesWriter tw = new TypesWriter();
- tw.writeByte(Packets.SSH_MSG_KEX_DH_GEX_REQUEST_OLD);
- tw.writeUINT32(n);
- payload = tw.getBytes();
- }
- return payload;
- }
-}
+
+package com.trilead.ssh2.packets;
+
+import com.trilead.ssh2.DHGexParameters;
+
+/**
+ * PacketKexDhGexRequestOld.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketKexDhGexRequestOld.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class PacketKexDhGexRequestOld
+{
+ byte[] payload;
+
+ int n;
+
+ public PacketKexDhGexRequestOld(DHGexParameters para)
+ {
+ this.n = para.getPref_group_len();
+ }
+
+ public byte[] getPayload()
+ {
+ if (payload == null)
+ {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_KEX_DH_GEX_REQUEST_OLD);
+ tw.writeUINT32(n);
+ payload = tw.getBytes();
+ }
+ return payload;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketKexInit.java b/src/com/trilead/ssh2/packets/PacketKexInit.java
index 7da5067..087d658 100644
--- a/src/com/trilead/ssh2/packets/PacketKexInit.java
+++ b/src/com/trilead/ssh2/packets/PacketKexInit.java
@@ -1,166 +1,165 @@
-
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-import java.security.SecureRandom;
-
-import com.trilead.ssh2.compression.CompressionFactory;
-import com.trilead.ssh2.crypto.CryptoWishList;
-import com.trilead.ssh2.transport.KexParameters;
-
-
-/**
- * PacketKexInit.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketKexInit.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketKexInit
-{
- byte[] payload;
-
- KexParameters kp = new KexParameters();
-
- public PacketKexInit(CryptoWishList cwl, SecureRandom rnd)
- {
- kp.cookie = new byte[16];
- rnd.nextBytes(kp.cookie);
-
- kp.kex_algorithms = cwl.kexAlgorithms;
- kp.server_host_key_algorithms = cwl.serverHostKeyAlgorithms;
- kp.encryption_algorithms_client_to_server = cwl.c2s_enc_algos;
- kp.encryption_algorithms_server_to_client = cwl.s2c_enc_algos;
- kp.mac_algorithms_client_to_server = cwl.c2s_mac_algos;
- kp.mac_algorithms_server_to_client = cwl.s2c_mac_algos;
- kp.compression_algorithms_client_to_server = cwl.c2s_comp_algos;
- kp.compression_algorithms_server_to_client = cwl.s2c_comp_algos;
- kp.languages_client_to_server = new String[] {};
- kp.languages_server_to_client = new String[] {};
- kp.first_kex_packet_follows = false;
- kp.reserved_field1 = 0;
- }
-
- public PacketKexInit(byte payload[], int off, int len) throws IOException
- {
- this.payload = new byte[len];
- System.arraycopy(payload, off, this.payload, 0, len);
-
- TypesReader tr = new TypesReader(payload, off, len);
-
- int packet_type = tr.readByte();
-
- if (packet_type != Packets.SSH_MSG_KEXINIT)
- throw new IOException("This is not a KexInitPacket! (" + packet_type + ")");
-
- kp.cookie = tr.readBytes(16);
- kp.kex_algorithms = tr.readNameList();
- kp.server_host_key_algorithms = tr.readNameList();
- kp.encryption_algorithms_client_to_server = tr.readNameList();
- kp.encryption_algorithms_server_to_client = tr.readNameList();
- kp.mac_algorithms_client_to_server = tr.readNameList();
- kp.mac_algorithms_server_to_client = tr.readNameList();
- kp.compression_algorithms_client_to_server = tr.readNameList();
- kp.compression_algorithms_server_to_client = tr.readNameList();
- kp.languages_client_to_server = tr.readNameList();
- kp.languages_server_to_client = tr.readNameList();
- kp.first_kex_packet_follows = tr.readBoolean();
- kp.reserved_field1 = tr.readUINT32();
-
- if (tr.remain() != 0)
- throw new IOException("Padding in KexInitPacket!");
- }
-
- public byte[] getPayload()
- {
- if (payload == null)
- {
- TypesWriter tw = new TypesWriter();
- tw.writeByte(Packets.SSH_MSG_KEXINIT);
- tw.writeBytes(kp.cookie, 0, 16);
- tw.writeNameList(kp.kex_algorithms);
- tw.writeNameList(kp.server_host_key_algorithms);
- tw.writeNameList(kp.encryption_algorithms_client_to_server);
- tw.writeNameList(kp.encryption_algorithms_server_to_client);
- tw.writeNameList(kp.mac_algorithms_client_to_server);
- tw.writeNameList(kp.mac_algorithms_server_to_client);
- tw.writeNameList(kp.compression_algorithms_client_to_server);
- tw.writeNameList(kp.compression_algorithms_server_to_client);
- tw.writeNameList(kp.languages_client_to_server);
- tw.writeNameList(kp.languages_server_to_client);
- tw.writeBoolean(kp.first_kex_packet_follows);
- tw.writeUINT32(kp.reserved_field1);
- payload = tw.getBytes();
- }
- return payload;
- }
-
- public KexParameters getKexParameters()
- {
- return kp;
- }
-
- public String[] getCompression_algorithms_client_to_server()
- {
- return kp.compression_algorithms_client_to_server;
- }
-
- public String[] getCompression_algorithms_server_to_client()
- {
- return kp.compression_algorithms_server_to_client;
- }
-
- public byte[] getCookie()
- {
- return kp.cookie;
- }
-
- public String[] getEncryption_algorithms_client_to_server()
- {
- return kp.encryption_algorithms_client_to_server;
- }
-
- public String[] getEncryption_algorithms_server_to_client()
- {
- return kp.encryption_algorithms_server_to_client;
- }
-
- public boolean isFirst_kex_packet_follows()
- {
- return kp.first_kex_packet_follows;
- }
-
- public String[] getKex_algorithms()
- {
- return kp.kex_algorithms;
- }
-
- public String[] getLanguages_client_to_server()
- {
- return kp.languages_client_to_server;
- }
-
- public String[] getLanguages_server_to_client()
- {
- return kp.languages_server_to_client;
- }
-
- public String[] getMac_algorithms_client_to_server()
- {
- return kp.mac_algorithms_client_to_server;
- }
-
- public String[] getMac_algorithms_server_to_client()
- {
- return kp.mac_algorithms_server_to_client;
- }
-
- public int getReserved_field1()
- {
- return kp.reserved_field1;
- }
-
- public String[] getServer_host_key_algorithms()
- {
- return kp.server_host_key_algorithms;
- }
-}
+
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+import java.security.SecureRandom;
+
+import com.trilead.ssh2.crypto.CryptoWishList;
+import com.trilead.ssh2.transport.KexParameters;
+
+
+/**
+ * PacketKexInit.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketKexInit.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class PacketKexInit
+{
+ byte[] payload;
+
+ KexParameters kp = new KexParameters();
+
+ public PacketKexInit(CryptoWishList cwl)
+ {
+ kp.cookie = new byte[16];
+ new SecureRandom().nextBytes(kp.cookie);
+
+ kp.kex_algorithms = cwl.kexAlgorithms;
+ kp.server_host_key_algorithms = cwl.serverHostKeyAlgorithms;
+ kp.encryption_algorithms_client_to_server = cwl.c2s_enc_algos;
+ kp.encryption_algorithms_server_to_client = cwl.s2c_enc_algos;
+ kp.mac_algorithms_client_to_server = cwl.c2s_mac_algos;
+ kp.mac_algorithms_server_to_client = cwl.s2c_mac_algos;
+ kp.compression_algorithms_client_to_server = cwl.c2s_comp_algos;
+ kp.compression_algorithms_server_to_client = cwl.s2c_comp_algos;
+ kp.languages_client_to_server = new String[] {};
+ kp.languages_server_to_client = new String[] {};
+ kp.first_kex_packet_follows = false;
+ kp.reserved_field1 = 0;
+ }
+
+ public PacketKexInit(byte payload[], int off, int len) throws IOException
+ {
+ this.payload = new byte[len];
+ System.arraycopy(payload, off, this.payload, 0, len);
+
+ TypesReader tr = new TypesReader(payload, off, len);
+
+ int packet_type = tr.readByte();
+
+ if (packet_type != Packets.SSH_MSG_KEXINIT)
+ throw new IOException("This is not a KexInitPacket! (" + packet_type + ")");
+
+ kp.cookie = tr.readBytes(16);
+ kp.kex_algorithms = tr.readNameList();
+ kp.server_host_key_algorithms = tr.readNameList();
+ kp.encryption_algorithms_client_to_server = tr.readNameList();
+ kp.encryption_algorithms_server_to_client = tr.readNameList();
+ kp.mac_algorithms_client_to_server = tr.readNameList();
+ kp.mac_algorithms_server_to_client = tr.readNameList();
+ kp.compression_algorithms_client_to_server = tr.readNameList();
+ kp.compression_algorithms_server_to_client = tr.readNameList();
+ kp.languages_client_to_server = tr.readNameList();
+ kp.languages_server_to_client = tr.readNameList();
+ kp.first_kex_packet_follows = tr.readBoolean();
+ kp.reserved_field1 = tr.readUINT32();
+
+ if (tr.remain() != 0)
+ throw new IOException("Padding in KexInitPacket!");
+ }
+
+ public byte[] getPayload()
+ {
+ if (payload == null)
+ {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_KEXINIT);
+ tw.writeBytes(kp.cookie, 0, 16);
+ tw.writeNameList(kp.kex_algorithms);
+ tw.writeNameList(kp.server_host_key_algorithms);
+ tw.writeNameList(kp.encryption_algorithms_client_to_server);
+ tw.writeNameList(kp.encryption_algorithms_server_to_client);
+ tw.writeNameList(kp.mac_algorithms_client_to_server);
+ tw.writeNameList(kp.mac_algorithms_server_to_client);
+ tw.writeNameList(kp.compression_algorithms_client_to_server);
+ tw.writeNameList(kp.compression_algorithms_server_to_client);
+ tw.writeNameList(kp.languages_client_to_server);
+ tw.writeNameList(kp.languages_server_to_client);
+ tw.writeBoolean(kp.first_kex_packet_follows);
+ tw.writeUINT32(kp.reserved_field1);
+ payload = tw.getBytes();
+ }
+ return payload;
+ }
+
+ public KexParameters getKexParameters()
+ {
+ return kp;
+ }
+
+ public String[] getCompression_algorithms_client_to_server()
+ {
+ return kp.compression_algorithms_client_to_server;
+ }
+
+ public String[] getCompression_algorithms_server_to_client()
+ {
+ return kp.compression_algorithms_server_to_client;
+ }
+
+ public byte[] getCookie()
+ {
+ return kp.cookie;
+ }
+
+ public String[] getEncryption_algorithms_client_to_server()
+ {
+ return kp.encryption_algorithms_client_to_server;
+ }
+
+ public String[] getEncryption_algorithms_server_to_client()
+ {
+ return kp.encryption_algorithms_server_to_client;
+ }
+
+ public boolean isFirst_kex_packet_follows()
+ {
+ return kp.first_kex_packet_follows;
+ }
+
+ public String[] getKex_algorithms()
+ {
+ return kp.kex_algorithms;
+ }
+
+ public String[] getLanguages_client_to_server()
+ {
+ return kp.languages_client_to_server;
+ }
+
+ public String[] getLanguages_server_to_client()
+ {
+ return kp.languages_server_to_client;
+ }
+
+ public String[] getMac_algorithms_client_to_server()
+ {
+ return kp.mac_algorithms_client_to_server;
+ }
+
+ public String[] getMac_algorithms_server_to_client()
+ {
+ return kp.mac_algorithms_server_to_client;
+ }
+
+ public int getReserved_field1()
+ {
+ return kp.reserved_field1;
+ }
+
+ public String[] getServer_host_key_algorithms()
+ {
+ return kp.server_host_key_algorithms;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketNewKeys.java b/src/com/trilead/ssh2/packets/PacketNewKeys.java
index 34ed7e7..3ca6503 100644
--- a/src/com/trilead/ssh2/packets/PacketNewKeys.java
+++ b/src/com/trilead/ssh2/packets/PacketNewKeys.java
@@ -1,46 +1,46 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketNewKeys.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketNewKeys.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketNewKeys
-{
- byte[] payload;
-
- public PacketNewKeys()
- {
- }
-
- public PacketNewKeys(byte payload[], int off, int len) throws IOException
- {
- this.payload = new byte[len];
- System.arraycopy(payload, off, this.payload, 0, len);
-
- TypesReader tr = new TypesReader(payload, off, len);
-
- int packet_type = tr.readByte();
-
- if (packet_type != Packets.SSH_MSG_NEWKEYS)
- throw new IOException("This is not a SSH_MSG_NEWKEYS! ("
- + packet_type + ")");
-
- if (tr.remain() != 0)
- throw new IOException("Padding in SSH_MSG_NEWKEYS packet!");
- }
-
- public byte[] getPayload()
- {
- if (payload == null)
- {
- TypesWriter tw = new TypesWriter();
- tw.writeByte(Packets.SSH_MSG_NEWKEYS);
- payload = tw.getBytes();
- }
- return payload;
- }
-}
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+/**
+ * PacketNewKeys.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketNewKeys.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class PacketNewKeys
+{
+ byte[] payload;
+
+ public PacketNewKeys()
+ {
+ }
+
+ public PacketNewKeys(byte payload[], int off, int len) throws IOException
+ {
+ this.payload = new byte[len];
+ System.arraycopy(payload, off, this.payload, 0, len);
+
+ TypesReader tr = new TypesReader(payload, off, len);
+
+ int packet_type = tr.readByte();
+
+ if (packet_type != Packets.SSH_MSG_NEWKEYS)
+ throw new IOException("This is not a SSH_MSG_NEWKEYS! ("
+ + packet_type + ")");
+
+ if (tr.remain() != 0)
+ throw new IOException("Padding in SSH_MSG_NEWKEYS packet!");
+ }
+
+ public byte[] getPayload()
+ {
+ if (payload == null)
+ {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_NEWKEYS);
+ payload = tw.getBytes();
+ }
+ return payload;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketOpenDirectTCPIPChannel.java b/src/com/trilead/ssh2/packets/PacketOpenDirectTCPIPChannel.java
index 289e70e..da6cbef 100644
--- a/src/com/trilead/ssh2/packets/PacketOpenDirectTCPIPChannel.java
+++ b/src/com/trilead/ssh2/packets/PacketOpenDirectTCPIPChannel.java
@@ -1,56 +1,56 @@
-package com.trilead.ssh2.packets;
-
-
-/**
- * PacketOpenDirectTCPIPChannel.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketOpenDirectTCPIPChannel.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketOpenDirectTCPIPChannel
-{
- byte[] payload;
-
- int channelID;
- int initialWindowSize;
- int maxPacketSize;
-
- String host_to_connect;
- int port_to_connect;
- String originator_IP_address;
- int originator_port;
-
- public PacketOpenDirectTCPIPChannel(int channelID, int initialWindowSize, int maxPacketSize,
- String host_to_connect, int port_to_connect, String originator_IP_address,
- int originator_port)
- {
- this.channelID = channelID;
- this.initialWindowSize = initialWindowSize;
- this.maxPacketSize = maxPacketSize;
- this.host_to_connect = host_to_connect;
- this.port_to_connect = port_to_connect;
- this.originator_IP_address = originator_IP_address;
- this.originator_port = originator_port;
- }
-
- public byte[] getPayload()
- {
- if (payload == null)
- {
- TypesWriter tw = new TypesWriter();
-
- tw.writeByte(Packets.SSH_MSG_CHANNEL_OPEN);
- tw.writeString("direct-tcpip");
- tw.writeUINT32(channelID);
- tw.writeUINT32(initialWindowSize);
- tw.writeUINT32(maxPacketSize);
- tw.writeString(host_to_connect);
- tw.writeUINT32(port_to_connect);
- tw.writeString(originator_IP_address);
- tw.writeUINT32(originator_port);
-
- payload = tw.getBytes();
- }
- return payload;
- }
-}
+package com.trilead.ssh2.packets;
+
+
+/**
+ * PacketOpenDirectTCPIPChannel.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketOpenDirectTCPIPChannel.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class PacketOpenDirectTCPIPChannel
+{
+ byte[] payload;
+
+ int channelID;
+ int initialWindowSize;
+ int maxPacketSize;
+
+ String host_to_connect;
+ int port_to_connect;
+ String originator_IP_address;
+ int originator_port;
+
+ public PacketOpenDirectTCPIPChannel(int channelID, int initialWindowSize, int maxPacketSize,
+ String host_to_connect, int port_to_connect, String originator_IP_address,
+ int originator_port)
+ {
+ this.channelID = channelID;
+ this.initialWindowSize = initialWindowSize;
+ this.maxPacketSize = maxPacketSize;
+ this.host_to_connect = host_to_connect;
+ this.port_to_connect = port_to_connect;
+ this.originator_IP_address = originator_IP_address;
+ this.originator_port = originator_port;
+ }
+
+ public byte[] getPayload()
+ {
+ if (payload == null)
+ {
+ TypesWriter tw = new TypesWriter();
+
+ tw.writeByte(Packets.SSH_MSG_CHANNEL_OPEN);
+ tw.writeString("direct-tcpip");
+ tw.writeUINT32(channelID);
+ tw.writeUINT32(initialWindowSize);
+ tw.writeUINT32(maxPacketSize);
+ tw.writeString(host_to_connect);
+ tw.writeUINT32(port_to_connect);
+ tw.writeString(originator_IP_address);
+ tw.writeUINT32(originator_port);
+
+ payload = tw.getBytes();
+ }
+ return payload;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketOpenSessionChannel.java b/src/com/trilead/ssh2/packets/PacketOpenSessionChannel.java
index ea69ad1..a75ea63 100644
--- a/src/com/trilead/ssh2/packets/PacketOpenSessionChannel.java
+++ b/src/com/trilead/ssh2/packets/PacketOpenSessionChannel.java
@@ -1,62 +1,62 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketOpenSessionChannel.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketOpenSessionChannel.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketOpenSessionChannel
-{
- byte[] payload;
-
- int channelID;
- int initialWindowSize;
- int maxPacketSize;
-
- public PacketOpenSessionChannel(int channelID, int initialWindowSize,
- int maxPacketSize)
- {
- this.channelID = channelID;
- this.initialWindowSize = initialWindowSize;
- this.maxPacketSize = maxPacketSize;
- }
-
- public PacketOpenSessionChannel(byte payload[], int off, int len) throws IOException
- {
- this.payload = new byte[len];
- System.arraycopy(payload, off, this.payload, 0, len);
-
- TypesReader tr = new TypesReader(payload);
-
- int packet_type = tr.readByte();
-
- if (packet_type != Packets.SSH_MSG_CHANNEL_OPEN)
- throw new IOException("This is not a SSH_MSG_CHANNEL_OPEN! ("
- + packet_type + ")");
-
- channelID = tr.readUINT32();
- initialWindowSize = tr.readUINT32();
- maxPacketSize = tr.readUINT32();
-
- if (tr.remain() != 0)
- throw new IOException("Padding in SSH_MSG_CHANNEL_OPEN packet!");
- }
-
- public byte[] getPayload()
- {
- if (payload == null)
- {
- TypesWriter tw = new TypesWriter();
- tw.writeByte(Packets.SSH_MSG_CHANNEL_OPEN);
- tw.writeString("session");
- tw.writeUINT32(channelID);
- tw.writeUINT32(initialWindowSize);
- tw.writeUINT32(maxPacketSize);
- payload = tw.getBytes();
- }
- return payload;
- }
-}
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+/**
+ * PacketOpenSessionChannel.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketOpenSessionChannel.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class PacketOpenSessionChannel
+{
+ byte[] payload;
+
+ int channelID;
+ int initialWindowSize;
+ int maxPacketSize;
+
+ public PacketOpenSessionChannel(int channelID, int initialWindowSize,
+ int maxPacketSize)
+ {
+ this.channelID = channelID;
+ this.initialWindowSize = initialWindowSize;
+ this.maxPacketSize = maxPacketSize;
+ }
+
+ public PacketOpenSessionChannel(byte payload[], int off, int len) throws IOException
+ {
+ this.payload = new byte[len];
+ System.arraycopy(payload, off, this.payload, 0, len);
+
+ TypesReader tr = new TypesReader(payload);
+
+ int packet_type = tr.readByte();
+
+ if (packet_type != Packets.SSH_MSG_CHANNEL_OPEN)
+ throw new IOException("This is not a SSH_MSG_CHANNEL_OPEN! ("
+ + packet_type + ")");
+
+ channelID = tr.readUINT32();
+ initialWindowSize = tr.readUINT32();
+ maxPacketSize = tr.readUINT32();
+
+ if (tr.remain() != 0)
+ throw new IOException("Padding in SSH_MSG_CHANNEL_OPEN packet!");
+ }
+
+ public byte[] getPayload()
+ {
+ if (payload == null)
+ {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_CHANNEL_OPEN);
+ tw.writeString("session");
+ tw.writeUINT32(channelID);
+ tw.writeUINT32(initialWindowSize);
+ tw.writeUINT32(maxPacketSize);
+ payload = tw.getBytes();
+ }
+ return payload;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketServiceAccept.java b/src/com/trilead/ssh2/packets/PacketServiceAccept.java
index 5081651..d5c9a90 100644
--- a/src/com/trilead/ssh2/packets/PacketServiceAccept.java
+++ b/src/com/trilead/ssh2/packets/PacketServiceAccept.java
@@ -1,61 +1,61 @@
-
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketServiceAccept.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketServiceAccept.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
- */
-public class PacketServiceAccept
-{
- byte[] payload;
-
- String serviceName;
-
- public PacketServiceAccept(String serviceName)
- {
- this.serviceName = serviceName;
- }
-
- public PacketServiceAccept(byte payload[], int off, int len) throws IOException
- {
- this.payload = new byte[len];
- System.arraycopy(payload, off, this.payload, 0, len);
-
- TypesReader tr = new TypesReader(payload, off, len);
-
- int packet_type = tr.readByte();
-
- if (packet_type != Packets.SSH_MSG_SERVICE_ACCEPT)
- throw new IOException("This is not a SSH_MSG_SERVICE_ACCEPT! (" + packet_type + ")");
-
- /* Be clever in case the server is not. Some servers seem to violate RFC4253 */
-
- if (tr.remain() > 0)
- {
- serviceName = tr.readString();
- }
- else
- {
- serviceName = "";
- }
-
- if (tr.remain() != 0)
- throw new IOException("Padding in SSH_MSG_SERVICE_ACCEPT packet!");
- }
-
- public byte[] getPayload()
- {
- if (payload == null)
- {
- TypesWriter tw = new TypesWriter();
- tw.writeByte(Packets.SSH_MSG_SERVICE_ACCEPT);
- tw.writeString(serviceName);
- payload = tw.getBytes();
- }
- return payload;
- }
-}
+
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+/**
+ * PacketServiceAccept.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketServiceAccept.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
+ */
+public class PacketServiceAccept
+{
+ byte[] payload;
+
+ String serviceName;
+
+ public PacketServiceAccept(String serviceName)
+ {
+ this.serviceName = serviceName;
+ }
+
+ public PacketServiceAccept(byte payload[], int off, int len) throws IOException
+ {
+ this.payload = new byte[len];
+ System.arraycopy(payload, off, this.payload, 0, len);
+
+ TypesReader tr = new TypesReader(payload, off, len);
+
+ int packet_type = tr.readByte();
+
+ if (packet_type != Packets.SSH_MSG_SERVICE_ACCEPT)
+ throw new IOException("This is not a SSH_MSG_SERVICE_ACCEPT! (" + packet_type + ")");
+
+ /* Be clever in case the server is not. Some servers seem to violate RFC4253 */
+
+ if (tr.remain() > 0)
+ {
+ serviceName = tr.readString();
+ }
+ else
+ {
+ serviceName = "";
+ }
+
+ if (tr.remain() != 0)
+ throw new IOException("Padding in SSH_MSG_SERVICE_ACCEPT packet!");
+ }
+
+ public byte[] getPayload()
+ {
+ if (payload == null)
+ {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_SERVICE_ACCEPT);
+ tw.writeString(serviceName);
+ payload = tw.getBytes();
+ }
+ return payload;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketServiceRequest.java b/src/com/trilead/ssh2/packets/PacketServiceRequest.java
index df5b8b4..c2d2065 100644
--- a/src/com/trilead/ssh2/packets/PacketServiceRequest.java
+++ b/src/com/trilead/ssh2/packets/PacketServiceRequest.java
@@ -1,52 +1,52 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketServiceRequest.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketServiceRequest.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketServiceRequest
-{
- byte[] payload;
-
- String serviceName;
-
- public PacketServiceRequest(String serviceName)
- {
- this.serviceName = serviceName;
- }
-
- public PacketServiceRequest(byte payload[], int off, int len) throws IOException
- {
- this.payload = new byte[len];
- System.arraycopy(payload, off, this.payload, 0, len);
-
- TypesReader tr = new TypesReader(payload, off, len);
-
- int packet_type = tr.readByte();
-
- if (packet_type != Packets.SSH_MSG_SERVICE_REQUEST)
- throw new IOException("This is not a SSH_MSG_SERVICE_REQUEST! ("
- + packet_type + ")");
-
- serviceName = tr.readString();
-
- if (tr.remain() != 0)
- throw new IOException("Padding in SSH_MSG_SERVICE_REQUEST packet!");
- }
-
- public byte[] getPayload()
- {
- if (payload == null)
- {
- TypesWriter tw = new TypesWriter();
- tw.writeByte(Packets.SSH_MSG_SERVICE_REQUEST);
- tw.writeString(serviceName);
- payload = tw.getBytes();
- }
- return payload;
- }
-}
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+/**
+ * PacketServiceRequest.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketServiceRequest.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class PacketServiceRequest
+{
+ byte[] payload;
+
+ String serviceName;
+
+ public PacketServiceRequest(String serviceName)
+ {
+ this.serviceName = serviceName;
+ }
+
+ public PacketServiceRequest(byte payload[], int off, int len) throws IOException
+ {
+ this.payload = new byte[len];
+ System.arraycopy(payload, off, this.payload, 0, len);
+
+ TypesReader tr = new TypesReader(payload, off, len);
+
+ int packet_type = tr.readByte();
+
+ if (packet_type != Packets.SSH_MSG_SERVICE_REQUEST)
+ throw new IOException("This is not a SSH_MSG_SERVICE_REQUEST! ("
+ + packet_type + ")");
+
+ serviceName = tr.readString();
+
+ if (tr.remain() != 0)
+ throw new IOException("Padding in SSH_MSG_SERVICE_REQUEST packet!");
+ }
+
+ public byte[] getPayload()
+ {
+ if (payload == null)
+ {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_SERVICE_REQUEST);
+ tw.writeString(serviceName);
+ payload = tw.getBytes();
+ }
+ return payload;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketSessionExecCommand.java b/src/com/trilead/ssh2/packets/PacketSessionExecCommand.java
index 5f459cf..84efa5d 100644
--- a/src/com/trilead/ssh2/packets/PacketSessionExecCommand.java
+++ b/src/com/trilead/ssh2/packets/PacketSessionExecCommand.java
@@ -1,39 +1,39 @@
-package com.trilead.ssh2.packets;
-
-
-/**
- * PacketSessionExecCommand.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketSessionExecCommand.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketSessionExecCommand
-{
- byte[] payload;
-
- public int recipientChannelID;
- public boolean wantReply;
- public String command;
-
- public PacketSessionExecCommand(int recipientChannelID, boolean wantReply, String command)
- {
- this.recipientChannelID = recipientChannelID;
- this.wantReply = wantReply;
- this.command = command;
- }
-
- public byte[] getPayload()
- {
- if (payload == null)
- {
- TypesWriter tw = new TypesWriter();
- tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
- tw.writeUINT32(recipientChannelID);
- tw.writeString("exec");
- tw.writeBoolean(wantReply);
- tw.writeString(command);
- payload = tw.getBytes();
- }
- return payload;
- }
-}
+package com.trilead.ssh2.packets;
+
+
+/**
+ * PacketSessionExecCommand.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketSessionExecCommand.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class PacketSessionExecCommand
+{
+ byte[] payload;
+
+ public int recipientChannelID;
+ public boolean wantReply;
+ public String command;
+
+ public PacketSessionExecCommand(int recipientChannelID, boolean wantReply, String command)
+ {
+ this.recipientChannelID = recipientChannelID;
+ this.wantReply = wantReply;
+ this.command = command;
+ }
+
+ public byte[] getPayload()
+ {
+ if (payload == null)
+ {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
+ tw.writeUINT32(recipientChannelID);
+ tw.writeString("exec");
+ tw.writeBoolean(wantReply);
+ tw.writeString(command);
+ payload = tw.getBytes();
+ }
+ return payload;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketSessionPtyRequest.java b/src/com/trilead/ssh2/packets/PacketSessionPtyRequest.java
index 93dd5ed..d9c3d59 100644
--- a/src/com/trilead/ssh2/packets/PacketSessionPtyRequest.java
+++ b/src/com/trilead/ssh2/packets/PacketSessionPtyRequest.java
@@ -1,57 +1,57 @@
-package com.trilead.ssh2.packets;
-
-
-/**
- * PacketSessionPtyRequest.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketSessionPtyRequest.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketSessionPtyRequest
-{
- byte[] payload;
-
- public int recipientChannelID;
- public boolean wantReply;
- public String term;
- public int character_width;
- public int character_height;
- public int pixel_width;
- public int pixel_height;
- public byte[] terminal_modes;
-
- public PacketSessionPtyRequest(int recipientChannelID, boolean wantReply, String term,
- int character_width, int character_height, int pixel_width, int pixel_height,
- byte[] terminal_modes)
- {
- this.recipientChannelID = recipientChannelID;
- this.wantReply = wantReply;
- this.term = term;
- this.character_width = character_width;
- this.character_height = character_height;
- this.pixel_width = pixel_width;
- this.pixel_height = pixel_height;
- this.terminal_modes = terminal_modes;
- }
-
- public byte[] getPayload()
- {
- if (payload == null)
- {
- TypesWriter tw = new TypesWriter();
- tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
- tw.writeUINT32(recipientChannelID);
- tw.writeString("pty-req");
- tw.writeBoolean(wantReply);
- tw.writeString(term);
- tw.writeUINT32(character_width);
- tw.writeUINT32(character_height);
- tw.writeUINT32(pixel_width);
- tw.writeUINT32(pixel_height);
- tw.writeString(terminal_modes, 0, terminal_modes.length);
-
- payload = tw.getBytes();
- }
- return payload;
- }
-}
+package com.trilead.ssh2.packets;
+
+
+/**
+ * PacketSessionPtyRequest.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketSessionPtyRequest.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class PacketSessionPtyRequest
+{
+ byte[] payload;
+
+ public int recipientChannelID;
+ public boolean wantReply;
+ public String term;
+ public int character_width;
+ public int character_height;
+ public int pixel_width;
+ public int pixel_height;
+ public byte[] terminal_modes;
+
+ public PacketSessionPtyRequest(int recipientChannelID, boolean wantReply, String term,
+ int character_width, int character_height, int pixel_width, int pixel_height,
+ byte[] terminal_modes)
+ {
+ this.recipientChannelID = recipientChannelID;
+ this.wantReply = wantReply;
+ this.term = term;
+ this.character_width = character_width;
+ this.character_height = character_height;
+ this.pixel_width = pixel_width;
+ this.pixel_height = pixel_height;
+ this.terminal_modes = terminal_modes;
+ }
+
+ public byte[] getPayload()
+ {
+ if (payload == null)
+ {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
+ tw.writeUINT32(recipientChannelID);
+ tw.writeString("pty-req");
+ tw.writeBoolean(wantReply);
+ tw.writeString(term);
+ tw.writeUINT32(character_width);
+ tw.writeUINT32(character_height);
+ tw.writeUINT32(pixel_width);
+ tw.writeUINT32(pixel_height);
+ tw.writeString(terminal_modes, 0, terminal_modes.length);
+
+ payload = tw.getBytes();
+ }
+ return payload;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketSessionStartShell.java b/src/com/trilead/ssh2/packets/PacketSessionStartShell.java
index edfc85b..e5add01 100644
--- a/src/com/trilead/ssh2/packets/PacketSessionStartShell.java
+++ b/src/com/trilead/ssh2/packets/PacketSessionStartShell.java
@@ -1,36 +1,36 @@
-
-package com.trilead.ssh2.packets;
-
-/**
- * PacketSessionStartShell.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketSessionStartShell.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketSessionStartShell
-{
- byte[] payload;
-
- public int recipientChannelID;
- public boolean wantReply;
-
- public PacketSessionStartShell(int recipientChannelID, boolean wantReply)
- {
- this.recipientChannelID = recipientChannelID;
- this.wantReply = wantReply;
- }
-
- public byte[] getPayload()
- {
- if (payload == null)
- {
- TypesWriter tw = new TypesWriter();
- tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
- tw.writeUINT32(recipientChannelID);
- tw.writeString("shell");
- tw.writeBoolean(wantReply);
- payload = tw.getBytes();
- }
- return payload;
- }
-}
+
+package com.trilead.ssh2.packets;
+
+/**
+ * PacketSessionStartShell.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketSessionStartShell.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class PacketSessionStartShell
+{
+ byte[] payload;
+
+ public int recipientChannelID;
+ public boolean wantReply;
+
+ public PacketSessionStartShell(int recipientChannelID, boolean wantReply)
+ {
+ this.recipientChannelID = recipientChannelID;
+ this.wantReply = wantReply;
+ }
+
+ public byte[] getPayload()
+ {
+ if (payload == null)
+ {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
+ tw.writeUINT32(recipientChannelID);
+ tw.writeString("shell");
+ tw.writeBoolean(wantReply);
+ payload = tw.getBytes();
+ }
+ return payload;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketSessionSubsystemRequest.java b/src/com/trilead/ssh2/packets/PacketSessionSubsystemRequest.java
index 3aa77ba..cdc3a8c 100644
--- a/src/com/trilead/ssh2/packets/PacketSessionSubsystemRequest.java
+++ b/src/com/trilead/ssh2/packets/PacketSessionSubsystemRequest.java
@@ -1,40 +1,40 @@
-package com.trilead.ssh2.packets;
-
-
-/**
- * PacketSessionSubsystemRequest.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketSessionSubsystemRequest.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketSessionSubsystemRequest
-{
- byte[] payload;
-
- public int recipientChannelID;
- public boolean wantReply;
- public String subsystem;
-
- public PacketSessionSubsystemRequest(int recipientChannelID, boolean wantReply, String subsystem)
- {
- this.recipientChannelID = recipientChannelID;
- this.wantReply = wantReply;
- this.subsystem = subsystem;
- }
-
- public byte[] getPayload()
- {
- if (payload == null)
- {
- TypesWriter tw = new TypesWriter();
- tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
- tw.writeUINT32(recipientChannelID);
- tw.writeString("subsystem");
- tw.writeBoolean(wantReply);
- tw.writeString(subsystem);
- payload = tw.getBytes();
- tw.getBytes(payload);
- }
- return payload;
- }
-}
+package com.trilead.ssh2.packets;
+
+
+/**
+ * PacketSessionSubsystemRequest.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketSessionSubsystemRequest.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class PacketSessionSubsystemRequest
+{
+ byte[] payload;
+
+ public int recipientChannelID;
+ public boolean wantReply;
+ public String subsystem;
+
+ public PacketSessionSubsystemRequest(int recipientChannelID, boolean wantReply, String subsystem)
+ {
+ this.recipientChannelID = recipientChannelID;
+ this.wantReply = wantReply;
+ this.subsystem = subsystem;
+ }
+
+ public byte[] getPayload()
+ {
+ if (payload == null)
+ {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
+ tw.writeUINT32(recipientChannelID);
+ tw.writeString("subsystem");
+ tw.writeBoolean(wantReply);
+ tw.writeString(subsystem);
+ payload = tw.getBytes();
+ tw.getBytes(payload);
+ }
+ return payload;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketSessionX11Request.java b/src/com/trilead/ssh2/packets/PacketSessionX11Request.java
index 5cc1d14..26479c7 100644
--- a/src/com/trilead/ssh2/packets/PacketSessionX11Request.java
+++ b/src/com/trilead/ssh2/packets/PacketSessionX11Request.java
@@ -1,53 +1,53 @@
-
-package com.trilead.ssh2.packets;
-
-/**
- * PacketSessionX11Request.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketSessionX11Request.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketSessionX11Request
-{
- byte[] payload;
-
- public int recipientChannelID;
- public boolean wantReply;
-
- public boolean singleConnection;
- String x11AuthenticationProtocol;
- String x11AuthenticationCookie;
- int x11ScreenNumber;
-
- public PacketSessionX11Request(int recipientChannelID, boolean wantReply, boolean singleConnection,
- String x11AuthenticationProtocol, String x11AuthenticationCookie, int x11ScreenNumber)
- {
- this.recipientChannelID = recipientChannelID;
- this.wantReply = wantReply;
-
- this.singleConnection = singleConnection;
- this.x11AuthenticationProtocol = x11AuthenticationProtocol;
- this.x11AuthenticationCookie = x11AuthenticationCookie;
- this.x11ScreenNumber = x11ScreenNumber;
- }
-
- public byte[] getPayload()
- {
- if (payload == null)
- {
- TypesWriter tw = new TypesWriter();
- tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
- tw.writeUINT32(recipientChannelID);
- tw.writeString("x11-req");
- tw.writeBoolean(wantReply);
-
- tw.writeBoolean(singleConnection);
- tw.writeString(x11AuthenticationProtocol);
- tw.writeString(x11AuthenticationCookie);
- tw.writeUINT32(x11ScreenNumber);
-
- payload = tw.getBytes();
- }
- return payload;
- }
-}
+
+package com.trilead.ssh2.packets;
+
+/**
+ * PacketSessionX11Request.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketSessionX11Request.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class PacketSessionX11Request
+{
+ byte[] payload;
+
+ public int recipientChannelID;
+ public boolean wantReply;
+
+ public boolean singleConnection;
+ String x11AuthenticationProtocol;
+ String x11AuthenticationCookie;
+ int x11ScreenNumber;
+
+ public PacketSessionX11Request(int recipientChannelID, boolean wantReply, boolean singleConnection,
+ String x11AuthenticationProtocol, String x11AuthenticationCookie, int x11ScreenNumber)
+ {
+ this.recipientChannelID = recipientChannelID;
+ this.wantReply = wantReply;
+
+ this.singleConnection = singleConnection;
+ this.x11AuthenticationProtocol = x11AuthenticationProtocol;
+ this.x11AuthenticationCookie = x11AuthenticationCookie;
+ this.x11ScreenNumber = x11ScreenNumber;
+ }
+
+ public byte[] getPayload()
+ {
+ if (payload == null)
+ {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
+ tw.writeUINT32(recipientChannelID);
+ tw.writeString("x11-req");
+ tw.writeBoolean(wantReply);
+
+ tw.writeBoolean(singleConnection);
+ tw.writeString(x11AuthenticationProtocol);
+ tw.writeString(x11AuthenticationCookie);
+ tw.writeUINT32(x11ScreenNumber);
+
+ payload = tw.getBytes();
+ }
+ return payload;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketUserauthBanner.java b/src/com/trilead/ssh2/packets/PacketUserauthBanner.java
index 2eafc5e..8ad8c3b 100644
--- a/src/com/trilead/ssh2/packets/PacketUserauthBanner.java
+++ b/src/com/trilead/ssh2/packets/PacketUserauthBanner.java
@@ -1,60 +1,60 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketUserauthBanner.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketUserauthBanner.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketUserauthBanner
-{
- byte[] payload;
-
- String message;
- String language;
-
- public PacketUserauthBanner(String message, String language)
- {
- this.message = message;
- this.language = language;
- }
-
- public String getBanner()
- {
- return message;
- }
-
- public PacketUserauthBanner(byte payload[], int off, int len) throws IOException
- {
- this.payload = new byte[len];
- System.arraycopy(payload, off, this.payload, 0, len);
-
- TypesReader tr = new TypesReader(payload, off, len);
-
- int packet_type = tr.readByte();
-
- if (packet_type != Packets.SSH_MSG_USERAUTH_BANNER)
- throw new IOException("This is not a SSH_MSG_USERAUTH_BANNER! (" + packet_type + ")");
-
- message = tr.readString("UTF-8");
- language = tr.readString();
-
- if (tr.remain() != 0)
- throw new IOException("Padding in SSH_MSG_USERAUTH_REQUEST packet!");
- }
-
- public byte[] getPayload()
- {
- if (payload == null)
- {
- TypesWriter tw = new TypesWriter();
- tw.writeByte(Packets.SSH_MSG_USERAUTH_BANNER);
- tw.writeString(message);
- tw.writeString(language);
- payload = tw.getBytes();
- }
- return payload;
- }
-}
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+/**
+ * PacketUserauthBanner.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketUserauthBanner.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class PacketUserauthBanner
+{
+ byte[] payload;
+
+ String message;
+ String language;
+
+ public PacketUserauthBanner(String message, String language)
+ {
+ this.message = message;
+ this.language = language;
+ }
+
+ public String getBanner()
+ {
+ return message;
+ }
+
+ public PacketUserauthBanner(byte payload[], int off, int len) throws IOException
+ {
+ this.payload = new byte[len];
+ System.arraycopy(payload, off, this.payload, 0, len);
+
+ TypesReader tr = new TypesReader(payload, off, len);
+
+ int packet_type = tr.readByte();
+
+ if (packet_type != Packets.SSH_MSG_USERAUTH_BANNER)
+ throw new IOException("This is not a SSH_MSG_USERAUTH_BANNER! (" + packet_type + ")");
+
+ message = tr.readString("UTF-8");
+ language = tr.readString();
+
+ if (tr.remain() != 0)
+ throw new IOException("Padding in SSH_MSG_USERAUTH_REQUEST packet!");
+ }
+
+ public byte[] getPayload()
+ {
+ if (payload == null)
+ {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_USERAUTH_BANNER);
+ tw.writeString(message);
+ tw.writeString(language);
+ payload = tw.getBytes();
+ }
+ return payload;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketUserauthFailure.java b/src/com/trilead/ssh2/packets/PacketUserauthFailure.java
index 77e9cf9..fd4a726 100644
--- a/src/com/trilead/ssh2/packets/PacketUserauthFailure.java
+++ b/src/com/trilead/ssh2/packets/PacketUserauthFailure.java
@@ -1,53 +1,53 @@
-
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketUserauthBanner.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketUserauthFailure.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketUserauthFailure
-{
- byte[] payload;
-
- String[] authThatCanContinue;
- boolean partialSuccess;
-
- public PacketUserauthFailure(String[] authThatCanContinue, boolean partialSuccess)
- {
- this.authThatCanContinue = authThatCanContinue;
- this.partialSuccess = partialSuccess;
- }
-
- public PacketUserauthFailure(byte payload[], int off, int len) throws IOException
- {
- this.payload = new byte[len];
- System.arraycopy(payload, off, this.payload, 0, len);
-
- TypesReader tr = new TypesReader(payload, off, len);
-
- int packet_type = tr.readByte();
-
- if (packet_type != Packets.SSH_MSG_USERAUTH_FAILURE)
- throw new IOException("This is not a SSH_MSG_USERAUTH_FAILURE! (" + packet_type + ")");
-
- authThatCanContinue = tr.readNameList();
- partialSuccess = tr.readBoolean();
-
- if (tr.remain() != 0)
- throw new IOException("Padding in SSH_MSG_USERAUTH_FAILURE packet!");
- }
-
- public String[] getAuthThatCanContinue()
- {
- return authThatCanContinue;
- }
-
- public boolean isPartialSuccess()
- {
- return partialSuccess;
- }
-}
+
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+/**
+ * PacketUserauthBanner.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketUserauthFailure.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class PacketUserauthFailure
+{
+ byte[] payload;
+
+ String[] authThatCanContinue;
+ boolean partialSuccess;
+
+ public PacketUserauthFailure(String[] authThatCanContinue, boolean partialSuccess)
+ {
+ this.authThatCanContinue = authThatCanContinue;
+ this.partialSuccess = partialSuccess;
+ }
+
+ public PacketUserauthFailure(byte payload[], int off, int len) throws IOException
+ {
+ this.payload = new byte[len];
+ System.arraycopy(payload, off, this.payload, 0, len);
+
+ TypesReader tr = new TypesReader(payload, off, len);
+
+ int packet_type = tr.readByte();
+
+ if (packet_type != Packets.SSH_MSG_USERAUTH_FAILURE)
+ throw new IOException("This is not a SSH_MSG_USERAUTH_FAILURE! (" + packet_type + ")");
+
+ authThatCanContinue = tr.readNameList();
+ partialSuccess = tr.readBoolean();
+
+ if (tr.remain() != 0)
+ throw new IOException("Padding in SSH_MSG_USERAUTH_FAILURE packet!");
+ }
+
+ public String[] getAuthThatCanContinue()
+ {
+ return authThatCanContinue;
+ }
+
+ public boolean isPartialSuccess()
+ {
+ return partialSuccess;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketUserauthInfoRequest.java b/src/com/trilead/ssh2/packets/PacketUserauthInfoRequest.java
index 75a0730..e1606d1 100644
--- a/src/com/trilead/ssh2/packets/PacketUserauthInfoRequest.java
+++ b/src/com/trilead/ssh2/packets/PacketUserauthInfoRequest.java
@@ -1,84 +1,84 @@
-
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketUserauthInfoRequest.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketUserauthInfoRequest.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketUserauthInfoRequest
-{
- byte[] payload;
-
- String name;
- String instruction;
- String languageTag;
- int numPrompts;
-
- String prompt[];
- boolean echo[];
-
- public PacketUserauthInfoRequest(byte payload[], int off, int len) throws IOException
- {
- this.payload = new byte[len];
- System.arraycopy(payload, off, this.payload, 0, len);
-
- TypesReader tr = new TypesReader(payload, off, len);
-
- int packet_type = tr.readByte();
-
- if (packet_type != Packets.SSH_MSG_USERAUTH_INFO_REQUEST)
- throw new IOException("This is not a SSH_MSG_USERAUTH_INFO_REQUEST! (" + packet_type + ")");
-
- name = tr.readString();
- instruction = tr.readString();
- languageTag = tr.readString();
-
- numPrompts = tr.readUINT32();
-
- prompt = new String[numPrompts];
- echo = new boolean[numPrompts];
-
- for (int i = 0; i < numPrompts; i++)
- {
- prompt[i] = tr.readString();
- echo[i] = tr.readBoolean();
- }
-
- if (tr.remain() != 0)
- throw new IOException("Padding in SSH_MSG_USERAUTH_INFO_REQUEST packet!");
- }
-
- public boolean[] getEcho()
- {
- return echo;
- }
-
- public String getInstruction()
- {
- return instruction;
- }
-
- public String getLanguageTag()
- {
- return languageTag;
- }
-
- public String getName()
- {
- return name;
- }
-
- public int getNumPrompts()
- {
- return numPrompts;
- }
-
- public String[] getPrompt()
- {
- return prompt;
- }
-}
+
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+/**
+ * PacketUserauthInfoRequest.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketUserauthInfoRequest.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class PacketUserauthInfoRequest
+{
+ byte[] payload;
+
+ String name;
+ String instruction;
+ String languageTag;
+ int numPrompts;
+
+ String prompt[];
+ boolean echo[];
+
+ public PacketUserauthInfoRequest(byte payload[], int off, int len) throws IOException
+ {
+ this.payload = new byte[len];
+ System.arraycopy(payload, off, this.payload, 0, len);
+
+ TypesReader tr = new TypesReader(payload, off, len);
+
+ int packet_type = tr.readByte();
+
+ if (packet_type != Packets.SSH_MSG_USERAUTH_INFO_REQUEST)
+ throw new IOException("This is not a SSH_MSG_USERAUTH_INFO_REQUEST! (" + packet_type + ")");
+
+ name = tr.readString();
+ instruction = tr.readString();
+ languageTag = tr.readString();
+
+ numPrompts = tr.readUINT32();
+
+ prompt = new String[numPrompts];
+ echo = new boolean[numPrompts];
+
+ for (int i = 0; i < numPrompts; i++)
+ {
+ prompt[i] = tr.readString();
+ echo[i] = tr.readBoolean();
+ }
+
+ if (tr.remain() != 0)
+ throw new IOException("Padding in SSH_MSG_USERAUTH_INFO_REQUEST packet!");
+ }
+
+ public boolean[] getEcho()
+ {
+ return echo;
+ }
+
+ public String getInstruction()
+ {
+ return instruction;
+ }
+
+ public String getLanguageTag()
+ {
+ return languageTag;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public int getNumPrompts()
+ {
+ return numPrompts;
+ }
+
+ public String[] getPrompt()
+ {
+ return prompt;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketUserauthInfoResponse.java b/src/com/trilead/ssh2/packets/PacketUserauthInfoResponse.java
index b06f0b5..e8795d4 100644
--- a/src/com/trilead/ssh2/packets/PacketUserauthInfoResponse.java
+++ b/src/com/trilead/ssh2/packets/PacketUserauthInfoResponse.java
@@ -1,35 +1,35 @@
-
-package com.trilead.ssh2.packets;
-
-/**
- * PacketUserauthInfoResponse.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketUserauthInfoResponse.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketUserauthInfoResponse
-{
- byte[] payload;
-
- String[] responses;
-
- public PacketUserauthInfoResponse(String[] responses)
- {
- this.responses = responses;
- }
-
- public byte[] getPayload()
- {
- if (payload == null)
- {
- TypesWriter tw = new TypesWriter();
- tw.writeByte(Packets.SSH_MSG_USERAUTH_INFO_RESPONSE);
- tw.writeUINT32(responses.length);
- for (int i = 0; i < responses.length; i++)
- tw.writeString(responses[i]);
-
- payload = tw.getBytes();
- }
- return payload;
- }
-}
+
+package com.trilead.ssh2.packets;
+
+/**
+ * PacketUserauthInfoResponse.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketUserauthInfoResponse.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class PacketUserauthInfoResponse
+{
+ byte[] payload;
+
+ String[] responses;
+
+ public PacketUserauthInfoResponse(String[] responses)
+ {
+ this.responses = responses;
+ }
+
+ public byte[] getPayload()
+ {
+ if (payload == null)
+ {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_USERAUTH_INFO_RESPONSE);
+ tw.writeUINT32(responses.length);
+ for (int i = 0; i < responses.length; i++)
+ tw.writeString(responses[i]);
+
+ payload = tw.getBytes();
+ }
+ return payload;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketUserauthRequestInteractive.java b/src/com/trilead/ssh2/packets/PacketUserauthRequestInteractive.java
index e0efd29..83e9f49 100644
--- a/src/com/trilead/ssh2/packets/PacketUserauthRequestInteractive.java
+++ b/src/com/trilead/ssh2/packets/PacketUserauthRequestInteractive.java
@@ -1,42 +1,42 @@
-
-package com.trilead.ssh2.packets;
-
-/**
- * PacketUserauthRequestInteractive.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketUserauthRequestInteractive.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketUserauthRequestInteractive
-{
- byte[] payload;
-
- String userName;
- String serviceName;
- String[] submethods;
-
- public PacketUserauthRequestInteractive(String serviceName, String user, String[] submethods)
- {
- this.serviceName = serviceName;
- this.userName = user;
- this.submethods = submethods;
- }
-
- public byte[] getPayload()
- {
- if (payload == null)
- {
- TypesWriter tw = new TypesWriter();
- tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
- tw.writeString(userName);
- tw.writeString(serviceName);
- tw.writeString("keyboard-interactive");
- tw.writeString(""); // draft-ietf-secsh-newmodes-04.txt says that
- // the language tag should be empty.
- tw.writeNameList(submethods);
-
- payload = tw.getBytes();
- }
- return payload;
- }
-}
+
+package com.trilead.ssh2.packets;
+
+/**
+ * PacketUserauthRequestInteractive.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketUserauthRequestInteractive.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class PacketUserauthRequestInteractive
+{
+ byte[] payload;
+
+ String userName;
+ String serviceName;
+ String[] submethods;
+
+ public PacketUserauthRequestInteractive(String serviceName, String user, String[] submethods)
+ {
+ this.serviceName = serviceName;
+ this.userName = user;
+ this.submethods = submethods;
+ }
+
+ public byte[] getPayload()
+ {
+ if (payload == null)
+ {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
+ tw.writeString(userName);
+ tw.writeString(serviceName);
+ tw.writeString("keyboard-interactive");
+ tw.writeString(""); // draft-ietf-secsh-newmodes-04.txt says that
+ // the language tag should be empty.
+ tw.writeNameList(submethods);
+
+ payload = tw.getBytes();
+ }
+ return payload;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketUserauthRequestNone.java b/src/com/trilead/ssh2/packets/PacketUserauthRequestNone.java
index 93f89d9..d786003 100644
--- a/src/com/trilead/ssh2/packets/PacketUserauthRequestNone.java
+++ b/src/com/trilead/ssh2/packets/PacketUserauthRequestNone.java
@@ -1,61 +1,61 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketUserauthRequestPassword.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketUserauthRequestNone.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketUserauthRequestNone
-{
- byte[] payload;
-
- String userName;
- String serviceName;
-
- public PacketUserauthRequestNone(String serviceName, String user)
- {
- this.serviceName = serviceName;
- this.userName = user;
- }
-
- public PacketUserauthRequestNone(byte payload[], int off, int len) throws IOException
- {
- this.payload = new byte[len];
- System.arraycopy(payload, off, this.payload, 0, len);
-
- TypesReader tr = new TypesReader(payload, off, len);
-
- int packet_type = tr.readByte();
-
- if (packet_type != Packets.SSH_MSG_USERAUTH_REQUEST)
- throw new IOException("This is not a SSH_MSG_USERAUTH_REQUEST! (" + packet_type + ")");
-
- userName = tr.readString();
- serviceName = tr.readString();
-
- String method = tr.readString();
-
- if (method.equals("none") == false)
- throw new IOException("This is not a SSH_MSG_USERAUTH_REQUEST with type none!");
-
- if (tr.remain() != 0)
- throw new IOException("Padding in SSH_MSG_USERAUTH_REQUEST packet!");
- }
-
- public byte[] getPayload()
- {
- if (payload == null)
- {
- TypesWriter tw = new TypesWriter();
- tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
- tw.writeString(userName);
- tw.writeString(serviceName);
- tw.writeString("none");
- payload = tw.getBytes();
- }
- return payload;
- }
-}
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+/**
+ * PacketUserauthRequestPassword.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketUserauthRequestNone.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class PacketUserauthRequestNone
+{
+ byte[] payload;
+
+ String userName;
+ String serviceName;
+
+ public PacketUserauthRequestNone(String serviceName, String user)
+ {
+ this.serviceName = serviceName;
+ this.userName = user;
+ }
+
+ public PacketUserauthRequestNone(byte payload[], int off, int len) throws IOException
+ {
+ this.payload = new byte[len];
+ System.arraycopy(payload, off, this.payload, 0, len);
+
+ TypesReader tr = new TypesReader(payload, off, len);
+
+ int packet_type = tr.readByte();
+
+ if (packet_type != Packets.SSH_MSG_USERAUTH_REQUEST)
+ throw new IOException("This is not a SSH_MSG_USERAUTH_REQUEST! (" + packet_type + ")");
+
+ userName = tr.readString();
+ serviceName = tr.readString();
+
+ String method = tr.readString();
+
+ if (method.equals("none") == false)
+ throw new IOException("This is not a SSH_MSG_USERAUTH_REQUEST with type none!");
+
+ if (tr.remain() != 0)
+ throw new IOException("Padding in SSH_MSG_USERAUTH_REQUEST packet!");
+ }
+
+ public byte[] getPayload()
+ {
+ if (payload == null)
+ {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
+ tw.writeString(userName);
+ tw.writeString(serviceName);
+ tw.writeString("none");
+ payload = tw.getBytes();
+ }
+ return payload;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketUserauthRequestPassword.java b/src/com/trilead/ssh2/packets/PacketUserauthRequestPassword.java
index df7b36b..83047dd 100644
--- a/src/com/trilead/ssh2/packets/PacketUserauthRequestPassword.java
+++ b/src/com/trilead/ssh2/packets/PacketUserauthRequestPassword.java
@@ -1,67 +1,67 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketUserauthRequestPassword.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketUserauthRequestPassword.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketUserauthRequestPassword
-{
- byte[] payload;
-
- String userName;
- String serviceName;
- String password;
-
- public PacketUserauthRequestPassword(String serviceName, String user, String pass)
- {
- this.serviceName = serviceName;
- this.userName = user;
- this.password = pass;
- }
-
- public PacketUserauthRequestPassword(byte payload[], int off, int len) throws IOException
- {
- this.payload = new byte[len];
- System.arraycopy(payload, off, this.payload, 0, len);
-
- TypesReader tr = new TypesReader(payload, off, len);
-
- int packet_type = tr.readByte();
-
- if (packet_type != Packets.SSH_MSG_USERAUTH_REQUEST)
- throw new IOException("This is not a SSH_MSG_USERAUTH_REQUEST! (" + packet_type + ")");
-
- userName = tr.readString();
- serviceName = tr.readString();
-
- String method = tr.readString();
-
- if (method.equals("password") == false)
- throw new IOException("This is not a SSH_MSG_USERAUTH_REQUEST with type password!");
-
- /* ... */
-
- if (tr.remain() != 0)
- throw new IOException("Padding in SSH_MSG_USERAUTH_REQUEST packet!");
- }
-
- public byte[] getPayload()
- {
- if (payload == null)
- {
- TypesWriter tw = new TypesWriter();
- tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
- tw.writeString(userName);
- tw.writeString(serviceName);
- tw.writeString("password");
- tw.writeBoolean(false);
- tw.writeString(password);
- payload = tw.getBytes();
- }
- return payload;
- }
-}
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+/**
+ * PacketUserauthRequestPassword.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketUserauthRequestPassword.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class PacketUserauthRequestPassword
+{
+ byte[] payload;
+
+ String userName;
+ String serviceName;
+ String password;
+
+ public PacketUserauthRequestPassword(String serviceName, String user, String pass)
+ {
+ this.serviceName = serviceName;
+ this.userName = user;
+ this.password = pass;
+ }
+
+ public PacketUserauthRequestPassword(byte payload[], int off, int len) throws IOException
+ {
+ this.payload = new byte[len];
+ System.arraycopy(payload, off, this.payload, 0, len);
+
+ TypesReader tr = new TypesReader(payload, off, len);
+
+ int packet_type = tr.readByte();
+
+ if (packet_type != Packets.SSH_MSG_USERAUTH_REQUEST)
+ throw new IOException("This is not a SSH_MSG_USERAUTH_REQUEST! (" + packet_type + ")");
+
+ userName = tr.readString();
+ serviceName = tr.readString();
+
+ String method = tr.readString();
+
+ if (method.equals("password") == false)
+ throw new IOException("This is not a SSH_MSG_USERAUTH_REQUEST with type password!");
+
+ /* ... */
+
+ if (tr.remain() != 0)
+ throw new IOException("Padding in SSH_MSG_USERAUTH_REQUEST packet!");
+ }
+
+ public byte[] getPayload()
+ {
+ if (payload == null)
+ {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
+ tw.writeString(userName);
+ tw.writeString(serviceName);
+ tw.writeString("password");
+ tw.writeBoolean(false);
+ tw.writeString(password);
+ payload = tw.getBytes();
+ }
+ return payload;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/PacketUserauthRequestPublicKey.java b/src/com/trilead/ssh2/packets/PacketUserauthRequestPublicKey.java
index 1e38673..6462864 100644
--- a/src/com/trilead/ssh2/packets/PacketUserauthRequestPublicKey.java
+++ b/src/com/trilead/ssh2/packets/PacketUserauthRequestPublicKey.java
@@ -1,65 +1,65 @@
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-
-/**
- * PacketUserauthRequestPublicKey.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: PacketUserauthRequestPublicKey.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class PacketUserauthRequestPublicKey
-{
- byte[] payload;
-
- String userName;
- String serviceName;
- String password;
- String pkAlgoName;
- byte[] pk;
- byte[] sig;
-
- public PacketUserauthRequestPublicKey(String serviceName, String user,
- String pkAlgorithmName, byte[] pk, byte[] sig)
- {
- this.serviceName = serviceName;
- this.userName = user;
- this.pkAlgoName = pkAlgorithmName;
- this.pk = pk;
- this.sig = sig;
- }
-
- public PacketUserauthRequestPublicKey(byte payload[], int off, int len) throws IOException
- {
- this.payload = new byte[len];
- System.arraycopy(payload, off, this.payload, 0, len);
-
- TypesReader tr = new TypesReader(payload, off, len);
-
- int packet_type = tr.readByte();
-
- if (packet_type != Packets.SSH_MSG_USERAUTH_REQUEST)
- throw new IOException("This is not a SSH_MSG_USERAUTH_REQUEST! ("
- + packet_type + ")");
-
- throw new IOException("Not implemented!");
- }
-
- public byte[] getPayload()
- {
- if (payload == null)
- {
- TypesWriter tw = new TypesWriter();
- tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
- tw.writeString(userName);
- tw.writeString(serviceName);
- tw.writeString("publickey");
- tw.writeBoolean(true);
- tw.writeString(pkAlgoName);
- tw.writeString(pk, 0, pk.length);
- tw.writeString(sig, 0, sig.length);
- payload = tw.getBytes();
- }
- return payload;
- }
-}
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+
+/**
+ * PacketUserauthRequestPublicKey.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: PacketUserauthRequestPublicKey.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class PacketUserauthRequestPublicKey
+{
+ byte[] payload;
+
+ String userName;
+ String serviceName;
+ String password;
+ String pkAlgoName;
+ byte[] pk;
+ byte[] sig;
+
+ public PacketUserauthRequestPublicKey(String serviceName, String user,
+ String pkAlgorithmName, byte[] pk, byte[] sig)
+ {
+ this.serviceName = serviceName;
+ this.userName = user;
+ this.pkAlgoName = pkAlgorithmName;
+ this.pk = pk;
+ this.sig = sig;
+ }
+
+ public PacketUserauthRequestPublicKey(byte payload[], int off, int len) throws IOException
+ {
+ this.payload = new byte[len];
+ System.arraycopy(payload, off, this.payload, 0, len);
+
+ TypesReader tr = new TypesReader(payload, off, len);
+
+ int packet_type = tr.readByte();
+
+ if (packet_type != Packets.SSH_MSG_USERAUTH_REQUEST)
+ throw new IOException("This is not a SSH_MSG_USERAUTH_REQUEST! ("
+ + packet_type + ")");
+
+ throw new IOException("Not implemented!");
+ }
+
+ public byte[] getPayload()
+ {
+ if (payload == null)
+ {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
+ tw.writeString(userName);
+ tw.writeString(serviceName);
+ tw.writeString("publickey");
+ tw.writeBoolean(true);
+ tw.writeString(pkAlgoName);
+ tw.writeString(pk, 0, pk.length);
+ tw.writeString(sig, 0, sig.length);
+ payload = tw.getBytes();
+ }
+ return payload;
+ }
+}
diff --git a/src/com/trilead/ssh2/packets/Packets.java b/src/com/trilead/ssh2/packets/Packets.java
index bc9a6c0..6989286 100644
--- a/src/com/trilead/ssh2/packets/Packets.java
+++ b/src/com/trilead/ssh2/packets/Packets.java
@@ -1,149 +1,149 @@
-
-package com.trilead.ssh2.packets;
-
-/**
- * Packets.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: Packets.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class Packets
-{
- public static final int SSH_MSG_DISCONNECT = 1;
- public static final int SSH_MSG_IGNORE = 2;
- public static final int SSH_MSG_UNIMPLEMENTED = 3;
- public static final int SSH_MSG_DEBUG = 4;
- public static final int SSH_MSG_SERVICE_REQUEST = 5;
- public static final int SSH_MSG_SERVICE_ACCEPT = 6;
-
- public static final int SSH_MSG_KEXINIT = 20;
- public static final int SSH_MSG_NEWKEYS = 21;
-
- public static final int SSH_MSG_KEXDH_INIT = 30;
- public static final int SSH_MSG_KEXDH_REPLY = 31;
-
- public static final int SSH_MSG_KEX_DH_GEX_REQUEST_OLD = 30;
- public static final int SSH_MSG_KEX_DH_GEX_REQUEST = 34;
- public static final int SSH_MSG_KEX_DH_GEX_GROUP = 31;
- public static final int SSH_MSG_KEX_DH_GEX_INIT = 32;
- public static final int SSH_MSG_KEX_DH_GEX_REPLY = 33;
-
- public static final int SSH_MSG_USERAUTH_REQUEST = 50;
- public static final int SSH_MSG_USERAUTH_FAILURE = 51;
- public static final int SSH_MSG_USERAUTH_SUCCESS = 52;
- public static final int SSH_MSG_USERAUTH_BANNER = 53;
- public static final int SSH_MSG_USERAUTH_INFO_REQUEST = 60;
- public static final int SSH_MSG_USERAUTH_INFO_RESPONSE = 61;
-
- public static final int SSH_MSG_GLOBAL_REQUEST = 80;
- public static final int SSH_MSG_REQUEST_SUCCESS = 81;
- public static final int SSH_MSG_REQUEST_FAILURE = 82;
-
- public static final int SSH_MSG_CHANNEL_OPEN = 90;
- public static final int SSH_MSG_CHANNEL_OPEN_CONFIRMATION = 91;
- public static final int SSH_MSG_CHANNEL_OPEN_FAILURE = 92;
- public static final int SSH_MSG_CHANNEL_WINDOW_ADJUST = 93;
- public static final int SSH_MSG_CHANNEL_DATA = 94;
- public static final int SSH_MSG_CHANNEL_EXTENDED_DATA = 95;
- public static final int SSH_MSG_CHANNEL_EOF = 96;
- public static final int SSH_MSG_CHANNEL_CLOSE = 97;
- public static final int SSH_MSG_CHANNEL_REQUEST = 98;
- public static final int SSH_MSG_CHANNEL_SUCCESS = 99;
- public static final int SSH_MSG_CHANNEL_FAILURE = 100;
-
- public static final int SSH_EXTENDED_DATA_STDERR = 1;
-
- public static final int SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT = 1;
- public static final int SSH_DISCONNECT_PROTOCOL_ERROR = 2;
- public static final int SSH_DISCONNECT_KEY_EXCHANGE_FAILED = 3;
- public static final int SSH_DISCONNECT_RESERVED = 4;
- public static final int SSH_DISCONNECT_MAC_ERROR = 5;
- public static final int SSH_DISCONNECT_COMPRESSION_ERROR = 6;
- public static final int SSH_DISCONNECT_SERVICE_NOT_AVAILABLE = 7;
- public static final int SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED = 8;
- public static final int SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE = 9;
- public static final int SSH_DISCONNECT_CONNECTION_LOST = 10;
- public static final int SSH_DISCONNECT_BY_APPLICATION = 11;
- public static final int SSH_DISCONNECT_TOO_MANY_CONNECTIONS = 12;
- public static final int SSH_DISCONNECT_AUTH_CANCELLED_BY_USER = 13;
- public static final int SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE = 14;
- public static final int SSH_DISCONNECT_ILLEGAL_USER_NAME = 15;
-
- public static final int SSH_OPEN_ADMINISTRATIVELY_PROHIBITED = 1;
- public static final int SSH_OPEN_CONNECT_FAILED = 2;
- public static final int SSH_OPEN_UNKNOWN_CHANNEL_TYPE = 3;
- public static final int SSH_OPEN_RESOURCE_SHORTAGE = 4;
-
- private static final String[] reverseNames = new String[101];
-
- static
- {
- reverseNames[1] = "SSH_MSG_DISCONNECT";
- reverseNames[2] = "SSH_MSG_IGNORE";
- reverseNames[3] = "SSH_MSG_UNIMPLEMENTED";
- reverseNames[4] = "SSH_MSG_DEBUG";
- reverseNames[5] = "SSH_MSG_SERVICE_REQUEST";
- reverseNames[6] = "SSH_MSG_SERVICE_ACCEPT";
-
- reverseNames[20] = "SSH_MSG_KEXINIT";
- reverseNames[21] = "SSH_MSG_NEWKEYS";
-
- reverseNames[30] = "SSH_MSG_KEXDH_INIT";
- reverseNames[31] = "SSH_MSG_KEXDH_REPLY/SSH_MSG_KEX_DH_GEX_GROUP";
- reverseNames[32] = "SSH_MSG_KEX_DH_GEX_INIT";
- reverseNames[33] = "SSH_MSG_KEX_DH_GEX_REPLY";
- reverseNames[34] = "SSH_MSG_KEX_DH_GEX_REQUEST";
-
- reverseNames[50] = "SSH_MSG_USERAUTH_REQUEST";
- reverseNames[51] = "SSH_MSG_USERAUTH_FAILURE";
- reverseNames[52] = "SSH_MSG_USERAUTH_SUCCESS";
- reverseNames[53] = "SSH_MSG_USERAUTH_BANNER";
-
- reverseNames[60] = "SSH_MSG_USERAUTH_INFO_REQUEST";
- reverseNames[61] = "SSH_MSG_USERAUTH_INFO_RESPONSE";
-
- reverseNames[80] = "SSH_MSG_GLOBAL_REQUEST";
- reverseNames[81] = "SSH_MSG_REQUEST_SUCCESS";
- reverseNames[82] = "SSH_MSG_REQUEST_FAILURE";
-
- reverseNames[90] = "SSH_MSG_CHANNEL_OPEN";
- reverseNames[91] = "SSH_MSG_CHANNEL_OPEN_CONFIRMATION";
- reverseNames[92] = "SSH_MSG_CHANNEL_OPEN_FAILURE";
- reverseNames[93] = "SSH_MSG_CHANNEL_WINDOW_ADJUST";
- reverseNames[94] = "SSH_MSG_CHANNEL_DATA";
- reverseNames[95] = "SSH_MSG_CHANNEL_EXTENDED_DATA";
- reverseNames[96] = "SSH_MSG_CHANNEL_EOF";
- reverseNames[97] = "SSH_MSG_CHANNEL_CLOSE";
- reverseNames[98] = "SSH_MSG_CHANNEL_REQUEST";
- reverseNames[99] = "SSH_MSG_CHANNEL_SUCCESS";
- reverseNames[100] = "SSH_MSG_CHANNEL_FAILURE";
- }
-
- public static final String getMessageName(int type)
- {
- String res = null;
-
- if ((type >= 0) && (type < reverseNames.length))
- {
- res = reverseNames[type];
- }
-
- return (res == null) ? ("UNKNOWN MSG " + type) : res;
- }
-
- // public static final void debug(String tag, byte[] msg)
- // {
- // System.err.println(tag + " Type: " + msg[0] + ", LEN: " + msg.length);
- //
- // for (int i = 0; i < msg.length; i++)
- // {
- // if (((msg[i] >= 'a') && (msg[i] <= 'z')) || ((msg[i] >= 'A') && (msg[i] <= 'Z'))
- // || ((msg[i] >= '0') && (msg[i] <= '9')) || (msg[i] == ' '))
- // System.err.print((char) msg[i]);
- // else
- // System.err.print(".");
- // }
- // System.err.println();
- // System.err.flush();
- // }
-}
+
+package com.trilead.ssh2.packets;
+
+/**
+ * Packets.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: Packets.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class Packets
+{
+ public static final int SSH_MSG_DISCONNECT = 1;
+ public static final int SSH_MSG_IGNORE = 2;
+ public static final int SSH_MSG_UNIMPLEMENTED = 3;
+ public static final int SSH_MSG_DEBUG = 4;
+ public static final int SSH_MSG_SERVICE_REQUEST = 5;
+ public static final int SSH_MSG_SERVICE_ACCEPT = 6;
+
+ public static final int SSH_MSG_KEXINIT = 20;
+ public static final int SSH_MSG_NEWKEYS = 21;
+
+ public static final int SSH_MSG_KEXDH_INIT = 30;
+ public static final int SSH_MSG_KEXDH_REPLY = 31;
+
+ public static final int SSH_MSG_KEX_DH_GEX_REQUEST_OLD = 30;
+ public static final int SSH_MSG_KEX_DH_GEX_REQUEST = 34;
+ public static final int SSH_MSG_KEX_DH_GEX_GROUP = 31;
+ public static final int SSH_MSG_KEX_DH_GEX_INIT = 32;
+ public static final int SSH_MSG_KEX_DH_GEX_REPLY = 33;
+
+ public static final int SSH_MSG_USERAUTH_REQUEST = 50;
+ public static final int SSH_MSG_USERAUTH_FAILURE = 51;
+ public static final int SSH_MSG_USERAUTH_SUCCESS = 52;
+ public static final int SSH_MSG_USERAUTH_BANNER = 53;
+ public static final int SSH_MSG_USERAUTH_INFO_REQUEST = 60;
+ public static final int SSH_MSG_USERAUTH_INFO_RESPONSE = 61;
+
+ public static final int SSH_MSG_GLOBAL_REQUEST = 80;
+ public static final int SSH_MSG_REQUEST_SUCCESS = 81;
+ public static final int SSH_MSG_REQUEST_FAILURE = 82;
+
+ public static final int SSH_MSG_CHANNEL_OPEN = 90;
+ public static final int SSH_MSG_CHANNEL_OPEN_CONFIRMATION = 91;
+ public static final int SSH_MSG_CHANNEL_OPEN_FAILURE = 92;
+ public static final int SSH_MSG_CHANNEL_WINDOW_ADJUST = 93;
+ public static final int SSH_MSG_CHANNEL_DATA = 94;
+ public static final int SSH_MSG_CHANNEL_EXTENDED_DATA = 95;
+ public static final int SSH_MSG_CHANNEL_EOF = 96;
+ public static final int SSH_MSG_CHANNEL_CLOSE = 97;
+ public static final int SSH_MSG_CHANNEL_REQUEST = 98;
+ public static final int SSH_MSG_CHANNEL_SUCCESS = 99;
+ public static final int SSH_MSG_CHANNEL_FAILURE = 100;
+
+ public static final int SSH_EXTENDED_DATA_STDERR = 1;
+
+ public static final int SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT = 1;
+ public static final int SSH_DISCONNECT_PROTOCOL_ERROR = 2;
+ public static final int SSH_DISCONNECT_KEY_EXCHANGE_FAILED = 3;
+ public static final int SSH_DISCONNECT_RESERVED = 4;
+ public static final int SSH_DISCONNECT_MAC_ERROR = 5;
+ public static final int SSH_DISCONNECT_COMPRESSION_ERROR = 6;
+ public static final int SSH_DISCONNECT_SERVICE_NOT_AVAILABLE = 7;
+ public static final int SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED = 8;
+ public static final int SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE = 9;
+ public static final int SSH_DISCONNECT_CONNECTION_LOST = 10;
+ public static final int SSH_DISCONNECT_BY_APPLICATION = 11;
+ public static final int SSH_DISCONNECT_TOO_MANY_CONNECTIONS = 12;
+ public static final int SSH_DISCONNECT_AUTH_CANCELLED_BY_USER = 13;
+ public static final int SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE = 14;
+ public static final int SSH_DISCONNECT_ILLEGAL_USER_NAME = 15;
+
+ public static final int SSH_OPEN_ADMINISTRATIVELY_PROHIBITED = 1;
+ public static final int SSH_OPEN_CONNECT_FAILED = 2;
+ public static final int SSH_OPEN_UNKNOWN_CHANNEL_TYPE = 3;
+ public static final int SSH_OPEN_RESOURCE_SHORTAGE = 4;
+
+ private static final String[] reverseNames = new String[101];
+
+ static
+ {
+ reverseNames[1] = "SSH_MSG_DISCONNECT";
+ reverseNames[2] = "SSH_MSG_IGNORE";
+ reverseNames[3] = "SSH_MSG_UNIMPLEMENTED";
+ reverseNames[4] = "SSH_MSG_DEBUG";
+ reverseNames[5] = "SSH_MSG_SERVICE_REQUEST";
+ reverseNames[6] = "SSH_MSG_SERVICE_ACCEPT";
+
+ reverseNames[20] = "SSH_MSG_KEXINIT";
+ reverseNames[21] = "SSH_MSG_NEWKEYS";
+
+ reverseNames[30] = "SSH_MSG_KEXDH_INIT";
+ reverseNames[31] = "SSH_MSG_KEXDH_REPLY/SSH_MSG_KEX_DH_GEX_GROUP";
+ reverseNames[32] = "SSH_MSG_KEX_DH_GEX_INIT";
+ reverseNames[33] = "SSH_MSG_KEX_DH_GEX_REPLY";
+ reverseNames[34] = "SSH_MSG_KEX_DH_GEX_REQUEST";
+
+ reverseNames[50] = "SSH_MSG_USERAUTH_REQUEST";
+ reverseNames[51] = "SSH_MSG_USERAUTH_FAILURE";
+ reverseNames[52] = "SSH_MSG_USERAUTH_SUCCESS";
+ reverseNames[53] = "SSH_MSG_USERAUTH_BANNER";
+
+ reverseNames[60] = "SSH_MSG_USERAUTH_INFO_REQUEST";
+ reverseNames[61] = "SSH_MSG_USERAUTH_INFO_RESPONSE";
+
+ reverseNames[80] = "SSH_MSG_GLOBAL_REQUEST";
+ reverseNames[81] = "SSH_MSG_REQUEST_SUCCESS";
+ reverseNames[82] = "SSH_MSG_REQUEST_FAILURE";
+
+ reverseNames[90] = "SSH_MSG_CHANNEL_OPEN";
+ reverseNames[91] = "SSH_MSG_CHANNEL_OPEN_CONFIRMATION";
+ reverseNames[92] = "SSH_MSG_CHANNEL_OPEN_FAILURE";
+ reverseNames[93] = "SSH_MSG_CHANNEL_WINDOW_ADJUST";
+ reverseNames[94] = "SSH_MSG_CHANNEL_DATA";
+ reverseNames[95] = "SSH_MSG_CHANNEL_EXTENDED_DATA";
+ reverseNames[96] = "SSH_MSG_CHANNEL_EOF";
+ reverseNames[97] = "SSH_MSG_CHANNEL_CLOSE";
+ reverseNames[98] = "SSH_MSG_CHANNEL_REQUEST";
+ reverseNames[99] = "SSH_MSG_CHANNEL_SUCCESS";
+ reverseNames[100] = "SSH_MSG_CHANNEL_FAILURE";
+ }
+
+ public static final String getMessageName(int type)
+ {
+ String res = null;
+
+ if ((type >= 0) && (type < reverseNames.length))
+ {
+ res = reverseNames[type];
+ }
+
+ return (res == null) ? ("UNKNOWN MSG " + type) : res;
+ }
+
+ // public static final void debug(String tag, byte[] msg)
+ // {
+ // System.err.println(tag + " Type: " + msg[0] + ", LEN: " + msg.length);
+ //
+ // for (int i = 0; i < msg.length; i++)
+ // {
+ // if (((msg[i] >= 'a') && (msg[i] <= 'z')) || ((msg[i] >= 'A') && (msg[i] <= 'Z'))
+ // || ((msg[i] >= '0') && (msg[i] <= '9')) || (msg[i] == ' '))
+ // System.err.print((char) msg[i]);
+ // else
+ // System.err.print(".");
+ // }
+ // System.err.println();
+ // System.err.flush();
+ // }
+}
diff --git a/src/com/trilead/ssh2/packets/TypesReader.java b/src/com/trilead/ssh2/packets/TypesReader.java
index c8d73e7..28f5363 100644
--- a/src/com/trilead/ssh2/packets/TypesReader.java
+++ b/src/com/trilead/ssh2/packets/TypesReader.java
@@ -1,177 +1,177 @@
-
-package com.trilead.ssh2.packets;
-
-import java.io.IOException;
-import java.math.BigInteger;
-
-import com.trilead.ssh2.util.Tokenizer;
-
-
-/**
- * TypesReader.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: TypesReader.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
- */
-public class TypesReader
-{
- byte[] arr;
- int pos = 0;
- int max = 0;
-
- public TypesReader(byte[] arr)
- {
- this.arr = arr;
- pos = 0;
- max = arr.length;
- }
-
- public TypesReader(byte[] arr, int off)
- {
- this.arr = arr;
- this.pos = off;
- this.max = arr.length;
-
- if ((pos < 0) || (pos > arr.length))
- throw new IllegalArgumentException("Illegal offset.");
- }
-
- public TypesReader(byte[] arr, int off, int len)
- {
- this.arr = arr;
- this.pos = off;
- this.max = off + len;
-
- if ((pos < 0) || (pos > arr.length))
- throw new IllegalArgumentException("Illegal offset.");
-
- if ((max < 0) || (max > arr.length))
- throw new IllegalArgumentException("Illegal length.");
- }
-
- public int readByte() throws IOException
- {
- if (pos >= max)
- throw new IOException("Packet too short.");
-
- return (arr[pos++] & 0xff);
- }
-
- public byte[] readBytes(int len) throws IOException
- {
- if ((pos + len) > max)
- throw new IOException("Packet too short.");
-
- byte[] res = new byte[len];
-
- System.arraycopy(arr, pos, res, 0, len);
- pos += len;
-
- return res;
- }
-
- public void readBytes(byte[] dst, int off, int len) throws IOException
- {
- if ((pos + len) > max)
- throw new IOException("Packet too short.");
-
- System.arraycopy(arr, pos, dst, off, len);
- pos += len;
- }
-
- public boolean readBoolean() throws IOException
- {
- if (pos >= max)
- throw new IOException("Packet too short.");
-
- return (arr[pos++] != 0);
- }
-
- public int readUINT32() throws IOException
- {
- if ((pos + 4) > max)
- throw new IOException("Packet too short.");
-
- return ((arr[pos++] & 0xff) << 24) | ((arr[pos++] & 0xff) << 16) | ((arr[pos++] & 0xff) << 8)
- | (arr[pos++] & 0xff);
- }
-
- public long readUINT64() throws IOException
- {
- if ((pos + 8) > max)
- throw new IOException("Packet too short.");
-
- long high = ((arr[pos++] & 0xff) << 24) | ((arr[pos++] & 0xff) << 16) | ((arr[pos++] & 0xff) << 8)
- | (arr[pos++] & 0xff); /* sign extension may take place - will be shifted away =) */
-
- long low = ((arr[pos++] & 0xff) << 24) | ((arr[pos++] & 0xff) << 16) | ((arr[pos++] & 0xff) << 8)
- | (arr[pos++] & 0xff); /* sign extension may take place - handle below */
-
- return (high << 32) | (low & 0xffffffffl); /* see Java language spec (15.22.1, 5.6.2) */
- }
-
- public BigInteger readMPINT() throws IOException
- {
- BigInteger b;
-
- byte raw[] = readByteString();
-
- if (raw.length == 0)
- b = BigInteger.ZERO;
- else
- b = new BigInteger(raw);
-
- return b;
- }
-
- public byte[] readByteString() throws IOException
- {
- int len = readUINT32();
-
- if ((len + pos) > max)
- throw new IOException("Malformed SSH byte string.");
-
- byte[] res = new byte[len];
- System.arraycopy(arr, pos, res, 0, len);
- pos += len;
- return res;
- }
-
- public String readString(String charsetName) throws IOException
- {
- int len = readUINT32();
-
- if ((len + pos) > max)
- throw new IOException("Malformed SSH string.");
-
- String res = (charsetName == null) ? new String(arr, pos, len) : new String(arr, pos, len, charsetName);
- pos += len;
-
- return res;
- }
-
- public String readString() throws IOException
- {
- int len = readUINT32();
-
- if ((len + pos) > max)
- throw new IOException("Malformed SSH string.");
-
- String res = new String(arr, pos, len, "ISO-8859-1");
-
- pos += len;
-
- return res;
- }
-
- public String[] readNameList() throws IOException
- {
- return Tokenizer.parseTokens(readString(), ',');
- }
-
- public int remain()
- {
- return max - pos;
- }
-
-}
+
+package com.trilead.ssh2.packets;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+import com.trilead.ssh2.util.Tokenizer;
+
+
+/**
+ * TypesReader.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: TypesReader.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
+ */
+public class TypesReader
+{
+ byte[] arr;
+ int pos = 0;
+ int max = 0;
+
+ public TypesReader(byte[] arr)
+ {
+ this.arr = arr;
+ pos = 0;
+ max = arr.length;
+ }
+
+ public TypesReader(byte[] arr, int off)
+ {
+ this.arr = arr;
+ this.pos = off;
+ this.max = arr.length;
+
+ if ((pos < 0) || (pos > arr.length))
+ throw new IllegalArgumentException("Illegal offset.");
+ }
+
+ public TypesReader(byte[] arr, int off, int len)
+ {
+ this.arr = arr;
+ this.pos = off;
+ this.max = off + len;
+
+ if ((pos < 0) || (pos > arr.length))
+ throw new IllegalArgumentException("Illegal offset.");
+
+ if ((max < 0) || (max > arr.length))
+ throw new IllegalArgumentException("Illegal length.");
+ }
+
+ public int readByte() throws IOException
+ {
+ if (pos >= max)
+ throw new IOException("Packet too short.");
+
+ return (arr[pos++] & 0xff);
+ }
+
+ public byte[] readBytes(int len) throws IOException
+ {
+ if ((pos + len) > max)
+ throw new IOException("Packet too short.");
+
+ byte[] res = new byte[len];
+
+ System.arraycopy(arr, pos, res, 0, len);
+ pos += len;
+
+ return res;
+ }
+
+ public void readBytes(byte[] dst, int off, int len) throws IOException
+ {
+ if ((pos + len) > max)
+ throw new IOException("Packet too short.");
+
+ System.arraycopy(arr, pos, dst, off, len);
+ pos += len;
+ }
+
+ public boolean readBoolean() throws IOException
+ {
+ if (pos >= max)
+ throw new IOException("Packet too short.");
+
+ return (arr[pos++] != 0);
+ }
+
+ public int readUINT32() throws IOException
+ {
+ if ((pos + 4) > max)
+ throw new IOException("Packet too short.");
+
+ return ((arr[pos++] & 0xff) << 24) | ((arr[pos++] & 0xff) << 16) | ((arr[pos++] & 0xff) << 8)
+ | (arr[pos++] & 0xff);
+ }
+
+ public long readUINT64() throws IOException
+ {
+ if ((pos + 8) > max)
+ throw new IOException("Packet too short.");
+
+ long high = ((arr[pos++] & 0xff) << 24) | ((arr[pos++] & 0xff) << 16) | ((arr[pos++] & 0xff) << 8)
+ | (arr[pos++] & 0xff); /* sign extension may take place - will be shifted away =) */
+
+ long low = ((arr[pos++] & 0xff) << 24) | ((arr[pos++] & 0xff) << 16) | ((arr[pos++] & 0xff) << 8)
+ | (arr[pos++] & 0xff); /* sign extension may take place - handle below */
+
+ return (high << 32) | (low & 0xffffffffl); /* see Java language spec (15.22.1, 5.6.2) */
+ }
+
+ public BigInteger readMPINT() throws IOException
+ {
+ BigInteger b;
+
+ byte raw[] = readByteString();
+
+ if (raw.length == 0)
+ b = BigInteger.ZERO;
+ else
+ b = new BigInteger(raw);
+
+ return b;
+ }
+
+ public byte[] readByteString() throws IOException
+ {
+ int len = readUINT32();
+
+ if ((len + pos) > max)
+ throw new IOException("Malformed SSH byte string.");
+
+ byte[] res = new byte[len];
+ System.arraycopy(arr, pos, res, 0, len);
+ pos += len;
+ return res;
+ }
+
+ public String readString(String charsetName) throws IOException
+ {
+ int len = readUINT32();
+
+ if ((len + pos) > max)
+ throw new IOException("Malformed SSH string.");
+
+ String res = (charsetName == null) ? new String(arr, pos, len) : new String(arr, pos, len, charsetName);
+ pos += len;
+
+ return res;
+ }
+
+ public String readString() throws IOException
+ {
+ int len = readUINT32();
+
+ if ((len + pos) > max)
+ throw new IOException("Malformed SSH string.");
+
+ String res = new String(arr, pos, len, "ISO-8859-1");
+
+ pos += len;
+
+ return res;
+ }
+
+ public String[] readNameList() throws IOException
+ {
+ return Tokenizer.parseTokens(readString(), ',');
+ }
+
+ public int remain()
+ {
+ return max - pos;
+ }
+
+}
diff --git a/src/com/trilead/ssh2/packets/TypesWriter.java b/src/com/trilead/ssh2/packets/TypesWriter.java
index f2e5ef3..9f7336b 100644
--- a/src/com/trilead/ssh2/packets/TypesWriter.java
+++ b/src/com/trilead/ssh2/packets/TypesWriter.java
@@ -1,169 +1,169 @@
-
-package com.trilead.ssh2.packets;
-
-import java.io.UnsupportedEncodingException;
-import java.math.BigInteger;
-
-/**
- * TypesWriter.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: TypesWriter.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
- */
-public class TypesWriter
-{
- byte arr[];
- int pos;
-
- public TypesWriter()
- {
- arr = new byte[256];
- pos = 0;
- }
-
- private void resize(int len)
- {
- byte new_arr[] = new byte[len];
- System.arraycopy(arr, 0, new_arr, 0, arr.length);
- arr = new_arr;
- }
-
- public int length()
- {
- return pos;
- }
-
- public byte[] getBytes()
- {
- byte[] dst = new byte[pos];
- System.arraycopy(arr, 0, dst, 0, pos);
- return dst;
- }
-
- public void getBytes(byte dst[])
- {
- System.arraycopy(arr, 0, dst, 0, pos);
- }
-
- public void writeUINT32(int val, int off)
- {
- if ((off + 4) > arr.length)
- resize(off + 32);
-
- arr[off++] = (byte) (val >> 24);
- arr[off++] = (byte) (val >> 16);
- arr[off++] = (byte) (val >> 8);
- arr[off++] = (byte) val;
- }
-
- public void writeUINT32(int val)
- {
- writeUINT32(val, pos);
- pos += 4;
- }
-
- public void writeUINT64(long val)
- {
- if ((pos + 8) > arr.length)
- resize(arr.length + 32);
-
- arr[pos++] = (byte) (val >> 56);
- arr[pos++] = (byte) (val >> 48);
- arr[pos++] = (byte) (val >> 40);
- arr[pos++] = (byte) (val >> 32);
- arr[pos++] = (byte) (val >> 24);
- arr[pos++] = (byte) (val >> 16);
- arr[pos++] = (byte) (val >> 8);
- arr[pos++] = (byte) val;
- }
-
- public void writeBoolean(boolean v)
- {
- if ((pos + 1) > arr.length)
- resize(arr.length + 32);
-
- arr[pos++] = v ? (byte) 1 : (byte) 0;
- }
-
- public void writeByte(int v, int off)
- {
- if ((off + 1) > arr.length)
- resize(off + 32);
-
- arr[off] = (byte) v;
- }
-
- public void writeByte(int v)
- {
- writeByte(v, pos);
- pos++;
- }
-
- public void writeMPInt(BigInteger b)
- {
- byte raw[] = b.toByteArray();
-
- if ((raw.length == 1) && (raw[0] == 0))
- writeUINT32(0); /* String with zero bytes of data */
- else
- writeString(raw, 0, raw.length);
- }
-
- public void writeBytes(byte[] buff)
- {
- writeBytes(buff, 0, buff.length);
- }
-
- public void writeBytes(byte[] buff, int off, int len)
- {
- if ((pos + len) > arr.length)
- resize(arr.length + len + 32);
-
- System.arraycopy(buff, off, arr, pos, len);
- pos += len;
- }
-
- public void writeString(byte[] buff, int off, int len)
- {
- writeUINT32(len);
- writeBytes(buff, off, len);
- }
-
- public void writeString(String v)
- {
- byte[] b;
-
- try
- {
- /* All Java JVMs must support ISO-8859-1 */
- b = v.getBytes("ISO-8859-1");
- }
- catch (UnsupportedEncodingException ignore)
- {
- b = v.getBytes();
- }
-
- writeUINT32(b.length);
- writeBytes(b, 0, b.length);
- }
-
- public void writeString(String v, String charsetName) throws UnsupportedEncodingException
- {
- byte[] b = (charsetName == null) ? v.getBytes() : v.getBytes(charsetName);
-
- writeUINT32(b.length);
- writeBytes(b, 0, b.length);
- }
-
- public void writeNameList(String v[])
- {
- StringBuffer sb = new StringBuffer();
- for (int i = 0; i < v.length; i++)
- {
- if (i > 0)
- sb.append(',');
- sb.append(v[i]);
- }
- writeString(sb.toString());
- }
-}
+
+package com.trilead.ssh2.packets;
+
+import java.io.UnsupportedEncodingException;
+import java.math.BigInteger;
+
+/**
+ * TypesWriter.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: TypesWriter.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
+ */
+public class TypesWriter
+{
+ byte arr[];
+ int pos;
+
+ public TypesWriter()
+ {
+ arr = new byte[256];
+ pos = 0;
+ }
+
+ private void resize(int len)
+ {
+ byte new_arr[] = new byte[len];
+ System.arraycopy(arr, 0, new_arr, 0, arr.length);
+ arr = new_arr;
+ }
+
+ public int length()
+ {
+ return pos;
+ }
+
+ public byte[] getBytes()
+ {
+ byte[] dst = new byte[pos];
+ System.arraycopy(arr, 0, dst, 0, pos);
+ return dst;
+ }
+
+ public void getBytes(byte dst[])
+ {
+ System.arraycopy(arr, 0, dst, 0, pos);
+ }
+
+ public void writeUINT32(int val, int off)
+ {
+ if ((off + 4) > arr.length)
+ resize(off + 32);
+
+ arr[off++] = (byte) (val >> 24);
+ arr[off++] = (byte) (val >> 16);
+ arr[off++] = (byte) (val >> 8);
+ arr[off++] = (byte) val;
+ }
+
+ public void writeUINT32(int val)
+ {
+ writeUINT32(val, pos);
+ pos += 4;
+ }
+
+ public void writeUINT64(long val)
+ {
+ if ((pos + 8) > arr.length)
+ resize(arr.length + 32);
+
+ arr[pos++] = (byte) (val >> 56);
+ arr[pos++] = (byte) (val >> 48);
+ arr[pos++] = (byte) (val >> 40);
+ arr[pos++] = (byte) (val >> 32);
+ arr[pos++] = (byte) (val >> 24);
+ arr[pos++] = (byte) (val >> 16);
+ arr[pos++] = (byte) (val >> 8);
+ arr[pos++] = (byte) val;
+ }
+
+ public void writeBoolean(boolean v)
+ {
+ if ((pos + 1) > arr.length)
+ resize(arr.length + 32);
+
+ arr[pos++] = v ? (byte) 1 : (byte) 0;
+ }
+
+ public void writeByte(int v, int off)
+ {
+ if ((off + 1) > arr.length)
+ resize(off + 32);
+
+ arr[off] = (byte) v;
+ }
+
+ public void writeByte(int v)
+ {
+ writeByte(v, pos);
+ pos++;
+ }
+
+ public void writeMPInt(BigInteger b)
+ {
+ byte raw[] = b.toByteArray();
+
+ if ((raw.length == 1) && (raw[0] == 0))
+ writeUINT32(0); /* String with zero bytes of data */
+ else
+ writeString(raw, 0, raw.length);
+ }
+
+ public void writeBytes(byte[] buff)
+ {
+ writeBytes(buff, 0, buff.length);
+ }
+
+ public void writeBytes(byte[] buff, int off, int len)
+ {
+ if ((pos + len) > arr.length)
+ resize(arr.length + len + 32);
+
+ System.arraycopy(buff, off, arr, pos, len);
+ pos += len;
+ }
+
+ public void writeString(byte[] buff, int off, int len)
+ {
+ writeUINT32(len);
+ writeBytes(buff, off, len);
+ }
+
+ public void writeString(String v)
+ {
+ byte[] b;
+
+ try
+ {
+ /* All Java JVMs must support ISO-8859-1 */
+ b = v.getBytes("ISO-8859-1");
+ }
+ catch (UnsupportedEncodingException ignore)
+ {
+ b = v.getBytes();
+ }
+
+ writeUINT32(b.length);
+ writeBytes(b, 0, b.length);
+ }
+
+ public void writeString(String v, String charsetName) throws UnsupportedEncodingException
+ {
+ byte[] b = (charsetName == null) ? v.getBytes() : v.getBytes(charsetName);
+
+ writeUINT32(b.length);
+ writeBytes(b, 0, b.length);
+ }
+
+ public void writeNameList(String v[])
+ {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < v.length; i++)
+ {
+ if (i > 0)
+ sb.append(',');
+ sb.append(v[i]);
+ }
+ writeString(sb.toString());
+ }
+}
diff --git a/src/com/trilead/ssh2/sftp/AttrTextHints.java b/src/com/trilead/ssh2/sftp/AttrTextHints.java
index 7bac60f..19f0525 100644
--- a/src/com/trilead/ssh2/sftp/AttrTextHints.java
+++ b/src/com/trilead/ssh2/sftp/AttrTextHints.java
@@ -1,38 +1,38 @@
-
-package com.trilead.ssh2.sftp;
-
-/**
- *
- * Values for the 'text-hint' field in the SFTP ATTRS data type.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: AttrTextHints.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- *
- */
-public class AttrTextHints
-{
- /**
- * The server knows the file is a text file, and should be opened
- * using the SSH_FXF_ACCESS_TEXT_MODE flag.
- */
- public static final int SSH_FILEXFER_ATTR_KNOWN_TEXT = 0x00;
-
- /**
- * The server has applied a heuristic or other mechanism and
- * believes that the file should be opened with the
- * SSH_FXF_ACCESS_TEXT_MODE flag.
- */
- public static final int SSH_FILEXFER_ATTR_GUESSED_TEXT = 0x01;
-
- /**
- * The server knows the file has binary content.
- */
- public static final int SSH_FILEXFER_ATTR_KNOWN_BINARY = 0x02;
-
- /**
- * The server has applied a heuristic or other mechanism and
- * believes has binary content, and should not be opened with the
- * SSH_FXF_ACCESS_TEXT_MODE flag.
- */
- public static final int SSH_FILEXFER_ATTR_GUESSED_BINARY = 0x03;
-}
+
+package com.trilead.ssh2.sftp;
+
+/**
+ *
+ * Values for the 'text-hint' field in the SFTP ATTRS data type.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: AttrTextHints.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ *
+ */
+public class AttrTextHints
+{
+ /**
+ * The server knows the file is a text file, and should be opened
+ * using the SSH_FXF_ACCESS_TEXT_MODE flag.
+ */
+ public static final int SSH_FILEXFER_ATTR_KNOWN_TEXT = 0x00;
+
+ /**
+ * The server has applied a heuristic or other mechanism and
+ * believes that the file should be opened with the
+ * SSH_FXF_ACCESS_TEXT_MODE flag.
+ */
+ public static final int SSH_FILEXFER_ATTR_GUESSED_TEXT = 0x01;
+
+ /**
+ * The server knows the file has binary content.
+ */
+ public static final int SSH_FILEXFER_ATTR_KNOWN_BINARY = 0x02;
+
+ /**
+ * The server has applied a heuristic or other mechanism and
+ * believes has binary content, and should not be opened with the
+ * SSH_FXF_ACCESS_TEXT_MODE flag.
+ */
+ public static final int SSH_FILEXFER_ATTR_GUESSED_BINARY = 0x03;
+}
diff --git a/src/com/trilead/ssh2/sftp/AttribBits.java b/src/com/trilead/ssh2/sftp/AttribBits.java
index f040673..b143613 100644
--- a/src/com/trilead/ssh2/sftp/AttribBits.java
+++ b/src/com/trilead/ssh2/sftp/AttribBits.java
@@ -1,129 +1,129 @@
-
-package com.trilead.ssh2.sftp;
-
-/**
- *
- * SFTP Attribute Bits for the "attrib-bits" and "attrib-bits-valid" fields
- * of the SFTP ATTR data type.
- * <p>
- * Yes, these are the "attrib-bits", even though they have "_FLAGS_" in
- * their name. Don't ask - I did not invent it.
- * <p>
- * "<i>These fields, taken together, reflect various attributes of the file
- * or directory, on the server. Bits not set in 'attrib-bits-valid' MUST be
- * ignored in the 'attrib-bits' field. This allows both the server and the
- * client to communicate only the bits it knows about without inadvertently
- * twiddling bits they don't understand.</i>"
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: AttribBits.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- *
- */
-public class AttribBits
-{
- /**
- * Advisory, read-only bit. This bit is not part of the access
- * control information on the file, but is rather an advisory field
- * indicating that the file should not be written.
- */
- public static final int SSH_FILEXFER_ATTR_FLAGS_READONLY = 0x00000001;
-
- /**
- * The file is part of the operating system.
- */
- public static final int SSH_FILEXFER_ATTR_FLAGS_SYSTEM = 0x00000002;
-
- /**
- * File SHOULD NOT be shown to user unless specifically requested.
- * For example, most UNIX systems SHOULD set this bit if the filename
- * begins with a 'period'. This bit may be read-only (see section 5.4 of
- * the SFTP standard draft). Most UNIX systems will not allow this to be
- * changed.
- */
- public static final int SSH_FILEXFER_ATTR_FLAGS_HIDDEN = 0x00000004;
-
- /**
- * This attribute applies only to directories. This attribute is
- * always read-only, and cannot be modified. This attribute means
- * that files and directory names in this directory should be compared
- * without regard to case.
- * <p>
- * It is recommended that where possible, the server's filesystem be
- * allowed to do comparisons. For example, if a client wished to prompt
- * a user before overwriting a file, it should not compare the new name
- * with the previously retrieved list of names in the directory. Rather,
- * it should first try to create the new file by specifying
- * SSH_FXF_CREATE_NEW flag. Then, if this fails and returns
- * SSH_FX_FILE_ALREADY_EXISTS, it should prompt the user and then retry
- * the create specifying SSH_FXF_CREATE_TRUNCATE.
- * <p>
- * Unless otherwise specified, filenames are assumed to be case sensitive.
- */
- public static final int SSH_FILEXFER_ATTR_FLAGS_CASE_INSENSITIVE = 0x00000008;
-
- /**
- * The file should be included in backup / archive operations.
- */
- public static final int SSH_FILEXFER_ATTR_FLAGS_ARCHIVE = 0x00000010;
-
- /**
- * The file is stored on disk using file-system level transparent
- * encryption. This flag does not affect the file data on the wire
- * (for either READ or WRITE requests.)
- */
- public static final int SSH_FILEXFER_ATTR_FLAGS_ENCRYPTED = 0x00000020;
-
- /**
- * The file is stored on disk using file-system level transparent
- * compression. This flag does not affect the file data on the wire.
- */
- public static final int SSH_FILEXFER_ATTR_FLAGS_COMPRESSED = 0x00000040;
-
- /**
- * The file is a sparse file; this means that file blocks that have
- * not been explicitly written are not stored on disk. For example, if
- * a client writes a buffer at 10 M from the beginning of the file,
- * the blocks between the previous EOF marker and the 10 M offset would
- * not consume physical disk space.
- * <p>
- * Some servers may store all files as sparse files, in which case
- * this bit will be unconditionally set. Other servers may not have
- * a mechanism for determining if the file is sparse, and so the file
- * MAY be stored sparse even if this flag is not set.
- */
- public static final int SSH_FILEXFER_ATTR_FLAGS_SPARSE = 0x00000080;
-
- /**
- * Opening the file without either the SSH_FXF_ACCESS_APPEND_DATA or
- * the SSH_FXF_ACCESS_APPEND_DATA_ATOMIC flag (see section 8.1.1.3
- * of the SFTP standard draft) MUST result in an
- * SSH_FX_INVALID_PARAMETER error.
- */
- public static final int SSH_FILEXFER_ATTR_FLAGS_APPEND_ONLY = 0x00000100;
-
- /**
- * The file cannot be deleted or renamed, no hard link can be created
- * to this file, and no data can be written to the file.
- * <p>
- * This bit implies a stronger level of protection than
- * SSH_FILEXFER_ATTR_FLAGS_READONLY, the file permission mask or ACLs.
- * Typically even the superuser cannot write to immutable files, and
- * only the superuser can set or remove the bit.
- */
- public static final int SSH_FILEXFER_ATTR_FLAGS_IMMUTABLE = 0x00000200;
-
- /**
- * When the file is modified, the changes are written synchronously
- * to the disk.
- */
- public static final int SSH_FILEXFER_ATTR_FLAGS_SYNC = 0x00000400;
-
- /**
- * The server MAY include this bit in a directory listing or realpath
- * response. It indicates there was a failure in the translation to UTF-8.
- * If this flag is included, the server SHOULD also include the
- * UNTRANSLATED_NAME attribute.
- */
- public static final int SSH_FILEXFER_ATTR_FLAGS_TRANSLATION_ERR = 0x00000800;
-
-}
+
+package com.trilead.ssh2.sftp;
+
+/**
+ *
+ * SFTP Attribute Bits for the "attrib-bits" and "attrib-bits-valid" fields
+ * of the SFTP ATTR data type.
+ * <p>
+ * Yes, these are the "attrib-bits", even though they have "_FLAGS_" in
+ * their name. Don't ask - I did not invent it.
+ * <p>
+ * "<i>These fields, taken together, reflect various attributes of the file
+ * or directory, on the server. Bits not set in 'attrib-bits-valid' MUST be
+ * ignored in the 'attrib-bits' field. This allows both the server and the
+ * client to communicate only the bits it knows about without inadvertently
+ * twiddling bits they don't understand.</i>"
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: AttribBits.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ *
+ */
+public class AttribBits
+{
+ /**
+ * Advisory, read-only bit. This bit is not part of the access
+ * control information on the file, but is rather an advisory field
+ * indicating that the file should not be written.
+ */
+ public static final int SSH_FILEXFER_ATTR_FLAGS_READONLY = 0x00000001;
+
+ /**
+ * The file is part of the operating system.
+ */
+ public static final int SSH_FILEXFER_ATTR_FLAGS_SYSTEM = 0x00000002;
+
+ /**
+ * File SHOULD NOT be shown to user unless specifically requested.
+ * For example, most UNIX systems SHOULD set this bit if the filename
+ * begins with a 'period'. This bit may be read-only (see section 5.4 of
+ * the SFTP standard draft). Most UNIX systems will not allow this to be
+ * changed.
+ */
+ public static final int SSH_FILEXFER_ATTR_FLAGS_HIDDEN = 0x00000004;
+
+ /**
+ * This attribute applies only to directories. This attribute is
+ * always read-only, and cannot be modified. This attribute means
+ * that files and directory names in this directory should be compared
+ * without regard to case.
+ * <p>
+ * It is recommended that where possible, the server's filesystem be
+ * allowed to do comparisons. For example, if a client wished to prompt
+ * a user before overwriting a file, it should not compare the new name
+ * with the previously retrieved list of names in the directory. Rather,
+ * it should first try to create the new file by specifying
+ * SSH_FXF_CREATE_NEW flag. Then, if this fails and returns
+ * SSH_FX_FILE_ALREADY_EXISTS, it should prompt the user and then retry
+ * the create specifying SSH_FXF_CREATE_TRUNCATE.
+ * <p>
+ * Unless otherwise specified, filenames are assumed to be case sensitive.
+ */
+ public static final int SSH_FILEXFER_ATTR_FLAGS_CASE_INSENSITIVE = 0x00000008;
+
+ /**
+ * The file should be included in backup / archive operations.
+ */
+ public static final int SSH_FILEXFER_ATTR_FLAGS_ARCHIVE = 0x00000010;
+
+ /**
+ * The file is stored on disk using file-system level transparent
+ * encryption. This flag does not affect the file data on the wire
+ * (for either READ or WRITE requests.)
+ */
+ public static final int SSH_FILEXFER_ATTR_FLAGS_ENCRYPTED = 0x00000020;
+
+ /**
+ * The file is stored on disk using file-system level transparent
+ * compression. This flag does not affect the file data on the wire.
+ */
+ public static final int SSH_FILEXFER_ATTR_FLAGS_COMPRESSED = 0x00000040;
+
+ /**
+ * The file is a sparse file; this means that file blocks that have
+ * not been explicitly written are not stored on disk. For example, if
+ * a client writes a buffer at 10 M from the beginning of the file,
+ * the blocks between the previous EOF marker and the 10 M offset would
+ * not consume physical disk space.
+ * <p>
+ * Some servers may store all files as sparse files, in which case
+ * this bit will be unconditionally set. Other servers may not have
+ * a mechanism for determining if the file is sparse, and so the file
+ * MAY be stored sparse even if this flag is not set.
+ */
+ public static final int SSH_FILEXFER_ATTR_FLAGS_SPARSE = 0x00000080;
+
+ /**
+ * Opening the file without either the SSH_FXF_ACCESS_APPEND_DATA or
+ * the SSH_FXF_ACCESS_APPEND_DATA_ATOMIC flag (see section 8.1.1.3
+ * of the SFTP standard draft) MUST result in an
+ * SSH_FX_INVALID_PARAMETER error.
+ */
+ public static final int SSH_FILEXFER_ATTR_FLAGS_APPEND_ONLY = 0x00000100;
+
+ /**
+ * The file cannot be deleted or renamed, no hard link can be created
+ * to this file, and no data can be written to the file.
+ * <p>
+ * This bit implies a stronger level of protection than
+ * SSH_FILEXFER_ATTR_FLAGS_READONLY, the file permission mask or ACLs.
+ * Typically even the superuser cannot write to immutable files, and
+ * only the superuser can set or remove the bit.
+ */
+ public static final int SSH_FILEXFER_ATTR_FLAGS_IMMUTABLE = 0x00000200;
+
+ /**
+ * When the file is modified, the changes are written synchronously
+ * to the disk.
+ */
+ public static final int SSH_FILEXFER_ATTR_FLAGS_SYNC = 0x00000400;
+
+ /**
+ * The server MAY include this bit in a directory listing or realpath
+ * response. It indicates there was a failure in the translation to UTF-8.
+ * If this flag is included, the server SHOULD also include the
+ * UNTRANSLATED_NAME attribute.
+ */
+ public static final int SSH_FILEXFER_ATTR_FLAGS_TRANSLATION_ERR = 0x00000800;
+
+}
diff --git a/src/com/trilead/ssh2/sftp/AttribFlags.java b/src/com/trilead/ssh2/sftp/AttribFlags.java
index 28ac613..ea27871 100644
--- a/src/com/trilead/ssh2/sftp/AttribFlags.java
+++ b/src/com/trilead/ssh2/sftp/AttribFlags.java
@@ -1,112 +1,112 @@
-
-package com.trilead.ssh2.sftp;
-
-/**
- *
- * Attribute Flags. The 'valid-attribute-flags' field in
- * the SFTP ATTRS data type specifies which of the fields are actually present.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: AttribFlags.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- *
- */
-public class AttribFlags
-{
- /**
- * Indicates that the 'allocation-size' field is present.
- */
- public static final int SSH_FILEXFER_ATTR_SIZE = 0x00000001;
-
- /** Protocol version 6:
- * 0x00000002 was used in a previous version of this protocol.
- * It is now a reserved value and MUST NOT appear in the mask.
- * Some future version of this protocol may reuse this value.
- */
- public static final int SSH_FILEXFER_ATTR_V3_UIDGID = 0x00000002;
-
- /**
- * Indicates that the 'permissions' field is present.
- */
- public static final int SSH_FILEXFER_ATTR_PERMISSIONS = 0x00000004;
-
- /**
- * Indicates that the 'atime' and 'mtime' field are present
- * (protocol v3).
- */
- public static final int SSH_FILEXFER_ATTR_V3_ACMODTIME = 0x00000008;
-
- /**
- * Indicates that the 'atime' field is present.
- */
- public static final int SSH_FILEXFER_ATTR_ACCESSTIME = 0x00000008;
-
- /**
- * Indicates that the 'createtime' field is present.
- */
- public static final int SSH_FILEXFER_ATTR_CREATETIME = 0x00000010;
-
- /**
- * Indicates that the 'mtime' field is present.
- */
- public static final int SSH_FILEXFER_ATTR_MODIFYTIME = 0x00000020;
-
- /**
- * Indicates that the 'acl' field is present.
- */
- public static final int SSH_FILEXFER_ATTR_ACL = 0x00000040;
-
- /**
- * Indicates that the 'owner' and 'group' fields are present.
- */
- public static final int SSH_FILEXFER_ATTR_OWNERGROUP = 0x00000080;
-
- /**
- * Indicates that additionally to the 'atime', 'createtime',
- * 'mtime' and 'ctime' fields (if present), there is also
- * 'atime-nseconds', 'createtime-nseconds', 'mtime-nseconds'
- * and 'ctime-nseconds'.
- */
- public static final int SSH_FILEXFER_ATTR_SUBSECOND_TIMES = 0x00000100;
-
- /**
- * Indicates that the 'attrib-bits' and 'attrib-bits-valid'
- * fields are present.
- */
- public static final int SSH_FILEXFER_ATTR_BITS = 0x00000200;
-
- /**
- * Indicates that the 'allocation-size' field is present.
- */
- public static final int SSH_FILEXFER_ATTR_ALLOCATION_SIZE = 0x00000400;
-
- /**
- * Indicates that the 'text-hint' field is present.
- */
- public static final int SSH_FILEXFER_ATTR_TEXT_HINT = 0x00000800;
-
- /**
- * Indicates that the 'mime-type' field is present.
- */
- public static final int SSH_FILEXFER_ATTR_MIME_TYPE = 0x00001000;
-
- /**
- * Indicates that the 'link-count' field is present.
- */
- public static final int SSH_FILEXFER_ATTR_LINK_COUNT = 0x00002000;
-
- /**
- * Indicates that the 'untranslated-name' field is present.
- */
- public static final int SSH_FILEXFER_ATTR_UNTRANSLATED_NAME = 0x00004000;
-
- /**
- * Indicates that the 'ctime' field is present.
- */
- public static final int SSH_FILEXFER_ATTR_CTIME = 0x00008000;
-
- /**
- * Indicates that the 'extended-count' field (and probablby some
- * 'extensions') is present.
- */
- public static final int SSH_FILEXFER_ATTR_EXTENDED = 0x80000000;
-}
+
+package com.trilead.ssh2.sftp;
+
+/**
+ *
+ * Attribute Flags. The 'valid-attribute-flags' field in
+ * the SFTP ATTRS data type specifies which of the fields are actually present.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: AttribFlags.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ *
+ */
+public class AttribFlags
+{
+ /**
+ * Indicates that the 'allocation-size' field is present.
+ */
+ public static final int SSH_FILEXFER_ATTR_SIZE = 0x00000001;
+
+ /** Protocol version 6:
+ * 0x00000002 was used in a previous version of this protocol.
+ * It is now a reserved value and MUST NOT appear in the mask.
+ * Some future version of this protocol may reuse this value.
+ */
+ public static final int SSH_FILEXFER_ATTR_V3_UIDGID = 0x00000002;
+
+ /**
+ * Indicates that the 'permissions' field is present.
+ */
+ public static final int SSH_FILEXFER_ATTR_PERMISSIONS = 0x00000004;
+
+ /**
+ * Indicates that the 'atime' and 'mtime' field are present
+ * (protocol v3).
+ */
+ public static final int SSH_FILEXFER_ATTR_V3_ACMODTIME = 0x00000008;
+
+ /**
+ * Indicates that the 'atime' field is present.
+ */
+ public static final int SSH_FILEXFER_ATTR_ACCESSTIME = 0x00000008;
+
+ /**
+ * Indicates that the 'createtime' field is present.
+ */
+ public static final int SSH_FILEXFER_ATTR_CREATETIME = 0x00000010;
+
+ /**
+ * Indicates that the 'mtime' field is present.
+ */
+ public static final int SSH_FILEXFER_ATTR_MODIFYTIME = 0x00000020;
+
+ /**
+ * Indicates that the 'acl' field is present.
+ */
+ public static final int SSH_FILEXFER_ATTR_ACL = 0x00000040;
+
+ /**
+ * Indicates that the 'owner' and 'group' fields are present.
+ */
+ public static final int SSH_FILEXFER_ATTR_OWNERGROUP = 0x00000080;
+
+ /**
+ * Indicates that additionally to the 'atime', 'createtime',
+ * 'mtime' and 'ctime' fields (if present), there is also
+ * 'atime-nseconds', 'createtime-nseconds', 'mtime-nseconds'
+ * and 'ctime-nseconds'.
+ */
+ public static final int SSH_FILEXFER_ATTR_SUBSECOND_TIMES = 0x00000100;
+
+ /**
+ * Indicates that the 'attrib-bits' and 'attrib-bits-valid'
+ * fields are present.
+ */
+ public static final int SSH_FILEXFER_ATTR_BITS = 0x00000200;
+
+ /**
+ * Indicates that the 'allocation-size' field is present.
+ */
+ public static final int SSH_FILEXFER_ATTR_ALLOCATION_SIZE = 0x00000400;
+
+ /**
+ * Indicates that the 'text-hint' field is present.
+ */
+ public static final int SSH_FILEXFER_ATTR_TEXT_HINT = 0x00000800;
+
+ /**
+ * Indicates that the 'mime-type' field is present.
+ */
+ public static final int SSH_FILEXFER_ATTR_MIME_TYPE = 0x00001000;
+
+ /**
+ * Indicates that the 'link-count' field is present.
+ */
+ public static final int SSH_FILEXFER_ATTR_LINK_COUNT = 0x00002000;
+
+ /**
+ * Indicates that the 'untranslated-name' field is present.
+ */
+ public static final int SSH_FILEXFER_ATTR_UNTRANSLATED_NAME = 0x00004000;
+
+ /**
+ * Indicates that the 'ctime' field is present.
+ */
+ public static final int SSH_FILEXFER_ATTR_CTIME = 0x00008000;
+
+ /**
+ * Indicates that the 'extended-count' field (and probablby some
+ * 'extensions') is present.
+ */
+ public static final int SSH_FILEXFER_ATTR_EXTENDED = 0x80000000;
+}
diff --git a/src/com/trilead/ssh2/sftp/AttribPermissions.java b/src/com/trilead/ssh2/sftp/AttribPermissions.java
index 0c946c9..558aa6f 100644
--- a/src/com/trilead/ssh2/sftp/AttribPermissions.java
+++ b/src/com/trilead/ssh2/sftp/AttribPermissions.java
@@ -1,32 +1,32 @@
-
-package com.trilead.ssh2.sftp;
-
-/**
- *
- * Permissions for the 'permissions' field in the SFTP ATTRS data type.
- * <p>
- * "<i>The 'permissions' field contains a bit mask specifying file permissions.
- * These permissions correspond to the st_mode field of the stat structure
- * defined by POSIX [IEEE.1003-1.1996].</i>"
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: AttribPermissions.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- *
- */
-public class AttribPermissions
-{
- /* Octal values! */
-
- public static final int S_IRUSR = 0400;
- public static final int S_IWUSR = 0200;
- public static final int S_IXUSR = 0100;
- public static final int S_IRGRP = 0040;
- public static final int S_IWGRP = 0020;
- public static final int S_IXGRP = 0010;
- public static final int S_IROTH = 0004;
- public static final int S_IWOTH = 0002;
- public static final int S_IXOTH = 0001;
- public static final int S_ISUID = 04000;
- public static final int S_ISGID = 02000;
- public static final int S_ISVTX = 01000;
-}
+
+package com.trilead.ssh2.sftp;
+
+/**
+ *
+ * Permissions for the 'permissions' field in the SFTP ATTRS data type.
+ * <p>
+ * "<i>The 'permissions' field contains a bit mask specifying file permissions.
+ * These permissions correspond to the st_mode field of the stat structure
+ * defined by POSIX [IEEE.1003-1.1996].</i>"
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: AttribPermissions.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ *
+ */
+public class AttribPermissions
+{
+ /* Octal values! */
+
+ public static final int S_IRUSR = 0400;
+ public static final int S_IWUSR = 0200;
+ public static final int S_IXUSR = 0100;
+ public static final int S_IRGRP = 0040;
+ public static final int S_IWGRP = 0020;
+ public static final int S_IXGRP = 0010;
+ public static final int S_IROTH = 0004;
+ public static final int S_IWOTH = 0002;
+ public static final int S_IXOTH = 0001;
+ public static final int S_ISUID = 04000;
+ public static final int S_ISGID = 02000;
+ public static final int S_ISVTX = 01000;
+}
diff --git a/src/com/trilead/ssh2/sftp/AttribTypes.java b/src/com/trilead/ssh2/sftp/AttribTypes.java
index 8cacc3e..e2f4169 100644
--- a/src/com/trilead/ssh2/sftp/AttribTypes.java
+++ b/src/com/trilead/ssh2/sftp/AttribTypes.java
@@ -1,28 +1,28 @@
-
-package com.trilead.ssh2.sftp;
-
-/**
- *
- * Types for the 'type' field in the SFTP ATTRS data type.
- * <p>
- * "<i>On a POSIX system, these values would be derived from the mode field
- * of the stat structure. SPECIAL should be used for files that are of
- * a known type which cannot be expressed in the protocol. UNKNOWN
- * should be used if the type is not known.</i>"
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: AttribTypes.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- *
- */
-public class AttribTypes
-{
- public static final int SSH_FILEXFER_TYPE_REGULAR = 1;
- public static final int SSH_FILEXFER_TYPE_DIRECTORY = 2;
- public static final int SSH_FILEXFER_TYPE_SYMLINK = 3;
- public static final int SSH_FILEXFER_TYPE_SPECIAL = 4;
- public static final int SSH_FILEXFER_TYPE_UNKNOWN = 5;
- public static final int SSH_FILEXFER_TYPE_SOCKET = 6;
- public static final int SSH_FILEXFER_TYPE_CHAR_DEVICE = 7;
- public static final int SSH_FILEXFER_TYPE_BLOCK_DEVICE = 8;
- public static final int SSH_FILEXFER_TYPE_FIFO = 9;
-}
+
+package com.trilead.ssh2.sftp;
+
+/**
+ *
+ * Types for the 'type' field in the SFTP ATTRS data type.
+ * <p>
+ * "<i>On a POSIX system, these values would be derived from the mode field
+ * of the stat structure. SPECIAL should be used for files that are of
+ * a known type which cannot be expressed in the protocol. UNKNOWN
+ * should be used if the type is not known.</i>"
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: AttribTypes.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ *
+ */
+public class AttribTypes
+{
+ public static final int SSH_FILEXFER_TYPE_REGULAR = 1;
+ public static final int SSH_FILEXFER_TYPE_DIRECTORY = 2;
+ public static final int SSH_FILEXFER_TYPE_SYMLINK = 3;
+ public static final int SSH_FILEXFER_TYPE_SPECIAL = 4;
+ public static final int SSH_FILEXFER_TYPE_UNKNOWN = 5;
+ public static final int SSH_FILEXFER_TYPE_SOCKET = 6;
+ public static final int SSH_FILEXFER_TYPE_CHAR_DEVICE = 7;
+ public static final int SSH_FILEXFER_TYPE_BLOCK_DEVICE = 8;
+ public static final int SSH_FILEXFER_TYPE_FIFO = 9;
+}
diff --git a/src/com/trilead/ssh2/sftp/ErrorCodes.java b/src/com/trilead/ssh2/sftp/ErrorCodes.java
index 58a0174..7317a00 100644
--- a/src/com/trilead/ssh2/sftp/ErrorCodes.java
+++ b/src/com/trilead/ssh2/sftp/ErrorCodes.java
@@ -1,104 +1,104 @@
-
-package com.trilead.ssh2.sftp;
-
-/**
- *
- * SFTP Error Codes
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: ErrorCodes.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- *
- */
-public class ErrorCodes
-{
- public static final int SSH_FX_OK = 0;
- public static final int SSH_FX_EOF = 1;
- public static final int SSH_FX_NO_SUCH_FILE = 2;
- public static final int SSH_FX_PERMISSION_DENIED = 3;
- public static final int SSH_FX_FAILURE = 4;
- public static final int SSH_FX_BAD_MESSAGE = 5;
- public static final int SSH_FX_NO_CONNECTION = 6;
- public static final int SSH_FX_CONNECTION_LOST = 7;
- public static final int SSH_FX_OP_UNSUPPORTED = 8;
- public static final int SSH_FX_INVALID_HANDLE = 9;
- public static final int SSH_FX_NO_SUCH_PATH = 10;
- public static final int SSH_FX_FILE_ALREADY_EXISTS = 11;
- public static final int SSH_FX_WRITE_PROTECT = 12;
- public static final int SSH_FX_NO_MEDIA = 13;
- public static final int SSH_FX_NO_SPACE_ON_FILESYSTEM = 14;
- public static final int SSH_FX_QUOTA_EXCEEDED = 15;
- public static final int SSH_FX_UNKNOWN_PRINCIPAL = 16;
- public static final int SSH_FX_LOCK_CONFLICT = 17;
- public static final int SSH_FX_DIR_NOT_EMPTY = 18;
- public static final int SSH_FX_NOT_A_DIRECTORY = 19;
- public static final int SSH_FX_INVALID_FILENAME = 20;
- public static final int SSH_FX_LINK_LOOP = 21;
- public static final int SSH_FX_CANNOT_DELETE = 22;
- public static final int SSH_FX_INVALID_PARAMETER = 23;
- public static final int SSH_FX_FILE_IS_A_DIRECTORY = 24;
- public static final int SSH_FX_BYTE_RANGE_LOCK_CONFLICT = 25;
- public static final int SSH_FX_BYTE_RANGE_LOCK_REFUSED = 26;
- public static final int SSH_FX_DELETE_PENDING = 27;
- public static final int SSH_FX_FILE_CORRUPT = 28;
- public static final int SSH_FX_OWNER_INVALID = 29;
- public static final int SSH_FX_GROUP_INVALID = 30;
- public static final int SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK = 31;
-
- private static final String[][] messages = {
-
- { "SSH_FX_OK", "Indicates successful completion of the operation." },
- { "SSH_FX_EOF",
- "An attempt to read past the end-of-file was made; or, there are no more directory entries to return." },
- { "SSH_FX_NO_SUCH_FILE", "A reference was made to a file which does not exist." },
- { "SSH_FX_PERMISSION_DENIED", "The user does not have sufficient permissions to perform the operation." },
- { "SSH_FX_FAILURE", "An error occurred, but no specific error code exists to describe the failure." },
- { "SSH_FX_BAD_MESSAGE", "A badly formatted packet or other SFTP protocol incompatibility was detected." },
- { "SSH_FX_NO_CONNECTION", "There is no connection to the server." },
- { "SSH_FX_CONNECTION_LOST", "The connection to the server was lost." },
- { "SSH_FX_OP_UNSUPPORTED",
- "An attempted operation could not be completed by the server because the server does not support the operation." },
- { "SSH_FX_INVALID_HANDLE", "The handle value was invalid." },
- { "SSH_FX_NO_SUCH_PATH", "The file path does not exist or is invalid." },
- { "SSH_FX_FILE_ALREADY_EXISTS", "The file already exists." },
- { "SSH_FX_WRITE_PROTECT", "The file is on read-only media, or the media is write protected." },
- { "SSH_FX_NO_MEDIA",
- "The requested operation cannot be completed because there is no media available in the drive." },
- { "SSH_FX_NO_SPACE_ON_FILESYSTEM",
- "The requested operation cannot be completed because there is insufficient free space on the filesystem." },
- { "SSH_FX_QUOTA_EXCEEDED",
- "The operation cannot be completed because it would exceed the user's storage quota." },
- {
- "SSH_FX_UNKNOWN_PRINCIPAL",
- "A principal referenced by the request (either the 'owner', 'group', or 'who' field of an ACL), was unknown. The error specific data contains the problematic names." },
- { "SSH_FX_LOCK_CONFLICT", "The file could not be opened because it is locked by another process." },
- { "SSH_FX_DIR_NOT_EMPTY", "The directory is not empty." },
- { "SSH_FX_NOT_A_DIRECTORY", "The specified file is not a directory." },
- { "SSH_FX_INVALID_FILENAME", "The filename is not valid." },
- { "SSH_FX_LINK_LOOP",
- "Too many symbolic links encountered or, an SSH_FXF_NOFOLLOW open encountered a symbolic link as the final component." },
- { "SSH_FX_CANNOT_DELETE",
- "The file cannot be deleted. One possible reason is that the advisory READONLY attribute-bit is set." },
- { "SSH_FX_INVALID_PARAMETER",
- "One of the parameters was out of range, or the parameters specified cannot be used together." },
- { "SSH_FX_FILE_IS_A_DIRECTORY",
- "The specified file was a directory in a context where a directory cannot be used." },
- { "SSH_FX_BYTE_RANGE_LOCK_CONFLICT",
- " A read or write operation failed because another process's mandatory byte-range lock overlaps with the request." },
- { "SSH_FX_BYTE_RANGE_LOCK_REFUSED", "A request for a byte range lock was refused." },
- { "SSH_FX_DELETE_PENDING", "An operation was attempted on a file for which a delete operation is pending." },
- { "SSH_FX_FILE_CORRUPT", "The file is corrupt; an filesystem integrity check should be run." },
- { "SSH_FX_OWNER_INVALID", "The principal specified can not be assigned as an owner of a file." },
- { "SSH_FX_GROUP_INVALID", "The principal specified can not be assigned as the primary group of a file." },
- { "SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK",
- "The requested operation could not be completed because the specifed byte range lock has not been granted." },
-
- };
-
- public static final String[] getDescription(int errorCode)
- {
- if ((errorCode < 0) || (errorCode >= messages.length))
- return null;
-
- return messages[errorCode];
- }
-}
+
+package com.trilead.ssh2.sftp;
+
+/**
+ *
+ * SFTP Error Codes
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: ErrorCodes.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ *
+ */
+public class ErrorCodes
+{
+ public static final int SSH_FX_OK = 0;
+ public static final int SSH_FX_EOF = 1;
+ public static final int SSH_FX_NO_SUCH_FILE = 2;
+ public static final int SSH_FX_PERMISSION_DENIED = 3;
+ public static final int SSH_FX_FAILURE = 4;
+ public static final int SSH_FX_BAD_MESSAGE = 5;
+ public static final int SSH_FX_NO_CONNECTION = 6;
+ public static final int SSH_FX_CONNECTION_LOST = 7;
+ public static final int SSH_FX_OP_UNSUPPORTED = 8;
+ public static final int SSH_FX_INVALID_HANDLE = 9;
+ public static final int SSH_FX_NO_SUCH_PATH = 10;
+ public static final int SSH_FX_FILE_ALREADY_EXISTS = 11;
+ public static final int SSH_FX_WRITE_PROTECT = 12;
+ public static final int SSH_FX_NO_MEDIA = 13;
+ public static final int SSH_FX_NO_SPACE_ON_FILESYSTEM = 14;
+ public static final int SSH_FX_QUOTA_EXCEEDED = 15;
+ public static final int SSH_FX_UNKNOWN_PRINCIPAL = 16;
+ public static final int SSH_FX_LOCK_CONFLICT = 17;
+ public static final int SSH_FX_DIR_NOT_EMPTY = 18;
+ public static final int SSH_FX_NOT_A_DIRECTORY = 19;
+ public static final int SSH_FX_INVALID_FILENAME = 20;
+ public static final int SSH_FX_LINK_LOOP = 21;
+ public static final int SSH_FX_CANNOT_DELETE = 22;
+ public static final int SSH_FX_INVALID_PARAMETER = 23;
+ public static final int SSH_FX_FILE_IS_A_DIRECTORY = 24;
+ public static final int SSH_FX_BYTE_RANGE_LOCK_CONFLICT = 25;
+ public static final int SSH_FX_BYTE_RANGE_LOCK_REFUSED = 26;
+ public static final int SSH_FX_DELETE_PENDING = 27;
+ public static final int SSH_FX_FILE_CORRUPT = 28;
+ public static final int SSH_FX_OWNER_INVALID = 29;
+ public static final int SSH_FX_GROUP_INVALID = 30;
+ public static final int SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK = 31;
+
+ private static final String[][] messages = {
+
+ { "SSH_FX_OK", "Indicates successful completion of the operation." },
+ { "SSH_FX_EOF",
+ "An attempt to read past the end-of-file was made; or, there are no more directory entries to return." },
+ { "SSH_FX_NO_SUCH_FILE", "A reference was made to a file which does not exist." },
+ { "SSH_FX_PERMISSION_DENIED", "The user does not have sufficient permissions to perform the operation." },
+ { "SSH_FX_FAILURE", "An error occurred, but no specific error code exists to describe the failure." },
+ { "SSH_FX_BAD_MESSAGE", "A badly formatted packet or other SFTP protocol incompatibility was detected." },
+ { "SSH_FX_NO_CONNECTION", "There is no connection to the server." },
+ { "SSH_FX_CONNECTION_LOST", "The connection to the server was lost." },
+ { "SSH_FX_OP_UNSUPPORTED",
+ "An attempted operation could not be completed by the server because the server does not support the operation." },
+ { "SSH_FX_INVALID_HANDLE", "The handle value was invalid." },
+ { "SSH_FX_NO_SUCH_PATH", "The file path does not exist or is invalid." },
+ { "SSH_FX_FILE_ALREADY_EXISTS", "The file already exists." },
+ { "SSH_FX_WRITE_PROTECT", "The file is on read-only media, or the media is write protected." },
+ { "SSH_FX_NO_MEDIA",
+ "The requested operation cannot be completed because there is no media available in the drive." },
+ { "SSH_FX_NO_SPACE_ON_FILESYSTEM",
+ "The requested operation cannot be completed because there is insufficient free space on the filesystem." },
+ { "SSH_FX_QUOTA_EXCEEDED",
+ "The operation cannot be completed because it would exceed the user's storage quota." },
+ {
+ "SSH_FX_UNKNOWN_PRINCIPAL",
+ "A principal referenced by the request (either the 'owner', 'group', or 'who' field of an ACL), was unknown. The error specific data contains the problematic names." },
+ { "SSH_FX_LOCK_CONFLICT", "The file could not be opened because it is locked by another process." },
+ { "SSH_FX_DIR_NOT_EMPTY", "The directory is not empty." },
+ { "SSH_FX_NOT_A_DIRECTORY", "The specified file is not a directory." },
+ { "SSH_FX_INVALID_FILENAME", "The filename is not valid." },
+ { "SSH_FX_LINK_LOOP",
+ "Too many symbolic links encountered or, an SSH_FXF_NOFOLLOW open encountered a symbolic link as the final component." },
+ { "SSH_FX_CANNOT_DELETE",
+ "The file cannot be deleted. One possible reason is that the advisory READONLY attribute-bit is set." },
+ { "SSH_FX_INVALID_PARAMETER",
+ "One of the parameters was out of range, or the parameters specified cannot be used together." },
+ { "SSH_FX_FILE_IS_A_DIRECTORY",
+ "The specified file was a directory in a context where a directory cannot be used." },
+ { "SSH_FX_BYTE_RANGE_LOCK_CONFLICT",
+ " A read or write operation failed because another process's mandatory byte-range lock overlaps with the request." },
+ { "SSH_FX_BYTE_RANGE_LOCK_REFUSED", "A request for a byte range lock was refused." },
+ { "SSH_FX_DELETE_PENDING", "An operation was attempted on a file for which a delete operation is pending." },
+ { "SSH_FX_FILE_CORRUPT", "The file is corrupt; an filesystem integrity check should be run." },
+ { "SSH_FX_OWNER_INVALID", "The principal specified can not be assigned as an owner of a file." },
+ { "SSH_FX_GROUP_INVALID", "The principal specified can not be assigned as the primary group of a file." },
+ { "SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK",
+ "The requested operation could not be completed because the specifed byte range lock has not been granted." },
+
+ };
+
+ public static final String[] getDescription(int errorCode)
+ {
+ if ((errorCode < 0) || (errorCode >= messages.length))
+ return null;
+
+ return messages[errorCode];
+ }
+}
diff --git a/src/com/trilead/ssh2/sftp/OpenFlags.java b/src/com/trilead/ssh2/sftp/OpenFlags.java
index bad30c3..b2979b9 100644
--- a/src/com/trilead/ssh2/sftp/OpenFlags.java
+++ b/src/com/trilead/ssh2/sftp/OpenFlags.java
@@ -1,223 +1,223 @@
-
-package com.trilead.ssh2.sftp;
-
-/**
- *
- * SFTP Open Flags.
- *
- * The following table is provided to assist in mapping POSIX semantics
- * to equivalent SFTP file open parameters:
- * <p>
- * TODO: This comment should be moved to the open method.
- * <p>
- * <ul>
- * <li>O_RDONLY
- * <ul><li>desired-access = READ_DATA | READ_ATTRIBUTES</li></ul>
- * </li>
- * </ul>
- * <ul>
- * <li>O_WRONLY
- * <ul><li>desired-access = WRITE_DATA | WRITE_ATTRIBUTES</li></ul>
- * </li>
- * </ul>
- * <ul>
- * <li>O_RDWR
- * <ul><li>desired-access = READ_DATA | READ_ATTRIBUTES | WRITE_DATA | WRITE_ATTRIBUTES</li></ul>
- * </li>
- * </ul>
- * <ul>
- * <li>O_APPEND
- * <ul>
- * <li>desired-access = WRITE_DATA | WRITE_ATTRIBUTES | APPEND_DATA</li>
- * <li>flags = SSH_FXF_ACCESS_APPEND_DATA and or SSH_FXF_ACCESS_APPEND_DATA_ATOMIC</li>
- * </ul>
- * </li>
- * </ul>
- * <ul>
- * <li>O_CREAT
- * <ul>
- * <li>flags = SSH_FXF_OPEN_OR_CREATE</li>
- * </ul>
- * </li>
- * </ul>
- * <ul>
- * <li>O_TRUNC
- * <ul>
- * <li>flags = SSH_FXF_TRUNCATE_EXISTING</li>
- * </ul>
- * </li>
- * </ul>
- * <ul>
- * <li>O_TRUNC|O_CREATE
- * <ul>
- * <li>flags = SSH_FXF_CREATE_TRUNCATE</li>
- * </ul>
- * </li>
- * </ul>
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: OpenFlags.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- */
-public class OpenFlags
-{
- /**
- * Disposition is a 3 bit field that controls how the file is opened.
- * The server MUST support these bits (possible enumaration values:
- * SSH_FXF_CREATE_NEW, SSH_FXF_CREATE_TRUNCATE, SSH_FXF_OPEN_EXISTING,
- * SSH_FXF_OPEN_OR_CREATE or SSH_FXF_TRUNCATE_EXISTING).
- */
- public static final int SSH_FXF_ACCESS_DISPOSITION = 0x00000007;
-
- /**
- * A new file is created; if the file already exists, the server
- * MUST return status SSH_FX_FILE_ALREADY_EXISTS.
- */
- public static final int SSH_FXF_CREATE_NEW = 0x00000000;
-
- /**
- * A new file is created; if the file already exists, it is opened
- * and truncated.
- */
- public static final int SSH_FXF_CREATE_TRUNCATE = 0x00000001;
-
- /**
- * An existing file is opened. If the file does not exist, the
- * server MUST return SSH_FX_NO_SUCH_FILE. If a directory in the
- * path does not exist, the server SHOULD return
- * SSH_FX_NO_SUCH_PATH. It is also acceptable if the server
- * returns SSH_FX_NO_SUCH_FILE in this case.
- */
- public static final int SSH_FXF_OPEN_EXISTING = 0x00000002;
-
- /**
- * If the file exists, it is opened. If the file does not exist,
- * it is created.
- */
- public static final int SSH_FXF_OPEN_OR_CREATE = 0x00000003;
-
- /**
- * An existing file is opened and truncated. If the file does not
- * exist, the server MUST return the same error codes as defined
- * for SSH_FXF_OPEN_EXISTING.
- */
- public static final int SSH_FXF_TRUNCATE_EXISTING = 0x00000004;
-
- /**
- * Data is always written at the end of the file. The offset field
- * of the SSH_FXP_WRITE requests are ignored.
- * <p>
- * Data is not required to be appended atomically. This means that
- * if multiple writers attempt to append data simultaneously, data
- * from the first may be lost. However, data MAY be appended
- * atomically.
- */
- public static final int SSH_FXF_ACCESS_APPEND_DATA = 0x00000008;
-
- /**
- * Data is always written at the end of the file. The offset field
- * of the SSH_FXP_WRITE requests are ignored.
- * <p>
- * Data MUST be written atomically so that there is no chance that
- * multiple appenders can collide and result in data being lost.
- * <p>
- * If both append flags are specified, the server SHOULD use atomic
- * append if it is available, but SHOULD use non-atomic appends
- * otherwise. The server SHOULD NOT fail the request in this case.
- */
- public static final int SSH_FXF_ACCESS_APPEND_DATA_ATOMIC = 0x00000010;
-
- /**
- * Indicates that the server should treat the file as text and
- * convert it to the canonical newline convention in use.
- * (See Determining Server Newline Convention in section 5.3 in the
- * SFTP standard draft).
- * <p>
- * When a file is opened with this flag, the offset field in the read
- * and write functions is ignored.
- * <p>
- * Servers MUST process multiple, parallel reads and writes correctly
- * in this mode. Naturally, it is permissible for them to do this by
- * serializing the requests.
- * <p>
- * Clients SHOULD use the SSH_FXF_ACCESS_APPEND_DATA flag to append
- * data to a text file rather then using write with a calculated offset.
- */
- public static final int SSH_FXF_ACCESS_TEXT_MODE = 0x00000020;
-
- /**
- * The server MUST guarantee that no other handle has been opened
- * with ACE4_READ_DATA access, and that no other handle will be
- * opened with ACE4_READ_DATA access until the client closes the
- * handle. (This MUST apply both to other clients and to other
- * processes on the server.)
- * <p>
- * If there is a conflicting lock the server MUST return
- * SSH_FX_LOCK_CONFLICT. If the server cannot make the locking
- * guarantee, it MUST return SSH_FX_OP_UNSUPPORTED.
- * <p>
- * Other handles MAY be opened for ACE4_WRITE_DATA or any other
- * combination of accesses, as long as ACE4_READ_DATA is not included
- * in the mask.
- */
- public static final int SSH_FXF_ACCESS_BLOCK_READ = 0x00000040;
-
- /**
- * The server MUST guarantee that no other handle has been opened
- * with ACE4_WRITE_DATA or ACE4_APPEND_DATA access, and that no other
- * handle will be opened with ACE4_WRITE_DATA or ACE4_APPEND_DATA
- * access until the client closes the handle. (This MUST apply both
- * to other clients and to other processes on the server.)
- * <p>
- * If there is a conflicting lock the server MUST return
- * SSH_FX_LOCK_CONFLICT. If the server cannot make the locking
- * guarantee, it MUST return SSH_FX_OP_UNSUPPORTED.
- * <p>
- * Other handles MAY be opened for ACE4_READ_DATA or any other
- * combination of accesses, as long as neither ACE4_WRITE_DATA nor
- * ACE4_APPEND_DATA are included in the mask.
- */
- public static final int SSH_FXF_ACCESS_BLOCK_WRITE = 0x00000080;
-
- /**
- * The server MUST guarantee that no other handle has been opened
- * with ACE4_DELETE access, opened with the
- * SSH_FXF_ACCESS_DELETE_ON_CLOSE flag set, and that no other handle
- * will be opened with ACE4_DELETE access or with the
- * SSH_FXF_ACCESS_DELETE_ON_CLOSE flag set, and that the file itself
- * is not deleted in any other way until the client closes the handle.
- * <p>
- * If there is a conflicting lock the server MUST return
- * SSH_FX_LOCK_CONFLICT. If the server cannot make the locking
- * guarantee, it MUST return SSH_FX_OP_UNSUPPORTED.
- */
- public static final int SSH_FXF_ACCESS_BLOCK_DELETE = 0x00000100;
-
- /**
- * If this bit is set, the above BLOCK modes are advisory. In advisory
- * mode, only other accesses that specify a BLOCK mode need be
- * considered when determining whether the BLOCK can be granted,
- * and the server need not prevent I/O operations that violate the
- * block mode.
- * <p>
- * The server MAY perform mandatory locking even if the BLOCK_ADVISORY
- * bit is set.
- */
- public static final int SSH_FXF_ACCESS_BLOCK_ADVISORY = 0x00000200;
-
- /**
- * If the final component of the path is a symlink, then the open
- * MUST fail, and the error SSH_FX_LINK_LOOP MUST be returned.
- */
- public static final int SSH_FXF_ACCESS_NOFOLLOW = 0x00000400;
-
- /**
- * The file should be deleted when the last handle to it is closed.
- * (The last handle may not be an sftp-handle.) This MAY be emulated
- * by a server if the OS doesn't support it by deleting the file when
- * this handle is closed.
- * <p>
- * It is implementation specific whether the directory entry is
- * removed immediately or when the handle is closed.
- */
- public static final int SSH_FXF_ACCESS_DELETE_ON_CLOSE = 0x00000800;
-}
+
+package com.trilead.ssh2.sftp;
+
+/**
+ *
+ * SFTP Open Flags.
+ *
+ * The following table is provided to assist in mapping POSIX semantics
+ * to equivalent SFTP file open parameters:
+ * <p>
+ * TODO: This comment should be moved to the open method.
+ * <p>
+ * <ul>
+ * <li>O_RDONLY
+ * <ul><li>desired-access = READ_DATA | READ_ATTRIBUTES</li></ul>
+ * </li>
+ * </ul>
+ * <ul>
+ * <li>O_WRONLY
+ * <ul><li>desired-access = WRITE_DATA | WRITE_ATTRIBUTES</li></ul>
+ * </li>
+ * </ul>
+ * <ul>
+ * <li>O_RDWR
+ * <ul><li>desired-access = READ_DATA | READ_ATTRIBUTES | WRITE_DATA | WRITE_ATTRIBUTES</li></ul>
+ * </li>
+ * </ul>
+ * <ul>
+ * <li>O_APPEND
+ * <ul>
+ * <li>desired-access = WRITE_DATA | WRITE_ATTRIBUTES | APPEND_DATA</li>
+ * <li>flags = SSH_FXF_ACCESS_APPEND_DATA and or SSH_FXF_ACCESS_APPEND_DATA_ATOMIC</li>
+ * </ul>
+ * </li>
+ * </ul>
+ * <ul>
+ * <li>O_CREAT
+ * <ul>
+ * <li>flags = SSH_FXF_OPEN_OR_CREATE</li>
+ * </ul>
+ * </li>
+ * </ul>
+ * <ul>
+ * <li>O_TRUNC
+ * <ul>
+ * <li>flags = SSH_FXF_TRUNCATE_EXISTING</li>
+ * </ul>
+ * </li>
+ * </ul>
+ * <ul>
+ * <li>O_TRUNC|O_CREATE
+ * <ul>
+ * <li>flags = SSH_FXF_CREATE_TRUNCATE</li>
+ * </ul>
+ * </li>
+ * </ul>
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: OpenFlags.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ */
+public class OpenFlags
+{
+ /**
+ * Disposition is a 3 bit field that controls how the file is opened.
+ * The server MUST support these bits (possible enumaration values:
+ * SSH_FXF_CREATE_NEW, SSH_FXF_CREATE_TRUNCATE, SSH_FXF_OPEN_EXISTING,
+ * SSH_FXF_OPEN_OR_CREATE or SSH_FXF_TRUNCATE_EXISTING).
+ */
+ public static final int SSH_FXF_ACCESS_DISPOSITION = 0x00000007;
+
+ /**
+ * A new file is created; if the file already exists, the server
+ * MUST return status SSH_FX_FILE_ALREADY_EXISTS.
+ */
+ public static final int SSH_FXF_CREATE_NEW = 0x00000000;
+
+ /**
+ * A new file is created; if the file already exists, it is opened
+ * and truncated.
+ */
+ public static final int SSH_FXF_CREATE_TRUNCATE = 0x00000001;
+
+ /**
+ * An existing file is opened. If the file does not exist, the
+ * server MUST return SSH_FX_NO_SUCH_FILE. If a directory in the
+ * path does not exist, the server SHOULD return
+ * SSH_FX_NO_SUCH_PATH. It is also acceptable if the server
+ * returns SSH_FX_NO_SUCH_FILE in this case.
+ */
+ public static final int SSH_FXF_OPEN_EXISTING = 0x00000002;
+
+ /**
+ * If the file exists, it is opened. If the file does not exist,
+ * it is created.
+ */
+ public static final int SSH_FXF_OPEN_OR_CREATE = 0x00000003;
+
+ /**
+ * An existing file is opened and truncated. If the file does not
+ * exist, the server MUST return the same error codes as defined
+ * for SSH_FXF_OPEN_EXISTING.
+ */
+ public static final int SSH_FXF_TRUNCATE_EXISTING = 0x00000004;
+
+ /**
+ * Data is always written at the end of the file. The offset field
+ * of the SSH_FXP_WRITE requests are ignored.
+ * <p>
+ * Data is not required to be appended atomically. This means that
+ * if multiple writers attempt to append data simultaneously, data
+ * from the first may be lost. However, data MAY be appended
+ * atomically.
+ */
+ public static final int SSH_FXF_ACCESS_APPEND_DATA = 0x00000008;
+
+ /**
+ * Data is always written at the end of the file. The offset field
+ * of the SSH_FXP_WRITE requests are ignored.
+ * <p>
+ * Data MUST be written atomically so that there is no chance that
+ * multiple appenders can collide and result in data being lost.
+ * <p>
+ * If both append flags are specified, the server SHOULD use atomic
+ * append if it is available, but SHOULD use non-atomic appends
+ * otherwise. The server SHOULD NOT fail the request in this case.
+ */
+ public static final int SSH_FXF_ACCESS_APPEND_DATA_ATOMIC = 0x00000010;
+
+ /**
+ * Indicates that the server should treat the file as text and
+ * convert it to the canonical newline convention in use.
+ * (See Determining Server Newline Convention in section 5.3 in the
+ * SFTP standard draft).
+ * <p>
+ * When a file is opened with this flag, the offset field in the read
+ * and write functions is ignored.
+ * <p>
+ * Servers MUST process multiple, parallel reads and writes correctly
+ * in this mode. Naturally, it is permissible for them to do this by
+ * serializing the requests.
+ * <p>
+ * Clients SHOULD use the SSH_FXF_ACCESS_APPEND_DATA flag to append
+ * data to a text file rather then using write with a calculated offset.
+ */
+ public static final int SSH_FXF_ACCESS_TEXT_MODE = 0x00000020;
+
+ /**
+ * The server MUST guarantee that no other handle has been opened
+ * with ACE4_READ_DATA access, and that no other handle will be
+ * opened with ACE4_READ_DATA access until the client closes the
+ * handle. (This MUST apply both to other clients and to other
+ * processes on the server.)
+ * <p>
+ * If there is a conflicting lock the server MUST return
+ * SSH_FX_LOCK_CONFLICT. If the server cannot make the locking
+ * guarantee, it MUST return SSH_FX_OP_UNSUPPORTED.
+ * <p>
+ * Other handles MAY be opened for ACE4_WRITE_DATA or any other
+ * combination of accesses, as long as ACE4_READ_DATA is not included
+ * in the mask.
+ */
+ public static final int SSH_FXF_ACCESS_BLOCK_READ = 0x00000040;
+
+ /**
+ * The server MUST guarantee that no other handle has been opened
+ * with ACE4_WRITE_DATA or ACE4_APPEND_DATA access, and that no other
+ * handle will be opened with ACE4_WRITE_DATA or ACE4_APPEND_DATA
+ * access until the client closes the handle. (This MUST apply both
+ * to other clients and to other processes on the server.)
+ * <p>
+ * If there is a conflicting lock the server MUST return
+ * SSH_FX_LOCK_CONFLICT. If the server cannot make the locking
+ * guarantee, it MUST return SSH_FX_OP_UNSUPPORTED.
+ * <p>
+ * Other handles MAY be opened for ACE4_READ_DATA or any other
+ * combination of accesses, as long as neither ACE4_WRITE_DATA nor
+ * ACE4_APPEND_DATA are included in the mask.
+ */
+ public static final int SSH_FXF_ACCESS_BLOCK_WRITE = 0x00000080;
+
+ /**
+ * The server MUST guarantee that no other handle has been opened
+ * with ACE4_DELETE access, opened with the
+ * SSH_FXF_ACCESS_DELETE_ON_CLOSE flag set, and that no other handle
+ * will be opened with ACE4_DELETE access or with the
+ * SSH_FXF_ACCESS_DELETE_ON_CLOSE flag set, and that the file itself
+ * is not deleted in any other way until the client closes the handle.
+ * <p>
+ * If there is a conflicting lock the server MUST return
+ * SSH_FX_LOCK_CONFLICT. If the server cannot make the locking
+ * guarantee, it MUST return SSH_FX_OP_UNSUPPORTED.
+ */
+ public static final int SSH_FXF_ACCESS_BLOCK_DELETE = 0x00000100;
+
+ /**
+ * If this bit is set, the above BLOCK modes are advisory. In advisory
+ * mode, only other accesses that specify a BLOCK mode need be
+ * considered when determining whether the BLOCK can be granted,
+ * and the server need not prevent I/O operations that violate the
+ * block mode.
+ * <p>
+ * The server MAY perform mandatory locking even if the BLOCK_ADVISORY
+ * bit is set.
+ */
+ public static final int SSH_FXF_ACCESS_BLOCK_ADVISORY = 0x00000200;
+
+ /**
+ * If the final component of the path is a symlink, then the open
+ * MUST fail, and the error SSH_FX_LINK_LOOP MUST be returned.
+ */
+ public static final int SSH_FXF_ACCESS_NOFOLLOW = 0x00000400;
+
+ /**
+ * The file should be deleted when the last handle to it is closed.
+ * (The last handle may not be an sftp-handle.) This MAY be emulated
+ * by a server if the OS doesn't support it by deleting the file when
+ * this handle is closed.
+ * <p>
+ * It is implementation specific whether the directory entry is
+ * removed immediately or when the handle is closed.
+ */
+ public static final int SSH_FXF_ACCESS_DELETE_ON_CLOSE = 0x00000800;
+}
diff --git a/src/com/trilead/ssh2/sftp/Packet.java b/src/com/trilead/ssh2/sftp/Packet.java
index 295055c..444af90 100644
--- a/src/com/trilead/ssh2/sftp/Packet.java
+++ b/src/com/trilead/ssh2/sftp/Packet.java
@@ -1,43 +1,43 @@
-
-package com.trilead.ssh2.sftp;
-
-/**
- *
- * SFTP Paket Types
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: Packet.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
- *
- */
-public class Packet
-{
- public static final int SSH_FXP_INIT = 1;
- public static final int SSH_FXP_VERSION = 2;
- public static final int SSH_FXP_OPEN = 3;
- public static final int SSH_FXP_CLOSE = 4;
- public static final int SSH_FXP_READ = 5;
- public static final int SSH_FXP_WRITE = 6;
- public static final int SSH_FXP_LSTAT = 7;
- public static final int SSH_FXP_FSTAT = 8;
- public static final int SSH_FXP_SETSTAT = 9;
- public static final int SSH_FXP_FSETSTAT = 10;
- public static final int SSH_FXP_OPENDIR = 11;
- public static final int SSH_FXP_READDIR = 12;
- public static final int SSH_FXP_REMOVE = 13;
- public static final int SSH_FXP_MKDIR = 14;
- public static final int SSH_FXP_RMDIR = 15;
- public static final int SSH_FXP_REALPATH = 16;
- public static final int SSH_FXP_STAT = 17;
- public static final int SSH_FXP_RENAME = 18;
- public static final int SSH_FXP_READLINK = 19;
- public static final int SSH_FXP_SYMLINK = 20;
-
- public static final int SSH_FXP_STATUS = 101;
- public static final int SSH_FXP_HANDLE = 102;
- public static final int SSH_FXP_DATA = 103;
- public static final int SSH_FXP_NAME = 104;
- public static final int SSH_FXP_ATTRS = 105;
-
- public static final int SSH_FXP_EXTENDED = 200;
- public static final int SSH_FXP_EXTENDED_REPLY = 201;
-}
+
+package com.trilead.ssh2.sftp;
+
+/**
+ *
+ * SFTP Paket Types
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: Packet.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
+ *
+ */
+public class Packet
+{
+ public static final int SSH_FXP_INIT = 1;
+ public static final int SSH_FXP_VERSION = 2;
+ public static final int SSH_FXP_OPEN = 3;
+ public static final int SSH_FXP_CLOSE = 4;
+ public static final int SSH_FXP_READ = 5;
+ public static final int SSH_FXP_WRITE = 6;
+ public static final int SSH_FXP_LSTAT = 7;
+ public static final int SSH_FXP_FSTAT = 8;
+ public static final int SSH_FXP_SETSTAT = 9;
+ public static final int SSH_FXP_FSETSTAT = 10;
+ public static final int SSH_FXP_OPENDIR = 11;
+ public static final int SSH_FXP_READDIR = 12;
+ public static final int SSH_FXP_REMOVE = 13;
+ public static final int SSH_FXP_MKDIR = 14;
+ public static final int SSH_FXP_RMDIR = 15;
+ public static final int SSH_FXP_REALPATH = 16;
+ public static final int SSH_FXP_STAT = 17;
+ public static final int SSH_FXP_RENAME = 18;
+ public static final int SSH_FXP_READLINK = 19;
+ public static final int SSH_FXP_SYMLINK = 20;
+
+ public static final int SSH_FXP_STATUS = 101;
+ public static final int SSH_FXP_HANDLE = 102;
+ public static final int SSH_FXP_DATA = 103;
+ public static final int SSH_FXP_NAME = 104;
+ public static final int SSH_FXP_ATTRS = 105;
+
+ public static final int SSH_FXP_EXTENDED = 200;
+ public static final int SSH_FXP_EXTENDED_REPLY = 201;
+}
diff --git a/src/com/trilead/ssh2/signature/DSAPrivateKey.java b/src/com/trilead/ssh2/signature/DSAPrivateKey.java
deleted file mode 100644
index d2a63ae..0000000
--- a/src/com/trilead/ssh2/signature/DSAPrivateKey.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package com.trilead.ssh2.signature;
-
-import java.math.BigInteger;
-
-/**
- * DSAPrivateKey.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: DSAPrivateKey.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public class DSAPrivateKey
-{
- private BigInteger p;
- private BigInteger q;
- private BigInteger g;
- private BigInteger x;
- private BigInteger y;
-
- public DSAPrivateKey(BigInteger p, BigInteger q, BigInteger g,
- BigInteger y, BigInteger x)
- {
- this.p = p;
- this.q = q;
- this.g = g;
- this.y = y;
- this.x = x;
- }
-
- public BigInteger getP()
- {
- return p;
- }
-
- public BigInteger getQ()
- {
- return q;
- }
-
- public BigInteger getG()
- {
- return g;
- }
-
- public BigInteger getY()
- {
- return y;
- }
-
- public BigInteger getX()
- {
- return x;
- }
-
- public DSAPublicKey getPublicKey()
- {
- return new DSAPublicKey(p, q, g, y);
- }
-} \ No newline at end of file
diff --git a/src/com/trilead/ssh2/signature/DSAPublicKey.java b/src/com/trilead/ssh2/signature/DSAPublicKey.java
deleted file mode 100644
index f8351ff..0000000
--- a/src/com/trilead/ssh2/signature/DSAPublicKey.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.trilead.ssh2.signature;
-
-import java.math.BigInteger;
-
-/**
- * DSAPublicKey.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: DSAPublicKey.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public class DSAPublicKey
-{
- private BigInteger p;
- private BigInteger q;
- private BigInteger g;
- private BigInteger y;
-
- public DSAPublicKey(BigInteger p, BigInteger q, BigInteger g, BigInteger y)
- {
- this.p = p;
- this.q = q;
- this.g = g;
- this.y = y;
- }
-
- public BigInteger getP()
- {
- return p;
- }
-
- public BigInteger getQ()
- {
- return q;
- }
-
- public BigInteger getG()
- {
- return g;
- }
-
- public BigInteger getY()
- {
- return y;
- }
-} \ No newline at end of file
diff --git a/src/com/trilead/ssh2/signature/DSASHA1Verify.java b/src/com/trilead/ssh2/signature/DSASHA1Verify.java
index c838ebd..6fb6ddb 100644
--- a/src/com/trilead/ssh2/signature/DSASHA1Verify.java
+++ b/src/com/trilead/ssh2/signature/DSASHA1Verify.java
@@ -1,210 +1,255 @@
-
-package com.trilead.ssh2.signature;
-
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.SecureRandom;
-
-import com.trilead.ssh2.crypto.digest.SHA1;
-import com.trilead.ssh2.log.Logger;
-import com.trilead.ssh2.packets.TypesReader;
-import com.trilead.ssh2.packets.TypesWriter;
-
-
-/**
- * DSASHA1Verify.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: DSASHA1Verify.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
- */
-public class DSASHA1Verify
-{
- private static final Logger log = Logger.getLogger(DSASHA1Verify.class);
-
- public static DSAPublicKey decodeSSHDSAPublicKey(byte[] key) throws IOException
- {
- TypesReader tr = new TypesReader(key);
-
- String key_format = tr.readString();
-
- if (key_format.equals("ssh-dss") == false)
- throw new IllegalArgumentException("This is not a ssh-dss public key!");
-
- BigInteger p = tr.readMPINT();
- BigInteger q = tr.readMPINT();
- BigInteger g = tr.readMPINT();
- BigInteger y = tr.readMPINT();
-
- if (tr.remain() != 0)
- throw new IOException("Padding in DSA public key!");
-
- return new DSAPublicKey(p, q, g, y);
- }
-
- public static byte[] encodeSSHDSAPublicKey(DSAPublicKey pk) throws IOException
- {
- TypesWriter tw = new TypesWriter();
-
- tw.writeString("ssh-dss");
- tw.writeMPInt(pk.getP());
- tw.writeMPInt(pk.getQ());
- tw.writeMPInt(pk.getG());
- tw.writeMPInt(pk.getY());
-
- return tw.getBytes();
- }
-
- public static byte[] encodeSSHDSASignature(DSASignature ds)
- {
- TypesWriter tw = new TypesWriter();
-
- tw.writeString("ssh-dss");
-
- byte[] r = ds.getR().toByteArray();
- byte[] s = ds.getS().toByteArray();
-
- byte[] a40 = new byte[40];
-
- /* Patch (unsigned) r and s into the target array. */
-
- int r_copylen = (r.length < 20) ? r.length : 20;
- int s_copylen = (s.length < 20) ? s.length : 20;
-
- System.arraycopy(r, r.length - r_copylen, a40, 20 - r_copylen, r_copylen);
- System.arraycopy(s, s.length - s_copylen, a40, 40 - s_copylen, s_copylen);
-
- tw.writeString(a40, 0, 40);
-
- return tw.getBytes();
- }
-
- public static DSASignature decodeSSHDSASignature(byte[] sig) throws IOException
- {
- byte[] rsArray = null;
-
- if (sig.length == 40)
- {
- /* OK, another broken SSH server. */
- rsArray = sig;
- }
- else
- {
- /* Hopefully a server obeing the standard... */
- TypesReader tr = new TypesReader(sig);
-
- String sig_format = tr.readString();
-
- if (sig_format.equals("ssh-dss") == false)
- throw new IOException("Peer sent wrong signature format");
-
- rsArray = tr.readByteString();
-
- if (rsArray.length != 40)
- throw new IOException("Peer sent corrupt signature");
-
- if (tr.remain() != 0)
- throw new IOException("Padding in DSA signature!");
- }
-
- /* Remember, s and r are unsigned ints. */
-
- byte[] tmp = new byte[20];
-
- System.arraycopy(rsArray, 0, tmp, 0, 20);
- BigInteger r = new BigInteger(1, tmp);
-
- System.arraycopy(rsArray, 20, tmp, 0, 20);
- BigInteger s = new BigInteger(1, tmp);
-
- if (log.isEnabled())
- {
- log.log(30, "decoded ssh-dss signature: first bytes r(" + ((rsArray[0]) & 0xff) + "), s("
- + ((rsArray[20]) & 0xff) + ")");
- }
-
- return new DSASignature(r, s);
- }
-
- public static boolean verifySignature(byte[] message, DSASignature ds, DSAPublicKey dpk) throws IOException
- {
- /* Inspired by Bouncycastle's DSASigner class */
-
- SHA1 md = new SHA1();
- md.update(message);
- byte[] sha_message = new byte[md.getDigestLength()];
- md.digest(sha_message);
-
- BigInteger m = new BigInteger(1, sha_message);
-
- BigInteger r = ds.getR();
- BigInteger s = ds.getS();
-
- BigInteger g = dpk.getG();
- BigInteger p = dpk.getP();
- BigInteger q = dpk.getQ();
- BigInteger y = dpk.getY();
-
- BigInteger zero = BigInteger.ZERO;
-
- if (log.isEnabled())
- {
- log.log(60, "ssh-dss signature: m: " + m.toString(16));
- log.log(60, "ssh-dss signature: r: " + r.toString(16));
- log.log(60, "ssh-dss signature: s: " + s.toString(16));
- log.log(60, "ssh-dss signature: g: " + g.toString(16));
- log.log(60, "ssh-dss signature: p: " + p.toString(16));
- log.log(60, "ssh-dss signature: q: " + q.toString(16));
- log.log(60, "ssh-dss signature: y: " + y.toString(16));
- }
-
- if (zero.compareTo(r) >= 0 || q.compareTo(r) <= 0)
- {
- log.log(20, "ssh-dss signature: zero.compareTo(r) >= 0 || q.compareTo(r) <= 0");
- return false;
- }
-
- if (zero.compareTo(s) >= 0 || q.compareTo(s) <= 0)
- {
- log.log(20, "ssh-dss signature: zero.compareTo(s) >= 0 || q.compareTo(s) <= 0");
- return false;
- }
-
- BigInteger w = s.modInverse(q);
-
- BigInteger u1 = m.multiply(w).mod(q);
- BigInteger u2 = r.multiply(w).mod(q);
-
- u1 = g.modPow(u1, p);
- u2 = y.modPow(u2, p);
-
- BigInteger v = u1.multiply(u2).mod(p).mod(q);
-
- return v.equals(r);
- }
-
- public static DSASignature generateSignature(byte[] message, DSAPrivateKey pk, SecureRandom rnd)
- {
- SHA1 md = new SHA1();
- md.update(message);
- byte[] sha_message = new byte[md.getDigestLength()];
- md.digest(sha_message);
-
- BigInteger m = new BigInteger(1, sha_message);
- BigInteger k;
- int qBitLength = pk.getQ().bitLength();
-
- do
- {
- k = new BigInteger(qBitLength, rnd);
- }
- while (k.compareTo(pk.getQ()) >= 0);
-
- BigInteger r = pk.getG().modPow(k, pk.getP()).mod(pk.getQ());
-
- k = k.modInverse(pk.getQ()).multiply(m.add((pk).getX().multiply(r)));
-
- BigInteger s = k.mod(pk.getQ());
-
- return new DSASignature(r, s);
- }
-}
+
+package com.trilead.ssh2.signature;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.spec.DSAPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+
+import com.trilead.ssh2.log.Logger;
+import com.trilead.ssh2.packets.TypesReader;
+import com.trilead.ssh2.packets.TypesWriter;
+
+
+/**
+ * DSASHA1Verify.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: DSASHA1Verify.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
+ */
+public class DSASHA1Verify
+{
+ private static final Logger log = Logger.getLogger(DSASHA1Verify.class);
+
+ public static DSAPublicKey decodeSSHDSAPublicKey(byte[] key) throws IOException
+ {
+ TypesReader tr = new TypesReader(key);
+
+ String key_format = tr.readString();
+
+ if (key_format.equals("ssh-dss") == false)
+ throw new IllegalArgumentException("This is not a ssh-dss public key!");
+
+ BigInteger p = tr.readMPINT();
+ BigInteger q = tr.readMPINT();
+ BigInteger g = tr.readMPINT();
+ BigInteger y = tr.readMPINT();
+
+ if (tr.remain() != 0)
+ throw new IOException("Padding in DSA public key!");
+
+ try {
+ KeyFactory kf = KeyFactory.getInstance("DSA");
+
+ KeySpec ks = new DSAPublicKeySpec(y, p, q, g);
+ return (DSAPublicKey) kf.generatePublic(ks);
+ } catch (NoSuchAlgorithmException e) {
+ IOException ex = new IOException();
+ ex.initCause(e);
+ throw ex;
+ } catch (InvalidKeySpecException e) {
+ IOException ex = new IOException();
+ ex.initCause(e);
+ throw ex;
+ }
+ }
+
+ public static byte[] encodeSSHDSAPublicKey(DSAPublicKey pk) throws IOException
+ {
+ TypesWriter tw = new TypesWriter();
+
+ tw.writeString("ssh-dss");
+
+ DSAParams params = pk.getParams();
+ tw.writeMPInt(params.getP());
+ tw.writeMPInt(params.getQ());
+ tw.writeMPInt(params.getG());
+ tw.writeMPInt(pk.getY());
+
+ return tw.getBytes();
+ }
+
+ /**
+ * Convert from Java's signature ASN.1 encoding to the SSH spec.
+ * <p>
+ * Java ASN.1 encoding:
+ * <pre>
+ * SEQUENCE ::= {
+ * r INTEGER,
+ * s INTEGER
+ * }
+ * </pre>
+ */
+ public static byte[] encodeSSHDSASignature(byte[] ds)
+ {
+ TypesWriter tw = new TypesWriter();
+
+ tw.writeString("ssh-dss");
+
+ int len, index;
+
+ index = 3;
+ len = ds[index++] & 0xff;
+ byte[] r = new byte[len];
+ System.arraycopy(ds, index, r, 0, r.length);
+
+ index = index + len + 1;
+ len = ds[index++] & 0xff;
+ byte[] s = new byte[len];
+ System.arraycopy(ds, index, s, 0, s.length);
+
+ byte[] a40 = new byte[40];
+
+ /* Patch (unsigned) r and s into the target array. */
+
+ int r_copylen = (r.length < 20) ? r.length : 20;
+ int s_copylen = (s.length < 20) ? s.length : 20;
+
+ System.arraycopy(r, r.length - r_copylen, a40, 20 - r_copylen, r_copylen);
+ System.arraycopy(s, s.length - s_copylen, a40, 40 - s_copylen, s_copylen);
+
+ tw.writeString(a40, 0, 40);
+
+ return tw.getBytes();
+ }
+
+ public static byte[] decodeSSHDSASignature(byte[] sig) throws IOException
+ {
+ byte[] rsArray = null;
+
+ if (sig.length == 40)
+ {
+ /* OK, another broken SSH server. */
+ rsArray = sig;
+ }
+ else
+ {
+ /* Hopefully a server obeying the standard... */
+ TypesReader tr = new TypesReader(sig);
+
+ String sig_format = tr.readString();
+ if (sig_format.equals("ssh-dss") == false)
+ throw new IOException("Peer sent wrong signature format");
+
+ rsArray = tr.readByteString();
+
+ if (rsArray.length != 40)
+ throw new IOException("Peer sent corrupt signature");
+
+ if (tr.remain() != 0)
+ throw new IOException("Padding in DSA signature!");
+ }
+
+ int i = 0;
+ int j = 0;
+ byte[] tmp;
+
+ if (rsArray[0] == 0 && rsArray[1] == 0 && rsArray[2] == 0) {
+ j = ((rsArray[i++] << 24) & 0xff000000) | ((rsArray[i++] << 16) & 0x00ff0000)
+ | ((rsArray[i++] << 8) & 0x0000ff00) | ((rsArray[i++]) & 0x000000ff);
+ i += j;
+ j = ((rsArray[i++] << 24) & 0xff000000) | ((rsArray[i++] << 16) & 0x00ff0000)
+ | ((rsArray[i++] << 8) & 0x0000ff00) | ((rsArray[i++]) & 0x000000ff);
+ tmp = new byte[j];
+ System.arraycopy(rsArray, i, tmp, 0, j);
+ rsArray = tmp;
+ }
+
+ /* ASN.1 */
+ int frst = ((rsArray[0] & 0x80) != 0 ? 1 : 0);
+ int scnd = ((rsArray[20] & 0x80) != 0 ? 1 : 0);
+
+ /* Calculate output length */
+ int length = rsArray.length + 6 + frst + scnd;
+ tmp = new byte[length];
+
+ /* DER-encoding to match Java */
+ tmp[0] = (byte) 0x30;
+
+ if (rsArray.length != 40)
+ throw new IOException("Peer sent corrupt signature");
+ /* Calculate length */
+ tmp[1] = (byte) 0x2c;
+ tmp[1] += frst;
+ tmp[1] += scnd;
+
+ /* First item */
+ tmp[2] = (byte) 0x02;
+
+ /* First item length */
+ tmp[3] = (byte) 0x14;
+ tmp[3] += frst;
+
+ /* Copy in the data for first item */
+ System.arraycopy(rsArray, 0, tmp, 4 + frst, 20);
+
+ /* Second item */
+ tmp[4 + tmp[3]] = (byte) 0x02;
+
+ /* Second item length */
+ tmp[5 + tmp[3]] = (byte) 0x14;
+ tmp[5 + tmp[3]] += scnd;
+
+ /* Copy in the data for the second item */
+ System.arraycopy(rsArray, 20, tmp, 6 + tmp[3] + scnd, 20);
+
+ /* Swap buffers */
+ rsArray = tmp;
+
+ return rsArray;
+ }
+
+ public static boolean verifySignature(byte[] message, byte[] ds, DSAPublicKey dpk) throws IOException
+ {
+ try {
+ Signature s = Signature.getInstance("SHA1withDSA");
+ s.initVerify(dpk);
+ s.update(message);
+ return s.verify(ds);
+ } catch (NoSuchAlgorithmException e) {
+ IOException ex = new IOException("No such algorithm");
+ ex.initCause(e);
+ throw ex;
+ } catch (InvalidKeyException e) {
+ IOException ex = new IOException("No such algorithm");
+ ex.initCause(e);
+ throw ex;
+ } catch (SignatureException e) {
+ IOException ex = new IOException();
+ ex.initCause(e);
+ throw ex;
+ }
+ }
+
+ public static byte[] generateSignature(byte[] message, DSAPrivateKey pk, SecureRandom rnd) throws IOException
+ {
+ try {
+ Signature s = Signature.getInstance("SHA1withDSA");
+ s.initSign(pk);
+ s.update(message);
+ return s.sign();
+ } catch (NoSuchAlgorithmException e) {
+ IOException ex = new IOException();
+ ex.initCause(e);
+ throw ex;
+ } catch (InvalidKeyException e) {
+ IOException ex = new IOException();
+ ex.initCause(e);
+ throw ex;
+ } catch (SignatureException e) {
+ IOException ex = new IOException();
+ ex.initCause(e);
+ throw ex;
+ }
+ }
+}
diff --git a/src/com/trilead/ssh2/signature/DSASignature.java b/src/com/trilead/ssh2/signature/DSASignature.java
deleted file mode 100644
index eff84cd..0000000
--- a/src/com/trilead/ssh2/signature/DSASignature.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.trilead.ssh2.signature;
-
-import java.math.BigInteger;
-
-/**
- * DSASignature.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: DSASignature.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public class DSASignature
-{
- private BigInteger r;
- private BigInteger s;
-
- public DSASignature(BigInteger r, BigInteger s)
- {
- this.r = r;
- this.s = s;
- }
-
- public BigInteger getR()
- {
- return r;
- }
-
- public BigInteger getS()
- {
- return s;
- }
-}
diff --git a/src/com/trilead/ssh2/signature/ECDSASHA2Verify.java b/src/com/trilead/ssh2/signature/ECDSASHA2Verify.java
new file mode 100644
index 0000000..7d8dd3e
--- /dev/null
+++ b/src/com/trilead/ssh2/signature/ECDSASHA2Verify.java
@@ -0,0 +1,487 @@
+/**
+ *
+ */
+package com.trilead.ssh2.signature;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECFieldFp;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.EllipticCurve;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.util.Map;
+import java.util.TreeMap;
+
+import com.trilead.ssh2.log.Logger;
+import com.trilead.ssh2.packets.TypesReader;
+import com.trilead.ssh2.packets.TypesWriter;
+
+/**
+ * @author Kenny Root
+ *
+ */
+public class ECDSASHA2Verify {
+ private static final Logger log = Logger.getLogger(ECDSASHA2Verify.class);
+
+ public static final String ECDSA_SHA2_PREFIX = "ecdsa-sha2-";
+
+ private static final String NISTP256 = "nistp256";
+ private static final String NISTP256_OID = "1.2.840.10045.3.1.7";
+ private static final String NISTP384 = "nistp384";
+ private static final String NISTP384_OID = "1.3.132.0.34";
+ private static final String NISTP521 = "nistp521";
+ private static final String NISTP521_OID = "1.3.132.0.35";
+
+ private static final Map<String, ECParameterSpec> CURVES = new TreeMap<String, ECParameterSpec>();
+ static {
+ CURVES.put(NISTP256, EllipticCurves.nistp256);
+ CURVES.put(NISTP384, EllipticCurves.nistp384);
+ CURVES.put(NISTP521, EllipticCurves.nistp521);
+ }
+
+ private static final Map<Integer, String> CURVE_SIZES = new TreeMap<Integer, String>();
+ static {
+ CURVE_SIZES.put(256, NISTP256);
+ CURVE_SIZES.put(384, NISTP384);
+ CURVE_SIZES.put(521, NISTP521);
+ }
+
+ private static final Map<String, String> CURVE_OIDS = new TreeMap<String, String>();
+ static {
+ CURVE_OIDS.put(NISTP256_OID, NISTP256);
+ CURVE_OIDS.put(NISTP384_OID, NISTP256);
+ CURVE_OIDS.put(NISTP521_OID, NISTP256);
+ }
+
+ public static int[] getCurveSizes() {
+ int[] keys = new int[CURVE_SIZES.size()];
+ int i = 0;
+ for (Integer n : CURVE_SIZES.keySet().toArray(new Integer[keys.length])) {
+ keys[i++] = n;
+ }
+ return keys;
+ }
+
+ public static ECParameterSpec getCurveForSize(int size) {
+ final String name = CURVE_SIZES.get(size);
+ if (name == null) {
+ return null;
+ }
+ return CURVES.get(name);
+ }
+
+ public static ECPublicKey decodeSSHECDSAPublicKey(byte[] key) throws IOException
+ {
+ TypesReader tr = new TypesReader(key);
+
+ String key_format = tr.readString();
+
+ if (key_format.startsWith(ECDSA_SHA2_PREFIX) == false)
+ throw new IllegalArgumentException("This is not an ECDSA public key");
+
+ String curveName = tr.readString();
+ byte[] groupBytes = tr.readByteString();
+
+ if (tr.remain() != 0)
+ throw new IOException("Padding in ECDSA public key!");
+
+ if (key_format.equals(ECDSA_SHA2_PREFIX + curveName) == false) {
+ throw new IOException("Key format is inconsistent with curve name: " + key_format
+ + " != " + curveName);
+ }
+
+ ECParameterSpec params = CURVES.get(curveName);
+ if (params == null) {
+ throw new IOException("Curve is not supported: " + curveName);
+ }
+
+ ECPoint group = ECDSASHA2Verify.decodeECPoint(groupBytes, params.getCurve());
+ if (group == null) {
+ throw new IOException("Invalid ECDSA group");
+ }
+
+ KeySpec keySpec = new ECPublicKeySpec(group, params);
+
+ try {
+ KeyFactory kf = KeyFactory.getInstance("EC");
+ return (ECPublicKey) kf.generatePublic(keySpec);
+ } catch (NoSuchAlgorithmException nsae) {
+ IOException ioe = new IOException("No EC KeyFactory available");
+ ioe.initCause(nsae);
+ throw ioe;
+ } catch (InvalidKeySpecException ikse) {
+ IOException ioe = new IOException("No EC KeyFactory available");
+ ioe.initCause(ikse);
+ throw ioe;
+ }
+ }
+
+ public static byte[] encodeSSHECDSAPublicKey(ECPublicKey key) throws IOException {
+ TypesWriter tw = new TypesWriter();
+
+ String curveName = getCurveName(key.getParams());
+
+ String keyFormat = ECDSA_SHA2_PREFIX + curveName;
+
+ tw.writeString(keyFormat);
+
+ tw.writeString(curveName);
+
+ byte[] encoded = encodeECPoint(key.getW(), key.getParams().getCurve());
+ tw.writeString(encoded, 0, encoded.length);
+
+ return tw.getBytes();
+ }
+
+ public static String getCurveName(ECParameterSpec params) throws IOException {
+ int fieldSize = getCurveSize(params);
+ final String curveName = getCurveName(fieldSize);
+ if (curveName == null) {
+ throw new IOException("invalid curve size " + fieldSize);
+ }
+ return curveName;
+ }
+
+ public static String getCurveName(int fieldSize) {
+ String curveName = CURVE_SIZES.get(fieldSize);
+ if (curveName == null) {
+ return null;
+ }
+ return curveName;
+ }
+
+ public static int getCurveSize(ECParameterSpec params) {
+ return params.getCurve().getField().getFieldSize();
+ }
+
+ public static ECParameterSpec getCurveForOID(String oid) {
+ String name = CURVE_OIDS.get(oid);
+ if (name == null)
+ return null;
+ return CURVES.get(name);
+ }
+
+ public static byte[] decodeSSHECDSASignature(byte[] sig) throws IOException {
+ byte[] rsArray = null;
+
+ TypesReader tr = new TypesReader(sig);
+
+ String sig_format = tr.readString();
+ if (sig_format.startsWith(ECDSA_SHA2_PREFIX) == false)
+ throw new IOException("Peer sent wrong signature format");
+
+ String curveName = sig_format.substring(ECDSA_SHA2_PREFIX.length());
+ if (CURVES.containsKey(curveName) == false) {
+ throw new IOException("Unsupported curve: " + curveName);
+ }
+
+ rsArray = tr.readByteString();
+
+ if (tr.remain() != 0)
+ throw new IOException("Padding in ECDSA signature!");
+
+ byte[] rArray;
+ byte[] sArray;
+ {
+ TypesReader rsReader = new TypesReader(rsArray);
+ rArray = rsReader.readMPINT().toByteArray();
+ sArray = rsReader.readMPINT().toByteArray();
+ }
+
+ int first = rArray.length;
+ int second = sArray.length;
+
+ /* We can't have the high bit set, so add an extra zero at the beginning if so. */
+ if ((rArray[0] & 0x80) != 0) {
+ first++;
+ }
+ if ((sArray[0] & 0x80) != 0) {
+ second++;
+ }
+
+ /* Calculate total output length */
+ ByteArrayOutputStream os = new ByteArrayOutputStream(6 + first + second);
+
+ /* ASN.1 SEQUENCE tag */
+ os.write(0x30);
+
+ /* Size of SEQUENCE */
+ writeLength(4 + first + second, os);
+
+ /* ASN.1 INTEGER tag */
+ os.write(0x02);
+
+ /* "r" INTEGER length */
+ writeLength(first, os);
+
+ /* Copy in the "r" INTEGER */
+ if (first != rArray.length) {
+ os.write(0x00);
+ }
+ os.write(rArray);
+
+ /* ASN.1 INTEGER tag */
+ os.write(0x02);
+
+ /* "s" INTEGER length */
+ writeLength(second, os);
+
+ /* Copy in the "s" INTEGER */
+ if (second != sArray.length) {
+ os.write(0x00);
+ }
+ os.write(sArray);
+
+ return os.toByteArray();
+ }
+
+ private static final void writeLength(int length, OutputStream os) throws IOException {
+ if (length <= 0x7F) {
+ os.write(length);
+ return;
+ }
+
+ int numOctets = 0;
+ int lenCopy = length;
+ while (lenCopy != 0) {
+ lenCopy >>>= 8;
+ numOctets++;
+ }
+
+ os.write(0x80 | numOctets);
+
+ for (int i = (numOctets - 1) * 8; i >= 0; i -= 8) {
+ os.write((byte) (length >> i));
+ }
+ }
+
+ public static byte[] encodeSSHECDSASignature(byte[] sig, ECParameterSpec params) throws IOException
+ {
+ TypesWriter tw = new TypesWriter();
+
+ String curveName = getCurveName(params);
+ tw.writeString(ECDSA_SHA2_PREFIX + curveName);
+
+ if ((sig[0] != 0x30) || (sig[1] != sig.length - 2) || (sig[2] != 0x02)) {
+ throw new IOException("Invalid signature format");
+ }
+
+ int rLength = sig[3];
+ if ((rLength + 6 > sig.length) || (sig[4 + rLength] != 0x02)) {
+ throw new IOException("Invalid signature format");
+ }
+
+ int sLength = sig[5 + rLength];
+ if (6 + rLength + sLength > sig.length) {
+ throw new IOException("Invalid signature format");
+ }
+
+ byte[] rArray = new byte[rLength];
+ byte[] sArray = new byte[sLength];
+
+ System.arraycopy(sig, 4, rArray, 0, rLength);
+ System.arraycopy(sig, 6 + rLength, sArray, 0, sLength);
+
+ BigInteger r = new BigInteger(1, rArray);
+ BigInteger s = new BigInteger(1, sArray);
+
+ // Write the <r,s> to its own types writer.
+ TypesWriter rsWriter = new TypesWriter();
+ rsWriter.writeMPInt(r);
+ rsWriter.writeMPInt(s);
+ byte[] encoded = rsWriter.getBytes();
+ tw.writeString(encoded, 0, encoded.length);
+
+ return tw.getBytes();
+ }
+
+ public static byte[] generateSignature(byte[] message, ECPrivateKey pk) throws IOException
+ {
+ final String algo = getSignatureAlgorithmForParams(pk.getParams());
+
+ try {
+ Signature s = Signature.getInstance(algo);
+ s.initSign(pk);
+ s.update(message);
+ return s.sign();
+ } catch (NoSuchAlgorithmException e) {
+ IOException ex = new IOException();
+ ex.initCause(e);
+ throw ex;
+ } catch (InvalidKeyException e) {
+ IOException ex = new IOException();
+ ex.initCause(e);
+ throw ex;
+ } catch (SignatureException e) {
+ IOException ex = new IOException();
+ ex.initCause(e);
+ throw ex;
+ }
+ }
+
+ public static boolean verifySignature(byte[] message, byte[] ds, ECPublicKey dpk) throws IOException
+ {
+ final String algo = getSignatureAlgorithmForParams(dpk.getParams());
+
+ try {
+ Signature s = Signature.getInstance(algo);
+ s.initVerify(dpk);
+ s.update(message);
+ return s.verify(ds);
+ } catch (NoSuchAlgorithmException e) {
+ IOException ex = new IOException("No such algorithm");
+ ex.initCause(e);
+ throw ex;
+ } catch (InvalidKeyException e) {
+ IOException ex = new IOException("No such algorithm");
+ ex.initCause(e);
+ throw ex;
+ } catch (SignatureException e) {
+ IOException ex = new IOException();
+ ex.initCause(e);
+ throw ex;
+ }
+ }
+
+ private static String getSignatureAlgorithmForParams(ECParameterSpec params) {
+ int size = getCurveSize(params);
+ if (size <= 256) {
+ return "SHA256withECDSA";
+ } else if (size <= 384) {
+ return "SHA384withECDSA";
+ } else {
+ return "SHA512withECDSA";
+ }
+ }
+
+ public static String getDigestAlgorithmForParams(ECParameterSpec params) {
+ int size = getCurveSize(params);
+ if (size <= 256) {
+ return "SHA256";
+ } else if (size <= 384) {
+ return "SHA384";
+ } else {
+ return "SHA512";
+ }
+ }
+
+ /**
+ * Decode an OctetString to EllipticCurvePoint according to SECG 2.3.4
+ */
+ public static ECPoint decodeECPoint(byte[] M, EllipticCurve curve) {
+ if (M.length == 0) {
+ return null;
+ }
+
+ // M has len 2 ceil(log_2(q)/8) + 1 ?
+ int elementSize = (curve.getField().getFieldSize() + 7) / 8;
+ if (M.length != 2 * elementSize + 1) {
+ return null;
+ }
+
+ // step 3.2
+ if (M[0] != 0x04) {
+ return null;
+ }
+
+ // Step 3.3
+ byte[] xp = new byte[elementSize];
+ System.arraycopy(M, 1, xp, 0, elementSize);
+
+ // Step 3.4
+ byte[] yp = new byte[elementSize];
+ System.arraycopy(M, 1 + elementSize, yp, 0, elementSize);
+
+ ECPoint P = new ECPoint(new BigInteger(1, xp), new BigInteger(1, yp));
+
+ // TODO check point 3.5
+
+ // Step 3.6
+ return P;
+ }
+
+ /**
+ * Encode EllipticCurvePoint to an OctetString
+ */
+ public static byte[] encodeECPoint(ECPoint group, EllipticCurve curve)
+ {
+ // M has len 2 ceil(log_2(q)/8) + 1 ?
+ int elementSize = (curve.getField().getFieldSize() + 7) / 8;
+ byte[] M = new byte[2 * elementSize + 1];
+
+ // Uncompressed format
+ M[0] = 0x04;
+
+ {
+ byte[] affineX = removeLeadingZeroes(group.getAffineX().toByteArray());
+ System.arraycopy(affineX, 0, M, 1 + elementSize - affineX.length, affineX.length);
+ }
+
+ {
+ byte[] affineY = removeLeadingZeroes(group.getAffineY().toByteArray());
+ System.arraycopy(affineY, 0, M, 1 + elementSize + elementSize - affineY.length,
+ affineY.length);
+ }
+
+ return M;
+ }
+
+ private static byte[] removeLeadingZeroes(byte[] input) {
+ if (input[0] != 0x00) {
+ return input;
+ }
+
+ int pos = 1;
+ while (pos < input.length - 1 && input[pos] == 0x00) {
+ pos++;
+ }
+
+ byte[] output = new byte[input.length - pos];
+ System.arraycopy(input, pos, output, 0, output.length);
+ return output;
+ }
+
+ public static class EllipticCurves {
+ public static ECParameterSpec nistp256 = new ECParameterSpec(
+ new EllipticCurve(
+ new ECFieldFp(new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", 16)),
+ new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", 16),
+ new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16)),
+ new ECPoint(new BigInteger("6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", 16),
+ new BigInteger("4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", 16)),
+ new BigInteger("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", 16),
+ 1);
+
+ public static ECParameterSpec nistp384 = new ECParameterSpec(
+ new EllipticCurve(
+ new ECFieldFp(new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF", 16)),
+ new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC", 16),
+ new BigInteger("B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF", 16)),
+ new ECPoint(new BigInteger("AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7", 16),
+ new BigInteger("3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F", 16)),
+ new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973", 16),
+ 1);
+
+ public static ECParameterSpec nistp521 = new ECParameterSpec(
+ new EllipticCurve(
+ new ECFieldFp(new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16)),
+ new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC", 16),
+ new BigInteger("0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00", 16)),
+ new ECPoint(new BigInteger("00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66", 16),
+ new BigInteger("011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650", 16)),
+ new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409", 16),
+ 1);
+ }
+}
diff --git a/src/com/trilead/ssh2/signature/RSAPrivateKey.java b/src/com/trilead/ssh2/signature/RSAPrivateKey.java
deleted file mode 100644
index 5d5e606..0000000
--- a/src/com/trilead/ssh2/signature/RSAPrivateKey.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.trilead.ssh2.signature;
-
-import java.math.BigInteger;
-
-/**
- * RSAPrivateKey.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: RSAPrivateKey.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public class RSAPrivateKey
-{
- private BigInteger d;
- private BigInteger e;
- private BigInteger n;
-
- public RSAPrivateKey(BigInteger d, BigInteger e, BigInteger n)
- {
- this.d = d;
- this.e = e;
- this.n = n;
- }
-
- public BigInteger getD()
- {
- return d;
- }
-
- public BigInteger getE()
- {
- return e;
- }
-
- public BigInteger getN()
- {
- return n;
- }
-
- public RSAPublicKey getPublicKey()
- {
- return new RSAPublicKey(e, n);
- }
-} \ No newline at end of file
diff --git a/src/com/trilead/ssh2/signature/RSAPublicKey.java b/src/com/trilead/ssh2/signature/RSAPublicKey.java
deleted file mode 100644
index e7e6611..0000000
--- a/src/com/trilead/ssh2/signature/RSAPublicKey.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.trilead.ssh2.signature;
-
-import java.math.BigInteger;
-
-/**
- * RSAPublicKey.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: RSAPublicKey.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public class RSAPublicKey
-{
- BigInteger e;
- BigInteger n;
-
- public RSAPublicKey(BigInteger e, BigInteger n)
- {
- this.e = e;
- this.n = n;
- }
-
- public BigInteger getE()
- {
- return e;
- }
-
- public BigInteger getN()
- {
- return n;
- }
-} \ No newline at end of file
diff --git a/src/com/trilead/ssh2/signature/RSASHA1Verify.java b/src/com/trilead/ssh2/signature/RSASHA1Verify.java
index 8a0f07a..3406312 100644
--- a/src/com/trilead/ssh2/signature/RSASHA1Verify.java
+++ b/src/com/trilead/ssh2/signature/RSASHA1Verify.java
@@ -1,285 +1,180 @@
-
-package com.trilead.ssh2.signature;
-
-import java.io.IOException;
-import java.math.BigInteger;
-
-import com.trilead.ssh2.crypto.SimpleDERReader;
-import com.trilead.ssh2.crypto.digest.SHA1;
-import com.trilead.ssh2.log.Logger;
-import com.trilead.ssh2.packets.TypesReader;
-import com.trilead.ssh2.packets.TypesWriter;
-
-
-/**
- * RSASHA1Verify.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: RSASHA1Verify.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public class RSASHA1Verify
-{
- private static final Logger log = Logger.getLogger(RSASHA1Verify.class);
-
- public static RSAPublicKey decodeSSHRSAPublicKey(byte[] key) throws IOException
- {
- TypesReader tr = new TypesReader(key);
-
- String key_format = tr.readString();
-
- if (key_format.equals("ssh-rsa") == false)
- throw new IllegalArgumentException("This is not a ssh-rsa public key");
-
- BigInteger e = tr.readMPINT();
- BigInteger n = tr.readMPINT();
-
- if (tr.remain() != 0)
- throw new IOException("Padding in RSA public key!");
-
- return new RSAPublicKey(e, n);
- }
-
- public static byte[] encodeSSHRSAPublicKey(RSAPublicKey pk) throws IOException
- {
- TypesWriter tw = new TypesWriter();
-
- tw.writeString("ssh-rsa");
- tw.writeMPInt(pk.getE());
- tw.writeMPInt(pk.getN());
-
- return tw.getBytes();
- }
-
- public static RSASignature decodeSSHRSASignature(byte[] sig) throws IOException
- {
- TypesReader tr = new TypesReader(sig);
-
- String sig_format = tr.readString();
-
- if (sig_format.equals("ssh-rsa") == false)
- throw new IOException("Peer sent wrong signature format");
-
- /* S is NOT an MPINT. "The value for 'rsa_signature_blob' is encoded as a string
- * containing s (which is an integer, without lengths or padding, unsigned and in
- * network byte order)." See also below.
- */
-
- byte[] s = tr.readByteString();
-
- if (s.length == 0)
- throw new IOException("Error in RSA signature, S is empty.");
-
- if (log.isEnabled())
- {
- log.log(80, "Decoding ssh-rsa signature string (length: " + s.length + ")");
- }
-
- if (tr.remain() != 0)
- throw new IOException("Padding in RSA signature!");
-
- return new RSASignature(new BigInteger(1, s));
- }
-
- public static byte[] encodeSSHRSASignature(RSASignature sig) throws IOException
- {
- TypesWriter tw = new TypesWriter();
-
- tw.writeString("ssh-rsa");
-
- /* S is NOT an MPINT. "The value for 'rsa_signature_blob' is encoded as a string
- * containing s (which is an integer, without lengths or padding, unsigned and in
- * network byte order)."
- */
-
- byte[] s = sig.getS().toByteArray();
-
- /* Remove first zero sign byte, if present */
-
- if ((s.length > 1) && (s[0] == 0x00))
- tw.writeString(s, 1, s.length - 1);
- else
- tw.writeString(s, 0, s.length);
-
- return tw.getBytes();
- }
-
- public static RSASignature generateSignature(byte[] message, RSAPrivateKey pk) throws IOException
- {
- SHA1 md = new SHA1();
- md.update(message);
- byte[] sha_message = new byte[md.getDigestLength()];
- md.digest(sha_message);
-
- byte[] der_header = new byte[] { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00,
- 0x04, 0x14 };
-
- int rsa_block_len = (pk.getN().bitLength() + 7) / 8;
-
- int num_pad = rsa_block_len - (2 + der_header.length + sha_message.length) - 1;
-
- if (num_pad < 8)
- throw new IOException("Cannot sign with RSA, message too long");
-
- byte[] sig = new byte[der_header.length + sha_message.length + 2 + num_pad];
-
- sig[0] = 0x01;
-
- for (int i = 0; i < num_pad; i++)
- {
- sig[i + 1] = (byte) 0xff;
- }
-
- sig[num_pad + 1] = 0x00;
-
- System.arraycopy(der_header, 0, sig, 2 + num_pad, der_header.length);
- System.arraycopy(sha_message, 0, sig, 2 + num_pad + der_header.length, sha_message.length);
-
- BigInteger m = new BigInteger(1, sig);
-
- BigInteger s = m.modPow(pk.getD(), pk.getN());
-
- return new RSASignature(s);
- }
-
- public static boolean verifySignature(byte[] message, RSASignature ds, RSAPublicKey dpk) throws IOException
- {
- SHA1 md = new SHA1();
- md.update(message);
- byte[] sha_message = new byte[md.getDigestLength()];
- md.digest(sha_message);
-
- BigInteger n = dpk.getN();
- BigInteger e = dpk.getE();
- BigInteger s = ds.getS();
-
- if (n.compareTo(s) <= 0)
- {
- log.log(20, "ssh-rsa signature: n.compareTo(s) <= 0");
- return false;
- }
-
- int rsa_block_len = (n.bitLength() + 7) / 8;
-
- /* And now the show begins */
-
- if (rsa_block_len < 1)
- {
- log.log(20, "ssh-rsa signature: rsa_block_len < 1");
- return false;
- }
-
- byte[] v = s.modPow(e, n).toByteArray();
-
- int startpos = 0;
-
- if ((v.length > 0) && (v[0] == 0x00))
- startpos++;
-
- if ((v.length - startpos) != (rsa_block_len - 1))
- {
- log.log(20, "ssh-rsa signature: (v.length - startpos) != (rsa_block_len - 1)");
- return false;
- }
-
- if (v[startpos] != 0x01)
- {
- log.log(20, "ssh-rsa signature: v[startpos] != 0x01");
- return false;
- }
-
- int pos = startpos + 1;
-
- while (true)
- {
- if (pos >= v.length)
- {
- log.log(20, "ssh-rsa signature: pos >= v.length");
- return false;
- }
- if (v[pos] == 0x00)
- break;
- if (v[pos] != (byte) 0xff)
- {
- log.log(20, "ssh-rsa signature: v[pos] != (byte) 0xff");
- return false;
- }
- pos++;
- }
-
- int num_pad = pos - (startpos + 1);
-
- if (num_pad < 8)
- {
- log.log(20, "ssh-rsa signature: num_pad < 8");
- return false;
- }
-
- pos++;
-
- if (pos >= v.length)
- {
- log.log(20, "ssh-rsa signature: pos >= v.length");
- return false;
- }
-
- SimpleDERReader dr = new SimpleDERReader(v, pos, v.length - pos);
-
- byte[] seq = dr.readSequenceAsByteArray();
-
- if (dr.available() != 0)
- {
- log.log(20, "ssh-rsa signature: dr.available() != 0");
- return false;
- }
-
- dr.resetInput(seq);
-
- /* Read digestAlgorithm */
-
- byte digestAlgorithm[] = dr.readSequenceAsByteArray();
-
- /* Inspired by RFC 3347, however, ignoring the comment regarding old BER based implementations */
-
- if ((digestAlgorithm.length < 8) || (digestAlgorithm.length > 9))
- {
- log.log(20, "ssh-rsa signature: (digestAlgorithm.length < 8) || (digestAlgorithm.length > 9)");
- return false;
- }
-
- byte[] digestAlgorithm_sha1 = new byte[] { 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00 };
-
- for (int i = 0; i < digestAlgorithm.length; i++)
- {
- if (digestAlgorithm[i] != digestAlgorithm_sha1[i])
- {
- log.log(20, "ssh-rsa signature: digestAlgorithm[i] != digestAlgorithm_sha1[i]");
- return false;
- }
- }
-
- byte[] digest = dr.readOctetString();
-
- if (dr.available() != 0)
- {
- log.log(20, "ssh-rsa signature: dr.available() != 0 (II)");
- return false;
- }
-
- if (digest.length != sha_message.length)
- {
- log.log(20, "ssh-rsa signature: digest.length != sha_message.length");
- return false;
- }
-
- for (int i = 0; i < sha_message.length; i++)
- {
- if (sha_message[i] != digest[i])
- {
- log.log(20, "ssh-rsa signature: sha_message[i] != digest[i]");
- return false;
- }
- }
-
- return true;
- }
-}
+
+package com.trilead.ssh2.signature;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.security.spec.RSAPublicKeySpec;
+
+import com.trilead.ssh2.log.Logger;
+import com.trilead.ssh2.packets.TypesReader;
+import com.trilead.ssh2.packets.TypesWriter;
+
+
+/**
+ * RSASHA1Verify.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: RSASHA1Verify.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
+ */
+public class RSASHA1Verify
+{
+ private static final Logger log = Logger.getLogger(RSASHA1Verify.class);
+
+ public static RSAPublicKey decodeSSHRSAPublicKey(byte[] key) throws IOException
+ {
+ TypesReader tr = new TypesReader(key);
+
+ String key_format = tr.readString();
+
+ if (key_format.equals("ssh-rsa") == false)
+ throw new IllegalArgumentException("This is not a ssh-rsa public key");
+
+ BigInteger e = tr.readMPINT();
+ BigInteger n = tr.readMPINT();
+
+ if (tr.remain() != 0)
+ throw new IOException("Padding in RSA public key!");
+
+ KeySpec keySpec = new RSAPublicKeySpec(n, e);
+
+ try {
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ return (RSAPublicKey) kf.generatePublic(keySpec);
+ } catch (NoSuchAlgorithmException nsae) {
+ IOException ioe = new IOException("No RSA KeyFactory available");
+ ioe.initCause(nsae);
+ throw ioe;
+ } catch (InvalidKeySpecException ikse) {
+ IOException ioe = new IOException("No RSA KeyFactory available");
+ ioe.initCause(ikse);
+ throw ioe;
+ }
+ }
+
+ public static byte[] encodeSSHRSAPublicKey(RSAPublicKey pk) throws IOException
+ {
+ TypesWriter tw = new TypesWriter();
+
+ tw.writeString("ssh-rsa");
+ tw.writeMPInt(pk.getPublicExponent());
+ tw.writeMPInt(pk.getModulus());
+
+ return tw.getBytes();
+ }
+
+ public static byte[] decodeSSHRSASignature(byte[] sig) throws IOException
+ {
+ TypesReader tr = new TypesReader(sig);
+
+ String sig_format = tr.readString();
+
+ if (sig_format.equals("ssh-rsa") == false)
+ throw new IOException("Peer sent wrong signature format");
+
+ /* S is NOT an MPINT. "The value for 'rsa_signature_blob' is encoded as a string
+ * containing s (which is an integer, without lengths or padding, unsigned and in
+ * network byte order)." See also below.
+ */
+
+ byte[] s = tr.readByteString();
+
+ if (s.length == 0)
+ throw new IOException("Error in RSA signature, S is empty.");
+
+ if (log.isEnabled())
+ {
+ log.log(80, "Decoding ssh-rsa signature string (length: " + s.length + ")");
+ }
+
+ if (tr.remain() != 0)
+ throw new IOException("Padding in RSA signature!");
+
+ if (s[0] == 0 && s[1] == 0 && s[2] == 0) {
+ int i = 0;
+ int j = ((s[i++] << 24) & 0xff000000) | ((s[i++] << 16) & 0x00ff0000)
+ | ((s[i++] << 8) & 0x0000ff00) | ((s[i++]) & 0x000000ff);
+ i += j;
+ j = ((s[i++] << 24) & 0xff000000) | ((s[i++] << 16) & 0x00ff0000)
+ | ((s[i++] << 8) & 0x0000ff00) | ((s[i++]) & 0x000000ff);
+ byte[] tmp = new byte[j];
+ System.arraycopy(s, i, tmp, 0, j);
+ sig = tmp;
+ }
+
+ return s;
+ }
+
+ public static byte[] encodeSSHRSASignature(byte[] s) throws IOException
+ {
+ TypesWriter tw = new TypesWriter();
+
+ tw.writeString("ssh-rsa");
+
+ /* S is NOT an MPINT. "The value for 'rsa_signature_blob' is encoded as a string
+ * containing s (which is an integer, without lengths or padding, unsigned and in
+ * network byte order)."
+ */
+
+ /* Remove first zero sign byte, if present */
+
+ if ((s.length > 1) && (s[0] == 0x00))
+ tw.writeString(s, 1, s.length - 1);
+ else
+ tw.writeString(s, 0, s.length);
+
+ return tw.getBytes();
+ }
+
+ public static byte[] generateSignature(byte[] message, RSAPrivateKey pk) throws IOException
+ {
+ try {
+ Signature s = Signature.getInstance("SHA1withRSA");
+ s.initSign(pk);
+ s.update(message);
+ return s.sign();
+ } catch (NoSuchAlgorithmException e) {
+ IOException ex = new IOException();
+ ex.initCause(e);
+ throw ex;
+ } catch (InvalidKeyException e) {
+ IOException ex = new IOException();
+ ex.initCause(e);
+ throw ex;
+ } catch (SignatureException e) {
+ IOException ex = new IOException();
+ ex.initCause(e);
+ throw ex;
+ }
+ }
+
+ public static boolean verifySignature(byte[] message, byte[] ds, RSAPublicKey dpk) throws IOException
+ {
+ try {
+ Signature s = Signature.getInstance("SHA1withRSA");
+ s.initVerify(dpk);
+ s.update(message);
+ return s.verify(ds);
+ } catch (NoSuchAlgorithmException e) {
+ IOException ex = new IOException();
+ ex.initCause(e);
+ throw ex;
+ } catch (InvalidKeyException e) {
+ IOException ex = new IOException();
+ ex.initCause(e);
+ throw ex;
+ } catch (SignatureException e) {
+ IOException ex = new IOException();
+ ex.initCause(e);
+ throw ex;
+ }
+ }
+}
diff --git a/src/com/trilead/ssh2/signature/RSASignature.java b/src/com/trilead/ssh2/signature/RSASignature.java
deleted file mode 100644
index e04e7ee..0000000
--- a/src/com/trilead/ssh2/signature/RSASignature.java
+++ /dev/null
@@ -1,27 +0,0 @@
-
-package com.trilead.ssh2.signature;
-
-import java.math.BigInteger;
-
-
-/**
- * RSASignature.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: RSASignature.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-
-public class RSASignature
-{
- BigInteger s;
-
- public BigInteger getS()
- {
- return s;
- }
-
- public RSASignature(BigInteger s)
- {
- this.s = s;
- }
-} \ No newline at end of file
diff --git a/src/com/trilead/ssh2/transport/ClientServerHello.java b/src/com/trilead/ssh2/transport/ClientServerHello.java
index 23726f3..d7a5ee5 100644
--- a/src/com/trilead/ssh2/transport/ClientServerHello.java
+++ b/src/com/trilead/ssh2/transport/ClientServerHello.java
@@ -1,125 +1,125 @@
-
-package com.trilead.ssh2.transport;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
-
-import com.trilead.ssh2.Connection;
-
-/**
- * ClientServerHello.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: ClientServerHello.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
- */
-public class ClientServerHello
-{
- String server_line;
- String client_line;
-
- String server_versioncomment;
-
- public final static int readLineRN(InputStream is, byte[] buffer) throws IOException
- {
- int pos = 0;
- boolean need10 = false;
- int len = 0;
- while (true)
- {
- int c = is.read();
- if (c == -1)
- throw new IOException("Premature connection close");
-
- buffer[pos++] = (byte) c;
-
- if (c == 13)
- {
- need10 = true;
- continue;
- }
-
- if (c == 10)
- break;
-
- if (need10 == true)
- throw new IOException("Malformed line sent by the server, the line does not end correctly.");
-
- len++;
- if (pos >= buffer.length)
- throw new IOException("The server sent a too long line.");
- }
-
- return len;
- }
-
- public ClientServerHello(InputStream bi, OutputStream bo) throws IOException
- {
- client_line = "SSH-2.0-" + Connection.identification;
-
- bo.write((client_line + "\r\n").getBytes("ISO-8859-1"));
- bo.flush();
-
- byte[] serverVersion = new byte[512];
-
- for (int i = 0; i < 50; i++)
- {
- int len = readLineRN(bi, serverVersion);
-
- server_line = new String(serverVersion, 0, len, "ISO-8859-1");
-
- if (server_line.startsWith("SSH-"))
- break;
- }
-
- if (server_line.startsWith("SSH-") == false)
- throw new IOException(
- "Malformed server identification string. There was no line starting with 'SSH-' amongst the first 50 lines.");
-
- if (server_line.startsWith("SSH-1.99-"))
- server_versioncomment = server_line.substring(9);
- else if (server_line.startsWith("SSH-2.0-"))
- server_versioncomment = server_line.substring(8);
- else
- throw new IOException("Server uses incompatible protocol, it is not SSH-2 compatible.");
- }
-
- /**
- * @return Returns the client_versioncomment.
- */
- public byte[] getClientString()
- {
- byte[] result;
-
- try
- {
- result = client_line.getBytes("ISO-8859-1");
- }
- catch (UnsupportedEncodingException ign)
- {
- result = client_line.getBytes();
- }
-
- return result;
- }
-
- /**
- * @return Returns the server_versioncomment.
- */
- public byte[] getServerString()
- {
- byte[] result;
-
- try
- {
- result = server_line.getBytes("ISO-8859-1");
- }
- catch (UnsupportedEncodingException ign)
- {
- result = server_line.getBytes();
- }
-
- return result;
- }
-}
+
+package com.trilead.ssh2.transport;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+
+import com.trilead.ssh2.Connection;
+
+/**
+ * ClientServerHello.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: ClientServerHello.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
+ */
+public class ClientServerHello
+{
+ String server_line;
+ String client_line;
+
+ String server_versioncomment;
+
+ public final static int readLineRN(InputStream is, byte[] buffer) throws IOException
+ {
+ int pos = 0;
+ boolean need10 = false;
+ int len = 0;
+ while (true)
+ {
+ int c = is.read();
+ if (c == -1)
+ throw new IOException("Premature connection close");
+
+ buffer[pos++] = (byte) c;
+
+ if (c == 13)
+ {
+ need10 = true;
+ continue;
+ }
+
+ if (c == 10)
+ break;
+
+ if (need10 == true)
+ throw new IOException("Malformed line sent by the server, the line does not end correctly.");
+
+ len++;
+ if (pos >= buffer.length)
+ throw new IOException("The server sent a too long line.");
+ }
+
+ return len;
+ }
+
+ public ClientServerHello(InputStream bi, OutputStream bo) throws IOException
+ {
+ client_line = "SSH-2.0-" + Connection.identification;
+
+ bo.write((client_line + "\r\n").getBytes("ISO-8859-1"));
+ bo.flush();
+
+ byte[] serverVersion = new byte[512];
+
+ for (int i = 0; i < 50; i++)
+ {
+ int len = readLineRN(bi, serverVersion);
+
+ server_line = new String(serverVersion, 0, len, "ISO-8859-1");
+
+ if (server_line.startsWith("SSH-"))
+ break;
+ }
+
+ if (server_line.startsWith("SSH-") == false)
+ throw new IOException(
+ "Malformed server identification string. There was no line starting with 'SSH-' amongst the first 50 lines.");
+
+ if (server_line.startsWith("SSH-1.99-"))
+ server_versioncomment = server_line.substring(9);
+ else if (server_line.startsWith("SSH-2.0-"))
+ server_versioncomment = server_line.substring(8);
+ else
+ throw new IOException("Server uses incompatible protocol, it is not SSH-2 compatible.");
+ }
+
+ /**
+ * @return Returns the client_versioncomment.
+ */
+ public byte[] getClientString()
+ {
+ byte[] result;
+
+ try
+ {
+ result = client_line.getBytes("ISO-8859-1");
+ }
+ catch (UnsupportedEncodingException ign)
+ {
+ result = client_line.getBytes();
+ }
+
+ return result;
+ }
+
+ /**
+ * @return Returns the server_versioncomment.
+ */
+ public byte[] getServerString()
+ {
+ byte[] result;
+
+ try
+ {
+ result = server_line.getBytes("ISO-8859-1");
+ }
+ catch (UnsupportedEncodingException ign)
+ {
+ result = server_line.getBytes();
+ }
+
+ return result;
+ }
+}
diff --git a/src/com/trilead/ssh2/transport/KexManager.java b/src/com/trilead/ssh2/transport/KexManager.java
index 476d93f..ee0784a 100644
--- a/src/com/trilead/ssh2/transport/KexManager.java
+++ b/src/com/trilead/ssh2/transport/KexManager.java
@@ -1,638 +1,690 @@
-
-package com.trilead.ssh2.transport;
-
-import java.io.IOException;
-import java.security.SecureRandom;
-
-import com.trilead.ssh2.ConnectionInfo;
-import com.trilead.ssh2.DHGexParameters;
-import com.trilead.ssh2.ServerHostKeyVerifier;
-import com.trilead.ssh2.compression.CompressionFactory;
-import com.trilead.ssh2.compression.ICompressor;
-import com.trilead.ssh2.crypto.CryptoWishList;
-import com.trilead.ssh2.crypto.KeyMaterial;
-import com.trilead.ssh2.crypto.cipher.BlockCipher;
-import com.trilead.ssh2.crypto.cipher.BlockCipherFactory;
-import com.trilead.ssh2.crypto.dh.DhExchange;
-import com.trilead.ssh2.crypto.dh.DhGroupExchange;
-import com.trilead.ssh2.crypto.digest.MAC;
-import com.trilead.ssh2.log.Logger;
-import com.trilead.ssh2.packets.PacketKexDHInit;
-import com.trilead.ssh2.packets.PacketKexDHReply;
-import com.trilead.ssh2.packets.PacketKexDhGexGroup;
-import com.trilead.ssh2.packets.PacketKexDhGexInit;
-import com.trilead.ssh2.packets.PacketKexDhGexReply;
-import com.trilead.ssh2.packets.PacketKexDhGexRequest;
-import com.trilead.ssh2.packets.PacketKexDhGexRequestOld;
-import com.trilead.ssh2.packets.PacketKexInit;
-import com.trilead.ssh2.packets.PacketNewKeys;
-import com.trilead.ssh2.packets.Packets;
-import com.trilead.ssh2.signature.DSAPublicKey;
-import com.trilead.ssh2.signature.DSASHA1Verify;
-import com.trilead.ssh2.signature.DSASignature;
-import com.trilead.ssh2.signature.RSAPublicKey;
-import com.trilead.ssh2.signature.RSASHA1Verify;
-import com.trilead.ssh2.signature.RSASignature;
-
-
-/**
- * KexManager.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: KexManager.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public class KexManager
-{
- private static final Logger log = Logger.getLogger(KexManager.class);
-
- KexState kxs;
- int kexCount = 0;
- KeyMaterial km;
- byte[] sessionId;
- ClientServerHello csh;
-
- final Object accessLock = new Object();
- ConnectionInfo lastConnInfo = null;
-
- boolean connectionClosed = false;
-
- boolean ignore_next_kex_packet = false;
-
- final TransportManager tm;
-
- CryptoWishList nextKEXcryptoWishList;
- DHGexParameters nextKEXdhgexParameters;
-
- ServerHostKeyVerifier verifier;
- final String hostname;
- final int port;
- final SecureRandom rnd;
-
- public KexManager(TransportManager tm, ClientServerHello csh, CryptoWishList initialCwl, String hostname, int port,
- ServerHostKeyVerifier keyVerifier, SecureRandom rnd)
- {
- this.tm = tm;
- this.csh = csh;
- this.nextKEXcryptoWishList = initialCwl;
- this.nextKEXdhgexParameters = new DHGexParameters();
- this.hostname = hostname;
- this.port = port;
- this.verifier = keyVerifier;
- this.rnd = rnd;
- }
-
- public ConnectionInfo getOrWaitForConnectionInfo(int minKexCount) throws IOException
- {
- synchronized (accessLock)
- {
- while (true)
- {
- if ((lastConnInfo != null) && (lastConnInfo.keyExchangeCounter >= minKexCount))
- return lastConnInfo;
-
- if (connectionClosed)
- throw (IOException) new IOException("Key exchange was not finished, connection is closed.")
- .initCause(tm.getReasonClosedCause());
-
- try
- {
- accessLock.wait();
- }
- catch (InterruptedException e)
- {
- }
- }
- }
- }
-
- private String getFirstMatch(String[] client, String[] server) throws NegotiateException
- {
- if (client == null || server == null)
- throw new IllegalArgumentException();
-
- if (client.length == 0)
- return null;
-
- for (int i = 0; i < client.length; i++)
- {
- for (int j = 0; j < server.length; j++)
- {
- if (client[i].equals(server[j]))
- return client[i];
- }
- }
- throw new NegotiateException();
- }
-
- private boolean compareFirstOfNameList(String[] a, String[] b)
- {
- if (a == null || b == null)
- throw new IllegalArgumentException();
-
- if ((a.length == 0) && (b.length == 0))
- return true;
-
- if ((a.length == 0) || (b.length == 0))
- return false;
-
- return (a[0].equals(b[0]));
- }
-
- private boolean isGuessOK(KexParameters cpar, KexParameters spar)
- {
- if (cpar == null || spar == null)
- throw new IllegalArgumentException();
-
- if (compareFirstOfNameList(cpar.kex_algorithms, spar.kex_algorithms) == false)
- {
- return false;
- }
-
- if (compareFirstOfNameList(cpar.server_host_key_algorithms, spar.server_host_key_algorithms) == false)
- {
- return false;
- }
-
- /*
- * We do NOT check here if the other algorithms can be agreed on, this
- * is just a check if kex_algorithms and server_host_key_algorithms were
- * guessed right!
- */
-
- return true;
- }
-
- private NegotiatedParameters mergeKexParameters(KexParameters client, KexParameters server)
- {
- NegotiatedParameters np = new NegotiatedParameters();
-
- try
- {
- np.kex_algo = getFirstMatch(client.kex_algorithms, server.kex_algorithms);
-
- log.log(20, "kex_algo=" + np.kex_algo);
-
- np.server_host_key_algo = getFirstMatch(client.server_host_key_algorithms,
- server.server_host_key_algorithms);
-
- log.log(20, "server_host_key_algo=" + np.server_host_key_algo);
-
- np.enc_algo_client_to_server = getFirstMatch(client.encryption_algorithms_client_to_server,
- server.encryption_algorithms_client_to_server);
- np.enc_algo_server_to_client = getFirstMatch(client.encryption_algorithms_server_to_client,
- server.encryption_algorithms_server_to_client);
-
- log.log(20, "enc_algo_client_to_server=" + np.enc_algo_client_to_server);
- log.log(20, "enc_algo_server_to_client=" + np.enc_algo_server_to_client);
-
- np.mac_algo_client_to_server = getFirstMatch(client.mac_algorithms_client_to_server,
- server.mac_algorithms_client_to_server);
- np.mac_algo_server_to_client = getFirstMatch(client.mac_algorithms_server_to_client,
- server.mac_algorithms_server_to_client);
-
- log.log(20, "mac_algo_client_to_server=" + np.mac_algo_client_to_server);
- log.log(20, "mac_algo_server_to_client=" + np.mac_algo_server_to_client);
-
- np.comp_algo_client_to_server = getFirstMatch(client.compression_algorithms_client_to_server,
- server.compression_algorithms_client_to_server);
- np.comp_algo_server_to_client = getFirstMatch(client.compression_algorithms_server_to_client,
- server.compression_algorithms_server_to_client);
-
- log.log(20, "comp_algo_client_to_server=" + np.comp_algo_client_to_server);
- log.log(20, "comp_algo_server_to_client=" + np.comp_algo_server_to_client);
-
- }
- catch (NegotiateException e)
- {
- return null;
- }
-
- try
- {
- np.lang_client_to_server = getFirstMatch(client.languages_client_to_server,
- server.languages_client_to_server);
- }
- catch (NegotiateException e1)
- {
- np.lang_client_to_server = null;
- }
-
- try
- {
- np.lang_server_to_client = getFirstMatch(client.languages_server_to_client,
- server.languages_server_to_client);
- }
- catch (NegotiateException e2)
- {
- np.lang_server_to_client = null;
- }
-
- if (isGuessOK(client, server))
- np.guessOK = true;
-
- return np;
- }
-
- public synchronized void initiateKEX(CryptoWishList cwl, DHGexParameters dhgex) throws IOException
- {
- nextKEXcryptoWishList = cwl;
- nextKEXdhgexParameters = dhgex;
-
- if (kxs == null)
- {
- kxs = new KexState();
-
- kxs.dhgexParameters = nextKEXdhgexParameters;
- PacketKexInit kp = new PacketKexInit(nextKEXcryptoWishList, rnd);
- kxs.localKEX = kp;
- tm.sendKexMessage(kp.getPayload());
- }
- }
-
- private boolean establishKeyMaterial()
- {
- try
- {
- int mac_cs_key_len = MAC.getKeyLen(kxs.np.mac_algo_client_to_server);
- int enc_cs_key_len = BlockCipherFactory.getKeySize(kxs.np.enc_algo_client_to_server);
- int enc_cs_block_len = BlockCipherFactory.getBlockSize(kxs.np.enc_algo_client_to_server);
-
- int mac_sc_key_len = MAC.getKeyLen(kxs.np.mac_algo_server_to_client);
- int enc_sc_key_len = BlockCipherFactory.getKeySize(kxs.np.enc_algo_server_to_client);
- int enc_sc_block_len = BlockCipherFactory.getBlockSize(kxs.np.enc_algo_server_to_client);
-
- km = KeyMaterial.create("SHA1", kxs.H, kxs.K, sessionId, enc_cs_key_len, enc_cs_block_len, mac_cs_key_len,
- enc_sc_key_len, enc_sc_block_len, mac_sc_key_len);
- }
- catch (IllegalArgumentException e)
- {
- return false;
- }
- return true;
- }
-
- private void finishKex() throws IOException
- {
- if (sessionId == null)
- sessionId = kxs.H;
-
- establishKeyMaterial();
-
- /* Tell the other side that we start using the new material */
-
- PacketNewKeys ign = new PacketNewKeys();
- tm.sendKexMessage(ign.getPayload());
-
- BlockCipher cbc;
- MAC mac;
- ICompressor comp;
-
- try
- {
- cbc = BlockCipherFactory.createCipher(kxs.np.enc_algo_client_to_server, true, km.enc_key_client_to_server,
- km.initial_iv_client_to_server);
-
- mac = new MAC(kxs.np.mac_algo_client_to_server, km.integrity_key_client_to_server);
-
- comp = CompressionFactory.createCompressor(kxs.np.comp_algo_client_to_server);
-
- }
- catch (IllegalArgumentException e1)
- {
- throw new IOException("Fatal error during MAC startup!");
- }
-
- tm.changeSendCipher(cbc, mac);
- tm.changeSendCompression(comp);
- tm.kexFinished();
- }
-
- public static final String[] getDefaultServerHostkeyAlgorithmList()
- {
- return new String[] { "ssh-rsa", "ssh-dss" };
- }
-
- public static final void checkServerHostkeyAlgorithmsList(String[] algos)
- {
- for (int i = 0; i < algos.length; i++)
- {
- if (("ssh-rsa".equals(algos[i]) == false) && ("ssh-dss".equals(algos[i]) == false))
- throw new IllegalArgumentException("Unknown server host key algorithm '" + algos[i] + "'");
- }
- }
-
- public static final String[] getDefaultKexAlgorithmList()
- {
- return new String[] { "diffie-hellman-group-exchange-sha1", "diffie-hellman-group14-sha1",
- "diffie-hellman-group1-sha1" };
- }
-
- public static final void checkKexAlgorithmList(String[] algos)
- {
- for (int i = 0; i < algos.length; i++)
- {
- if ("diffie-hellman-group-exchange-sha1".equals(algos[i]))
- continue;
-
- if ("diffie-hellman-group14-sha1".equals(algos[i]))
- continue;
-
- if ("diffie-hellman-group1-sha1".equals(algos[i]))
- continue;
-
- throw new IllegalArgumentException("Unknown kex algorithm '" + algos[i] + "'");
- }
- }
-
- private boolean verifySignature(byte[] sig, byte[] hostkey) throws IOException
- {
- if (kxs.np.server_host_key_algo.equals("ssh-rsa"))
- {
- RSASignature rs = RSASHA1Verify.decodeSSHRSASignature(sig);
- RSAPublicKey rpk = RSASHA1Verify.decodeSSHRSAPublicKey(hostkey);
-
- log.log(50, "Verifying ssh-rsa signature");
-
- return RSASHA1Verify.verifySignature(kxs.H, rs, rpk);
- }
-
- if (kxs.np.server_host_key_algo.equals("ssh-dss"))
- {
- DSASignature ds = DSASHA1Verify.decodeSSHDSASignature(sig);
- DSAPublicKey dpk = DSASHA1Verify.decodeSSHDSAPublicKey(hostkey);
-
- log.log(50, "Verifying ssh-dss signature");
-
- return DSASHA1Verify.verifySignature(kxs.H, ds, dpk);
- }
-
- throw new IOException("Unknown server host key algorithm '" + kxs.np.server_host_key_algo + "'");
- }
-
- public synchronized void handleMessage(byte[] msg, int msglen) throws IOException
- {
- PacketKexInit kip;
-
- if (msg == null)
- {
- synchronized (accessLock)
- {
- connectionClosed = true;
- accessLock.notifyAll();
- return;
- }
- }
-
- if ((kxs == null) && (msg[0] != Packets.SSH_MSG_KEXINIT))
- throw new IOException("Unexpected KEX message (type " + msg[0] + ")");
-
- if (ignore_next_kex_packet)
- {
- ignore_next_kex_packet = false;
- return;
- }
-
- if (msg[0] == Packets.SSH_MSG_KEXINIT)
- {
- if ((kxs != null) && (kxs.state != 0))
- throw new IOException("Unexpected SSH_MSG_KEXINIT message during on-going kex exchange!");
-
- if (kxs == null)
- {
- /*
- * Ah, OK, peer wants to do KEX. Let's be nice and play
- * together.
- */
- kxs = new KexState();
- kxs.dhgexParameters = nextKEXdhgexParameters;
- kip = new PacketKexInit(nextKEXcryptoWishList, rnd);
- kxs.localKEX = kip;
- tm.sendKexMessage(kip.getPayload());
- }
-
- kip = new PacketKexInit(msg, 0, msglen);
- kxs.remoteKEX = kip;
-
- kxs.np = mergeKexParameters(kxs.localKEX.getKexParameters(), kxs.remoteKEX.getKexParameters());
-
- if (kxs.np == null)
- throw new IOException("Cannot negotiate, proposals do not match.");
-
- if (kxs.remoteKEX.isFirst_kex_packet_follows() && (kxs.np.guessOK == false))
- {
- /*
- * Guess was wrong, we need to ignore the next kex packet.
- */
-
- ignore_next_kex_packet = true;
- }
-
- if (kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha1"))
- {
- if (kxs.dhgexParameters.getMin_group_len() == 0 || csh.server_versioncomment.matches("OpenSSH_2\\.([0-4]\\.|5\\.[0-2]).*"))
- {
- PacketKexDhGexRequestOld dhgexreq = new PacketKexDhGexRequestOld(kxs.dhgexParameters);
- tm.sendKexMessage(dhgexreq.getPayload());
-
- }
- else
- {
- PacketKexDhGexRequest dhgexreq = new PacketKexDhGexRequest(kxs.dhgexParameters);
- tm.sendKexMessage(dhgexreq.getPayload());
- }
- kxs.state = 1;
- return;
- }
-
- if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")
- || kxs.np.kex_algo.equals("diffie-hellman-group14-sha1"))
- {
- kxs.dhx = new DhExchange();
-
- if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1"))
- kxs.dhx.init(1, rnd);
- else
- kxs.dhx.init(14, rnd);
-
- PacketKexDHInit kp = new PacketKexDHInit(kxs.dhx.getE());
- tm.sendKexMessage(kp.getPayload());
- kxs.state = 1;
- return;
- }
-
- throw new IllegalStateException("Unkown KEX method!");
- }
-
- if (msg[0] == Packets.SSH_MSG_NEWKEYS)
- {
- if (km == null)
- throw new IOException("Peer sent SSH_MSG_NEWKEYS, but I have no key material ready!");
-
- BlockCipher cbc;
- MAC mac;
- ICompressor comp;
-
- try
- {
- cbc = BlockCipherFactory.createCipher(kxs.np.enc_algo_server_to_client, false,
- km.enc_key_server_to_client, km.initial_iv_server_to_client);
-
- mac = new MAC(kxs.np.mac_algo_server_to_client, km.integrity_key_server_to_client);
-
- comp = CompressionFactory.createCompressor(kxs.np.comp_algo_server_to_client);
- }
- catch (IllegalArgumentException e1)
- {
- throw new IOException("Fatal error during MAC startup!");
- }
-
- tm.changeRecvCipher(cbc, mac);
- tm.changeRecvCompression(comp);
-
- ConnectionInfo sci = new ConnectionInfo();
-
- kexCount++;
-
- sci.keyExchangeAlgorithm = kxs.np.kex_algo;
- sci.keyExchangeCounter = kexCount;
- sci.clientToServerCryptoAlgorithm = kxs.np.enc_algo_client_to_server;
- sci.serverToClientCryptoAlgorithm = kxs.np.enc_algo_server_to_client;
- sci.clientToServerMACAlgorithm = kxs.np.mac_algo_client_to_server;
- sci.serverToClientMACAlgorithm = kxs.np.mac_algo_server_to_client;
- sci.serverHostKeyAlgorithm = kxs.np.server_host_key_algo;
- sci.serverHostKey = kxs.hostkey;
-
- synchronized (accessLock)
- {
- lastConnInfo = sci;
- accessLock.notifyAll();
- }
-
- kxs = null;
- return;
- }
-
- if ((kxs == null) || (kxs.state == 0))
- throw new IOException("Unexpected Kex submessage!");
-
- if (kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha1"))
- {
- if (kxs.state == 1)
- {
- PacketKexDhGexGroup dhgexgrp = new PacketKexDhGexGroup(msg, 0, msglen);
- kxs.dhgx = new DhGroupExchange(dhgexgrp.getP(), dhgexgrp.getG());
- kxs.dhgx.init(rnd);
- PacketKexDhGexInit dhgexinit = new PacketKexDhGexInit(kxs.dhgx.getE());
- tm.sendKexMessage(dhgexinit.getPayload());
- kxs.state = 2;
- return;
- }
-
- if (kxs.state == 2)
- {
- PacketKexDhGexReply dhgexrpl = new PacketKexDhGexReply(msg, 0, msglen);
-
- kxs.hostkey = dhgexrpl.getHostKey();
-
- if (verifier != null)
- {
- boolean vres = false;
-
- try
- {
- vres = verifier.verifyServerHostKey(hostname, port, kxs.np.server_host_key_algo, kxs.hostkey);
- }
- catch (Exception e)
- {
- throw (IOException) new IOException(
- "The server hostkey was not accepted by the verifier callback.").initCause(e);
- }
-
- if (vres == false)
- throw new IOException("The server hostkey was not accepted by the verifier callback");
- }
-
- kxs.dhgx.setF(dhgexrpl.getF());
-
- try
- {
- kxs.H = kxs.dhgx.calculateH(csh.getClientString(), csh.getServerString(),
- kxs.localKEX.getPayload(), kxs.remoteKEX.getPayload(), dhgexrpl.getHostKey(),
- kxs.dhgexParameters);
- }
- catch (IllegalArgumentException e)
- {
- throw (IOException) new IOException("KEX error.").initCause(e);
- }
-
- boolean res = verifySignature(dhgexrpl.getSignature(), kxs.hostkey);
-
- if (res == false)
- throw new IOException("Hostkey signature sent by remote is wrong!");
-
- kxs.K = kxs.dhgx.getK();
-
- finishKex();
- kxs.state = -1;
- return;
- }
-
- throw new IllegalStateException("Illegal State in KEX Exchange!");
- }
-
- if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")
- || kxs.np.kex_algo.equals("diffie-hellman-group14-sha1"))
- {
- if (kxs.state == 1)
- {
-
- PacketKexDHReply dhr = new PacketKexDHReply(msg, 0, msglen);
-
- kxs.hostkey = dhr.getHostKey();
-
- if (verifier != null)
- {
- boolean vres = false;
-
- try
- {
- vres = verifier.verifyServerHostKey(hostname, port, kxs.np.server_host_key_algo, kxs.hostkey);
- }
- catch (Exception e)
- {
- throw (IOException) new IOException(
- "The server hostkey was not accepted by the verifier callback.").initCause(e);
- }
-
- if (vres == false)
- throw new IOException("The server hostkey was not accepted by the verifier callback");
- }
-
- kxs.dhx.setF(dhr.getF());
-
- try
- {
- kxs.H = kxs.dhx.calculateH(csh.getClientString(), csh.getServerString(), kxs.localKEX.getPayload(),
- kxs.remoteKEX.getPayload(), dhr.getHostKey());
- }
- catch (IllegalArgumentException e)
- {
- throw (IOException) new IOException("KEX error.").initCause(e);
- }
-
- boolean res = verifySignature(dhr.getSignature(), kxs.hostkey);
-
- if (res == false)
- throw new IOException("Hostkey signature sent by remote is wrong!");
-
- kxs.K = kxs.dhx.getK();
-
- finishKex();
- kxs.state = -1;
- return;
- }
- }
-
- throw new IllegalStateException("Unkown KEX method! (" + kxs.np.kex_algo + ")");
- }
-}
+
+package com.trilead.ssh2.transport;
+
+import java.io.IOException;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.TreeSet;
+
+import com.trilead.ssh2.ConnectionInfo;
+import com.trilead.ssh2.DHGexParameters;
+import com.trilead.ssh2.ServerHostKeyVerifier;
+import com.trilead.ssh2.compression.CompressionFactory;
+import com.trilead.ssh2.compression.ICompressor;
+import com.trilead.ssh2.crypto.CryptoWishList;
+import com.trilead.ssh2.crypto.KeyMaterial;
+import com.trilead.ssh2.crypto.cipher.BlockCipher;
+import com.trilead.ssh2.crypto.cipher.BlockCipherFactory;
+import com.trilead.ssh2.crypto.dh.DhGroupExchange;
+import com.trilead.ssh2.crypto.dh.GenericDhExchange;
+import com.trilead.ssh2.crypto.digest.MAC;
+import com.trilead.ssh2.log.Logger;
+import com.trilead.ssh2.packets.PacketKexDHInit;
+import com.trilead.ssh2.packets.PacketKexDHReply;
+import com.trilead.ssh2.packets.PacketKexDhGexGroup;
+import com.trilead.ssh2.packets.PacketKexDhGexInit;
+import com.trilead.ssh2.packets.PacketKexDhGexReply;
+import com.trilead.ssh2.packets.PacketKexDhGexRequest;
+import com.trilead.ssh2.packets.PacketKexDhGexRequestOld;
+import com.trilead.ssh2.packets.PacketKexInit;
+import com.trilead.ssh2.packets.PacketNewKeys;
+import com.trilead.ssh2.packets.Packets;
+import com.trilead.ssh2.signature.DSASHA1Verify;
+import com.trilead.ssh2.signature.ECDSASHA2Verify;
+import com.trilead.ssh2.signature.RSASHA1Verify;
+
+
+/**
+ * KexManager.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: KexManager.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+public class KexManager
+{
+ private static final Logger log = Logger.getLogger(KexManager.class);
+
+ private static final boolean supportsEc;
+ static {
+ KeyFactory keyFact;
+ try {
+ keyFact = KeyFactory.getInstance("EC");
+ } catch (NoSuchAlgorithmException ignored) {
+ keyFact = null;
+ log.log(10, "Disabling EC support due to lack of KeyFactory");
+ }
+ supportsEc = keyFact != null;
+ }
+
+ private static final Set<String> HOSTKEY_ALGS = new LinkedHashSet<String>();
+ static {
+ if (supportsEc) {
+ HOSTKEY_ALGS.add("ecdsa-sha2-nistp256");
+ HOSTKEY_ALGS.add("ecdsa-sha2-nistp384");
+ HOSTKEY_ALGS.add("ecdsa-sha2-nistp521");
+ }
+ HOSTKEY_ALGS.add("ssh-rsa");
+ HOSTKEY_ALGS.add("ssh-dss");
+ }
+
+ private static final Set<String> KEX_ALGS = new LinkedHashSet<String>();
+ static {
+ if (supportsEc) {
+ KEX_ALGS.add("ecdh-sha2-nistp256");
+ KEX_ALGS.add("ecdh-sha2-nistp384");
+ KEX_ALGS.add("ecdh-sha2-nistp521");
+ }
+ KEX_ALGS.add("diffie-hellman-group-exchange-sha256");
+ KEX_ALGS.add("diffie-hellman-group-exchange-sha1");
+ KEX_ALGS.add("diffie-hellman-group14-sha1");
+ KEX_ALGS.add("diffie-hellman-group1-sha1");
+ }
+
+ KexState kxs;
+ int kexCount = 0;
+ KeyMaterial km;
+ byte[] sessionId;
+ ClientServerHello csh;
+
+ final Object accessLock = new Object();
+ ConnectionInfo lastConnInfo = null;
+
+ boolean connectionClosed = false;
+
+ boolean ignore_next_kex_packet = false;
+
+ final TransportManager tm;
+
+ CryptoWishList nextKEXcryptoWishList;
+ DHGexParameters nextKEXdhgexParameters;
+
+ ServerHostKeyVerifier verifier;
+ final String hostname;
+ final int port;
+ final SecureRandom rnd;
+
+ public KexManager(TransportManager tm, ClientServerHello csh, CryptoWishList initialCwl, String hostname, int port,
+ ServerHostKeyVerifier keyVerifier, SecureRandom rnd)
+ {
+ this.tm = tm;
+ this.csh = csh;
+ this.nextKEXcryptoWishList = initialCwl;
+ this.nextKEXdhgexParameters = new DHGexParameters();
+ this.hostname = hostname;
+ this.port = port;
+ this.verifier = keyVerifier;
+ this.rnd = rnd;
+ }
+
+ public ConnectionInfo getOrWaitForConnectionInfo(int minKexCount) throws IOException
+ {
+ synchronized (accessLock)
+ {
+ while (true)
+ {
+ if ((lastConnInfo != null) && (lastConnInfo.keyExchangeCounter >= minKexCount))
+ return lastConnInfo;
+
+ if (connectionClosed)
+ throw (IOException) new IOException("Key exchange was not finished, connection is closed.")
+ .initCause(tm.getReasonClosedCause());
+
+ try
+ {
+ accessLock.wait();
+ }
+ catch (InterruptedException e)
+ {
+ }
+ }
+ }
+ }
+
+ private String getFirstMatch(String[] client, String[] server) throws NegotiateException
+ {
+ if (client == null || server == null)
+ throw new IllegalArgumentException();
+
+ if (client.length == 0)
+ return null;
+
+ for (int i = 0; i < client.length; i++)
+ {
+ for (int j = 0; j < server.length; j++)
+ {
+ if (client[i].equals(server[j]))
+ return client[i];
+ }
+ }
+ throw new NegotiateException();
+ }
+
+ private boolean compareFirstOfNameList(String[] a, String[] b)
+ {
+ if (a == null || b == null)
+ throw new IllegalArgumentException();
+
+ if ((a.length == 0) && (b.length == 0))
+ return true;
+
+ if ((a.length == 0) || (b.length == 0))
+ return false;
+
+ return (a[0].equals(b[0]));
+ }
+
+ private boolean isGuessOK(KexParameters cpar, KexParameters spar)
+ {
+ if (cpar == null || spar == null)
+ throw new IllegalArgumentException();
+
+ if (compareFirstOfNameList(cpar.kex_algorithms, spar.kex_algorithms) == false)
+ {
+ return false;
+ }
+
+ if (compareFirstOfNameList(cpar.server_host_key_algorithms, spar.server_host_key_algorithms) == false)
+ {
+ return false;
+ }
+
+ /*
+ * We do NOT check here if the other algorithms can be agreed on, this
+ * is just a check if kex_algorithms and server_host_key_algorithms were
+ * guessed right!
+ */
+
+ return true;
+ }
+
+ private NegotiatedParameters mergeKexParameters(KexParameters client, KexParameters server)
+ {
+ NegotiatedParameters np = new NegotiatedParameters();
+
+ try
+ {
+ np.kex_algo = getFirstMatch(client.kex_algorithms, server.kex_algorithms);
+
+ log.log(20, "kex_algo=" + np.kex_algo);
+
+ np.server_host_key_algo = getFirstMatch(client.server_host_key_algorithms,
+ server.server_host_key_algorithms);
+
+ log.log(20, "server_host_key_algo=" + np.server_host_key_algo);
+
+ np.enc_algo_client_to_server = getFirstMatch(client.encryption_algorithms_client_to_server,
+ server.encryption_algorithms_client_to_server);
+ np.enc_algo_server_to_client = getFirstMatch(client.encryption_algorithms_server_to_client,
+ server.encryption_algorithms_server_to_client);
+
+ log.log(20, "enc_algo_client_to_server=" + np.enc_algo_client_to_server);
+ log.log(20, "enc_algo_server_to_client=" + np.enc_algo_server_to_client);
+
+ np.mac_algo_client_to_server = getFirstMatch(client.mac_algorithms_client_to_server,
+ server.mac_algorithms_client_to_server);
+ np.mac_algo_server_to_client = getFirstMatch(client.mac_algorithms_server_to_client,
+ server.mac_algorithms_server_to_client);
+
+ log.log(20, "mac_algo_client_to_server=" + np.mac_algo_client_to_server);
+ log.log(20, "mac_algo_server_to_client=" + np.mac_algo_server_to_client);
+
+ np.comp_algo_client_to_server = getFirstMatch(client.compression_algorithms_client_to_server,
+ server.compression_algorithms_client_to_server);
+ np.comp_algo_server_to_client = getFirstMatch(client.compression_algorithms_server_to_client,
+ server.compression_algorithms_server_to_client);
+
+ log.log(20, "comp_algo_client_to_server=" + np.comp_algo_client_to_server);
+ log.log(20, "comp_algo_server_to_client=" + np.comp_algo_server_to_client);
+
+ }
+ catch (NegotiateException e)
+ {
+ return null;
+ }
+
+ try
+ {
+ np.lang_client_to_server = getFirstMatch(client.languages_client_to_server,
+ server.languages_client_to_server);
+ }
+ catch (NegotiateException e1)
+ {
+ np.lang_client_to_server = null;
+ }
+
+ try
+ {
+ np.lang_server_to_client = getFirstMatch(client.languages_server_to_client,
+ server.languages_server_to_client);
+ }
+ catch (NegotiateException e2)
+ {
+ np.lang_server_to_client = null;
+ }
+
+ if (isGuessOK(client, server))
+ np.guessOK = true;
+
+ return np;
+ }
+
+ public synchronized void initiateKEX(CryptoWishList cwl, DHGexParameters dhgex) throws IOException
+ {
+ nextKEXcryptoWishList = cwl;
+ nextKEXdhgexParameters = dhgex;
+
+ if (kxs == null)
+ {
+ kxs = new KexState();
+
+ kxs.dhgexParameters = nextKEXdhgexParameters;
+ PacketKexInit kp = new PacketKexInit(nextKEXcryptoWishList);
+ kxs.localKEX = kp;
+ tm.sendKexMessage(kp.getPayload());
+ }
+ }
+
+ private boolean establishKeyMaterial()
+ {
+ try
+ {
+ int mac_cs_key_len = MAC.getKeyLen(kxs.np.mac_algo_client_to_server);
+ int enc_cs_key_len = BlockCipherFactory.getKeySize(kxs.np.enc_algo_client_to_server);
+ int enc_cs_block_len = BlockCipherFactory.getBlockSize(kxs.np.enc_algo_client_to_server);
+
+ int mac_sc_key_len = MAC.getKeyLen(kxs.np.mac_algo_server_to_client);
+ int enc_sc_key_len = BlockCipherFactory.getKeySize(kxs.np.enc_algo_server_to_client);
+ int enc_sc_block_len = BlockCipherFactory.getBlockSize(kxs.np.enc_algo_server_to_client);
+
+ km = KeyMaterial.create(kxs.hashAlgo, kxs.H, kxs.K, sessionId, enc_cs_key_len, enc_cs_block_len, mac_cs_key_len,
+ enc_sc_key_len, enc_sc_block_len, mac_sc_key_len);
+ }
+ catch (IllegalArgumentException e)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ private void finishKex() throws IOException
+ {
+ if (sessionId == null)
+ sessionId = kxs.H;
+
+ establishKeyMaterial();
+
+ /* Tell the other side that we start using the new material */
+
+ PacketNewKeys ign = new PacketNewKeys();
+ tm.sendKexMessage(ign.getPayload());
+
+ BlockCipher cbc;
+ MAC mac;
+ ICompressor comp;
+
+ try
+ {
+ cbc = BlockCipherFactory.createCipher(kxs.np.enc_algo_client_to_server, true, km.enc_key_client_to_server,
+ km.initial_iv_client_to_server);
+
+ mac = new MAC(kxs.np.mac_algo_client_to_server, km.integrity_key_client_to_server);
+
+ comp = CompressionFactory.createCompressor(kxs.np.comp_algo_client_to_server);
+
+ }
+ catch (IllegalArgumentException e1)
+ {
+ throw new IOException("Fatal error during MAC startup!");
+ }
+
+ tm.changeSendCipher(cbc, mac);
+ tm.changeSendCompression(comp);
+ tm.kexFinished();
+ }
+
+ public static final String[] getDefaultServerHostkeyAlgorithmList()
+ {
+ return HOSTKEY_ALGS.toArray(new String[HOSTKEY_ALGS.size()]);
+ }
+
+ public static final void checkServerHostkeyAlgorithmsList(String[] algos)
+ {
+ for (int i = 0; i < algos.length; i++)
+ {
+ if (!HOSTKEY_ALGS.contains(algos[i]))
+ throw new IllegalArgumentException("Unknown server host key algorithm '" + algos[i] + "'");
+ }
+ }
+
+ public static final String[] getDefaultKexAlgorithmList()
+ {
+ return KEX_ALGS.toArray(new String[KEX_ALGS.size()]);
+ }
+
+ public static final void checkKexAlgorithmList(String[] algos)
+ {
+ for (int i = 0; i < algos.length; i++)
+ {
+ if (!KEX_ALGS.contains(algos[i]))
+ throw new IllegalArgumentException("Unknown kex algorithm '" + algos[i] + "'");
+ }
+ }
+
+ private boolean verifySignature(byte[] sig, byte[] hostkey) throws IOException
+ {
+ if (kxs.np.server_host_key_algo.startsWith("ecdsa-sha2-"))
+ {
+ byte[] rs = ECDSASHA2Verify.decodeSSHECDSASignature(sig);
+ ECPublicKey epk = ECDSASHA2Verify.decodeSSHECDSAPublicKey(hostkey);
+
+ log.log(50, "Verifying ecdsa signature");
+
+ return ECDSASHA2Verify.verifySignature(kxs.H, rs, epk);
+ }
+
+ if (kxs.np.server_host_key_algo.equals("ssh-rsa"))
+ {
+ byte[] rs = RSASHA1Verify.decodeSSHRSASignature(sig);
+ RSAPublicKey rpk = RSASHA1Verify.decodeSSHRSAPublicKey(hostkey);
+
+ log.log(50, "Verifying ssh-rsa signature");
+
+ return RSASHA1Verify.verifySignature(kxs.H, rs, rpk);
+ }
+
+ if (kxs.np.server_host_key_algo.equals("ssh-dss"))
+ {
+ byte[] ds = DSASHA1Verify.decodeSSHDSASignature(sig);
+ DSAPublicKey dpk = DSASHA1Verify.decodeSSHDSAPublicKey(hostkey);
+
+ log.log(50, "Verifying ssh-dss signature");
+
+ return DSASHA1Verify.verifySignature(kxs.H, ds, dpk);
+ }
+
+ throw new IOException("Unknown server host key algorithm '" + kxs.np.server_host_key_algo + "'");
+ }
+
+ public synchronized void handleMessage(byte[] msg, int msglen) throws IOException
+ {
+ PacketKexInit kip;
+
+ if (msg == null)
+ {
+ synchronized (accessLock)
+ {
+ connectionClosed = true;
+ accessLock.notifyAll();
+ return;
+ }
+ }
+
+ if ((kxs == null) && (msg[0] != Packets.SSH_MSG_KEXINIT))
+ throw new IOException("Unexpected KEX message (type " + msg[0] + ")");
+
+ if (ignore_next_kex_packet)
+ {
+ ignore_next_kex_packet = false;
+ return;
+ }
+
+ if (msg[0] == Packets.SSH_MSG_KEXINIT)
+ {
+ if ((kxs != null) && (kxs.state != 0))
+ throw new IOException("Unexpected SSH_MSG_KEXINIT message during on-going kex exchange!");
+
+ if (kxs == null)
+ {
+ /*
+ * Ah, OK, peer wants to do KEX. Let's be nice and play
+ * together.
+ */
+ kxs = new KexState();
+ kxs.dhgexParameters = nextKEXdhgexParameters;
+ kip = new PacketKexInit(nextKEXcryptoWishList);
+ kxs.localKEX = kip;
+ tm.sendKexMessage(kip.getPayload());
+ }
+
+ kip = new PacketKexInit(msg, 0, msglen);
+ kxs.remoteKEX = kip;
+
+ kxs.np = mergeKexParameters(kxs.localKEX.getKexParameters(), kxs.remoteKEX.getKexParameters());
+
+ if (kxs.np == null)
+ throw new IOException("Cannot negotiate, proposals do not match.");
+
+ if (kxs.remoteKEX.isFirst_kex_packet_follows() && (kxs.np.guessOK == false))
+ {
+ /*
+ * Guess was wrong, we need to ignore the next kex packet.
+ */
+
+ ignore_next_kex_packet = true;
+ }
+
+ if (kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha1")
+ || kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha256"))
+ {
+ if (kxs.dhgexParameters.getMin_group_len() == 0 || csh.server_versioncomment.matches("OpenSSH_2\\.([0-4]\\.|5\\.[0-2]).*"))
+ {
+ PacketKexDhGexRequestOld dhgexreq = new PacketKexDhGexRequestOld(kxs.dhgexParameters);
+ tm.sendKexMessage(dhgexreq.getPayload());
+ }
+ else
+ {
+ PacketKexDhGexRequest dhgexreq = new PacketKexDhGexRequest(kxs.dhgexParameters);
+ tm.sendKexMessage(dhgexreq.getPayload());
+ }
+ if (kxs.np.kex_algo.endsWith("sha1")) {
+ kxs.hashAlgo = "SHA1";
+ } else {
+ kxs.hashAlgo = "SHA-256";
+ }
+ kxs.state = 1;
+ return;
+ }
+
+ if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")
+ || kxs.np.kex_algo.equals("diffie-hellman-group14-sha1")
+ || kxs.np.kex_algo.equals("ecdh-sha2-nistp256")
+ || kxs.np.kex_algo.equals("ecdh-sha2-nistp384")
+ || kxs.np.kex_algo.equals("ecdh-sha2-nistp521")) {
+ kxs.dhx = GenericDhExchange.getInstance(kxs.np.kex_algo);
+
+ kxs.dhx.init(kxs.np.kex_algo);
+ kxs.hashAlgo = kxs.dhx.getHashAlgo();
+
+ PacketKexDHInit kp = new PacketKexDHInit(kxs.dhx.getE());
+ tm.sendKexMessage(kp.getPayload());
+ kxs.state = 1;
+ return;
+ }
+
+ throw new IllegalStateException("Unknown KEX method!");
+ }
+
+ if (msg[0] == Packets.SSH_MSG_NEWKEYS)
+ {
+ if (km == null)
+ throw new IOException("Peer sent SSH_MSG_NEWKEYS, but I have no key material ready!");
+
+ BlockCipher cbc;
+ MAC mac;
+ ICompressor comp;
+
+ try
+ {
+ cbc = BlockCipherFactory.createCipher(kxs.np.enc_algo_server_to_client, false,
+ km.enc_key_server_to_client, km.initial_iv_server_to_client);
+
+ mac = new MAC(kxs.np.mac_algo_server_to_client, km.integrity_key_server_to_client);
+
+ comp = CompressionFactory.createCompressor(kxs.np.comp_algo_server_to_client);
+ }
+ catch (IllegalArgumentException e1)
+ {
+ throw new IOException("Fatal error during MAC startup!");
+ }
+
+ tm.changeRecvCipher(cbc, mac);
+ tm.changeRecvCompression(comp);
+
+ ConnectionInfo sci = new ConnectionInfo();
+
+ kexCount++;
+
+ sci.keyExchangeAlgorithm = kxs.np.kex_algo;
+ sci.keyExchangeCounter = kexCount;
+ sci.clientToServerCryptoAlgorithm = kxs.np.enc_algo_client_to_server;
+ sci.serverToClientCryptoAlgorithm = kxs.np.enc_algo_server_to_client;
+ sci.clientToServerMACAlgorithm = kxs.np.mac_algo_client_to_server;
+ sci.serverToClientMACAlgorithm = kxs.np.mac_algo_server_to_client;
+ sci.serverHostKeyAlgorithm = kxs.np.server_host_key_algo;
+ sci.serverHostKey = kxs.hostkey;
+
+ synchronized (accessLock)
+ {
+ lastConnInfo = sci;
+ accessLock.notifyAll();
+ }
+
+ kxs = null;
+ return;
+ }
+
+ if ((kxs == null) || (kxs.state == 0))
+ throw new IOException("Unexpected Kex submessage!");
+
+ if (kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha1")
+ || kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha256"))
+ {
+ if (kxs.state == 1)
+ {
+ PacketKexDhGexGroup dhgexgrp = new PacketKexDhGexGroup(msg, 0, msglen);
+ kxs.dhgx = new DhGroupExchange(dhgexgrp.getP(), dhgexgrp.getG());
+ kxs.dhgx.init(rnd);
+ PacketKexDhGexInit dhgexinit = new PacketKexDhGexInit(kxs.dhgx.getE());
+ tm.sendKexMessage(dhgexinit.getPayload());
+ kxs.state = 2;
+ return;
+ }
+
+ if (kxs.state == 2)
+ {
+ PacketKexDhGexReply dhgexrpl = new PacketKexDhGexReply(msg, 0, msglen);
+
+ kxs.hostkey = dhgexrpl.getHostKey();
+
+ if (verifier != null)
+ {
+ boolean vres = false;
+
+ try
+ {
+ vres = verifier.verifyServerHostKey(hostname, port, kxs.np.server_host_key_algo, kxs.hostkey);
+ }
+ catch (Exception e)
+ {
+ throw (IOException) new IOException(
+ "The server hostkey was not accepted by the verifier callback.").initCause(e);
+ }
+
+ if (vres == false)
+ throw new IOException("The server hostkey was not accepted by the verifier callback");
+ }
+
+ kxs.dhgx.setF(dhgexrpl.getF());
+
+ try
+ {
+ kxs.H = kxs.dhgx.calculateH(kxs.hashAlgo,
+ csh.getClientString(), csh.getServerString(),
+ kxs.localKEX.getPayload(), kxs.remoteKEX.getPayload(),
+ dhgexrpl.getHostKey(), kxs.dhgexParameters);
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw (IOException) new IOException("KEX error.").initCause(e);
+ }
+
+ boolean res = verifySignature(dhgexrpl.getSignature(), kxs.hostkey);
+
+ if (res == false)
+ throw new IOException("Hostkey signature sent by remote is wrong!");
+
+ kxs.K = kxs.dhgx.getK();
+
+ finishKex();
+ kxs.state = -1;
+ return;
+ }
+
+ throw new IllegalStateException("Illegal State in KEX Exchange!");
+ }
+
+ if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")
+ || kxs.np.kex_algo.equals("diffie-hellman-group14-sha1")
+ || kxs.np.kex_algo.equals("ecdh-sha2-nistp256")
+ || kxs.np.kex_algo.equals("ecdh-sha2-nistp384")
+ || kxs.np.kex_algo.equals("ecdh-sha2-nistp521"))
+ {
+ if (kxs.state == 1)
+ {
+
+ PacketKexDHReply dhr = new PacketKexDHReply(msg, 0, msglen);
+
+ kxs.hostkey = dhr.getHostKey();
+
+ if (verifier != null)
+ {
+ boolean vres = false;
+
+ try
+ {
+ vres = verifier.verifyServerHostKey(hostname, port, kxs.np.server_host_key_algo, kxs.hostkey);
+ }
+ catch (Exception e)
+ {
+ throw (IOException) new IOException(
+ "The server hostkey was not accepted by the verifier callback.").initCause(e);
+ }
+
+ if (vres == false)
+ throw new IOException("The server hostkey was not accepted by the verifier callback");
+ }
+
+ kxs.dhx.setF(dhr.getF());
+
+ try
+ {
+ kxs.H = kxs.dhx.calculateH(csh.getClientString(), csh.getServerString(), kxs.localKEX.getPayload(),
+ kxs.remoteKEX.getPayload(), dhr.getHostKey());
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw (IOException) new IOException("KEX error.").initCause(e);
+ }
+
+ boolean res = verifySignature(dhr.getSignature(), kxs.hostkey);
+
+ if (res == false)
+ throw new IOException("Hostkey signature sent by remote is wrong!");
+
+ kxs.K = kxs.dhx.getK();
+
+ finishKex();
+ kxs.state = -1;
+ return;
+ }
+ }
+
+ throw new IllegalStateException("Unkown KEX method! (" + kxs.np.kex_algo + ")");
+ }
+}
diff --git a/src/com/trilead/ssh2/transport/KexParameters.java b/src/com/trilead/ssh2/transport/KexParameters.java
index 6e7e08f..70bcf3e 100644
--- a/src/com/trilead/ssh2/transport/KexParameters.java
+++ b/src/com/trilead/ssh2/transport/KexParameters.java
@@ -1,24 +1,24 @@
-package com.trilead.ssh2.transport;
-
-/**
- * KexParameters.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: KexParameters.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public class KexParameters
-{
- public byte[] cookie;
- public String[] kex_algorithms;
- public String[] server_host_key_algorithms;
- public String[] encryption_algorithms_client_to_server;
- public String[] encryption_algorithms_server_to_client;
- public String[] mac_algorithms_client_to_server;
- public String[] mac_algorithms_server_to_client;
- public String[] compression_algorithms_client_to_server;
- public String[] compression_algorithms_server_to_client;
- public String[] languages_client_to_server;
- public String[] languages_server_to_client;
- public boolean first_kex_packet_follows;
- public int reserved_field1;
-}
+package com.trilead.ssh2.transport;
+
+/**
+ * KexParameters.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: KexParameters.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+public class KexParameters
+{
+ public byte[] cookie;
+ public String[] kex_algorithms;
+ public String[] server_host_key_algorithms;
+ public String[] encryption_algorithms_client_to_server;
+ public String[] encryption_algorithms_server_to_client;
+ public String[] mac_algorithms_client_to_server;
+ public String[] mac_algorithms_server_to_client;
+ public String[] compression_algorithms_client_to_server;
+ public String[] compression_algorithms_server_to_client;
+ public String[] languages_client_to_server;
+ public String[] languages_server_to_client;
+ public boolean first_kex_packet_follows;
+ public int reserved_field1;
+}
diff --git a/src/com/trilead/ssh2/transport/KexState.java b/src/com/trilead/ssh2/transport/KexState.java
index dabf450..8611f3f 100644
--- a/src/com/trilead/ssh2/transport/KexState.java
+++ b/src/com/trilead/ssh2/transport/KexState.java
@@ -1,32 +1,33 @@
-package com.trilead.ssh2.transport;
-
-
-import java.math.BigInteger;
-
-import com.trilead.ssh2.DHGexParameters;
-import com.trilead.ssh2.crypto.dh.DhExchange;
-import com.trilead.ssh2.crypto.dh.DhGroupExchange;
-import com.trilead.ssh2.packets.PacketKexInit;
-
-/**
- * KexState.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: KexState.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public class KexState
-{
- public PacketKexInit localKEX;
- public PacketKexInit remoteKEX;
- public NegotiatedParameters np;
- public int state = 0;
-
- public BigInteger K;
- public byte[] H;
-
- public byte[] hostkey;
-
- public DhExchange dhx;
- public DhGroupExchange dhgx;
- public DHGexParameters dhgexParameters;
-}
+package com.trilead.ssh2.transport;
+
+
+import java.math.BigInteger;
+
+import com.trilead.ssh2.DHGexParameters;
+import com.trilead.ssh2.crypto.dh.DhGroupExchange;
+import com.trilead.ssh2.crypto.dh.GenericDhExchange;
+import com.trilead.ssh2.packets.PacketKexInit;
+
+/**
+ * KexState.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: KexState.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
+ */
+public class KexState
+{
+ public PacketKexInit localKEX;
+ public PacketKexInit remoteKEX;
+ public NegotiatedParameters np;
+ public int state = 0;
+
+ public BigInteger K;
+ public byte[] H;
+
+ public byte[] hostkey;
+
+ public String hashAlgo;
+ public GenericDhExchange dhx;
+ public DhGroupExchange dhgx;
+ public DHGexParameters dhgexParameters;
+}
diff --git a/src/com/trilead/ssh2/transport/MessageHandler.java b/src/com/trilead/ssh2/transport/MessageHandler.java
index 1f173e8..039d473 100644
--- a/src/com/trilead/ssh2/transport/MessageHandler.java
+++ b/src/com/trilead/ssh2/transport/MessageHandler.java
@@ -1,14 +1,14 @@
-package com.trilead.ssh2.transport;
-
-import java.io.IOException;
-
-/**
- * MessageHandler.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: MessageHandler.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public interface MessageHandler
-{
- public void handleMessage(byte[] msg, int msglen) throws IOException;
-}
+package com.trilead.ssh2.transport;
+
+import java.io.IOException;
+
+/**
+ * MessageHandler.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: MessageHandler.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+public interface MessageHandler
+{
+ public void handleMessage(byte[] msg, int msglen) throws IOException;
+}
diff --git a/src/com/trilead/ssh2/transport/NegotiateException.java b/src/com/trilead/ssh2/transport/NegotiateException.java
index 8562a65..ff53097 100644
--- a/src/com/trilead/ssh2/transport/NegotiateException.java
+++ b/src/com/trilead/ssh2/transport/NegotiateException.java
@@ -1,12 +1,12 @@
-package com.trilead.ssh2.transport;
-
-/**
- * NegotiateException.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: NegotiateException.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public class NegotiateException extends Exception
-{
- private static final long serialVersionUID = 3689910669428143157L;
-}
+package com.trilead.ssh2.transport;
+
+/**
+ * NegotiateException.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: NegotiateException.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+public class NegotiateException extends Exception
+{
+ private static final long serialVersionUID = 3689910669428143157L;
+}
diff --git a/src/com/trilead/ssh2/transport/NegotiatedParameters.java b/src/com/trilead/ssh2/transport/NegotiatedParameters.java
index 6f035c0..e9f3a0a 100644
--- a/src/com/trilead/ssh2/transport/NegotiatedParameters.java
+++ b/src/com/trilead/ssh2/transport/NegotiatedParameters.java
@@ -1,22 +1,22 @@
-package com.trilead.ssh2.transport;
-
-/**
- * NegotiatedParameters.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: NegotiatedParameters.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public class NegotiatedParameters
-{
- public boolean guessOK;
- public String kex_algo;
- public String server_host_key_algo;
- public String enc_algo_client_to_server;
- public String enc_algo_server_to_client;
- public String mac_algo_client_to_server;
- public String mac_algo_server_to_client;
- public String comp_algo_client_to_server;
- public String comp_algo_server_to_client;
- public String lang_client_to_server;
- public String lang_server_to_client;
-}
+package com.trilead.ssh2.transport;
+
+/**
+ * NegotiatedParameters.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: NegotiatedParameters.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
+ */
+public class NegotiatedParameters
+{
+ public boolean guessOK;
+ public String kex_algo;
+ public String server_host_key_algo;
+ public String enc_algo_client_to_server;
+ public String enc_algo_server_to_client;
+ public String mac_algo_client_to_server;
+ public String mac_algo_server_to_client;
+ public String comp_algo_client_to_server;
+ public String comp_algo_server_to_client;
+ public String lang_client_to_server;
+ public String lang_server_to_client;
+}
diff --git a/src/com/trilead/ssh2/transport/TransportConnection.java b/src/com/trilead/ssh2/transport/TransportConnection.java
index 77eaded..906c3c9 100644
--- a/src/com/trilead/ssh2/transport/TransportConnection.java
+++ b/src/com/trilead/ssh2/transport/TransportConnection.java
@@ -1,336 +1,343 @@
-
-package com.trilead.ssh2.transport;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.security.SecureRandom;
-
-import com.trilead.ssh2.compression.ICompressor;
-import com.trilead.ssh2.crypto.cipher.BlockCipher;
-import com.trilead.ssh2.crypto.cipher.CipherInputStream;
-import com.trilead.ssh2.crypto.cipher.CipherOutputStream;
-import com.trilead.ssh2.crypto.cipher.NullCipher;
-import com.trilead.ssh2.crypto.digest.MAC;
-import com.trilead.ssh2.log.Logger;
-import com.trilead.ssh2.packets.Packets;
-
-
-/**
- * TransportConnection.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: TransportConnection.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
- */
-public class TransportConnection
-{
- private static final Logger log = Logger.getLogger(TransportConnection.class);
-
- int send_seq_number = 0;
-
- int recv_seq_number = 0;
-
- CipherInputStream cis;
-
- CipherOutputStream cos;
-
- boolean useRandomPadding = false;
-
- /* Depends on current MAC and CIPHER */
-
- MAC send_mac;
-
- byte[] send_mac_buffer;
-
- int send_padd_blocksize = 8;
-
- MAC recv_mac;
-
- byte[] recv_mac_buffer;
-
- byte[] recv_mac_buffer_cmp;
-
- int recv_padd_blocksize = 8;
-
- ICompressor recv_comp = null;
-
- ICompressor send_comp = null;
-
- boolean can_compress = false;
-
- byte[] recv_comp_buffer;
-
- byte[] send_comp_buffer;
-
- /* won't change */
-
- final byte[] send_padding_buffer = new byte[256];
-
- final byte[] send_packet_header_buffer = new byte[5];
-
- final byte[] recv_padding_buffer = new byte[256];
-
- final byte[] recv_packet_header_buffer = new byte[5];
-
- boolean recv_packet_header_present = false;
-
- ClientServerHello csh;
-
- final SecureRandom rnd;
-
- public TransportConnection(InputStream is, OutputStream os, SecureRandom rnd)
- {
- this.cis = new CipherInputStream(new NullCipher(), is);
- this.cos = new CipherOutputStream(new NullCipher(), os);
- this.rnd = rnd;
- }
-
- public void changeRecvCipher(BlockCipher bc, MAC mac)
- {
- cis.changeCipher(bc);
- recv_mac = mac;
- recv_mac_buffer = (mac != null) ? new byte[mac.size()] : null;
- recv_mac_buffer_cmp = (mac != null) ? new byte[mac.size()] : null;
- recv_padd_blocksize = bc.getBlockSize();
- if (recv_padd_blocksize < 8)
- recv_padd_blocksize = 8;
- }
-
- public void changeSendCipher(BlockCipher bc, MAC mac)
- {
- if ((bc instanceof NullCipher) == false)
- {
- /* Only use zero byte padding for the first few packets */
- useRandomPadding = true;
- /* Once we start encrypting, there is no way back */
- }
-
- cos.changeCipher(bc);
- send_mac = mac;
- send_mac_buffer = (mac != null) ? new byte[mac.size()] : null;
- send_padd_blocksize = bc.getBlockSize();
- if (send_padd_blocksize < 8)
- send_padd_blocksize = 8;
- }
-
- public void changeRecvCompression(ICompressor comp)
- {
- recv_comp = comp;
-
- if (comp != null)
- recv_comp_buffer = new byte[comp.getBufferSize()];
- }
-
- public void changeSendCompression(ICompressor comp)
- {
- send_comp = comp;
-
- if (comp != null)
- send_comp_buffer = new byte[comp.getBufferSize()];
- }
-
- public void sendMessage(byte[] message) throws IOException
- {
- sendMessage(message, 0, message.length, 0);
- }
-
- public void sendMessage(byte[] message, int off, int len) throws IOException
- {
- sendMessage(message, off, len, 0);
- }
-
- public int getPacketOverheadEstimate()
- {
- // return an estimate for the paket overhead (for send operations)
- return 5 + 4 + (send_padd_blocksize - 1) + send_mac_buffer.length;
- }
-
- public void sendMessage(byte[] message, int off, int len, int padd) throws IOException
- {
- if (padd < 4)
- padd = 4;
- else if (padd > 64)
- padd = 64;
-
- if (send_comp != null && can_compress) {
- if (send_comp_buffer.length < message.length + 1024)
- send_comp_buffer = new byte[message.length + 1024];
- len = send_comp.compress(message, off, len, send_comp_buffer);
- message = send_comp_buffer;
- }
-
- int packet_len = 5 + len + padd; /* Minimum allowed padding is 4 */
-
- int slack = packet_len % send_padd_blocksize;
-
- if (slack != 0)
- {
- packet_len += (send_padd_blocksize - slack);
- }
-
- if (packet_len < 16)
- packet_len = 16;
-
- int padd_len = packet_len - (5 + len);
-
- if (useRandomPadding)
- {
- for (int i = 0; i < padd_len; i = i + 4)
- {
- /*
- * don't waste calls to rnd.nextInt() (by using only 8bit of the
- * output). just believe me: even though we may write here up to 3
- * bytes which won't be used, there is no "buffer overflow" (i.e.,
- * arrayindexoutofbounds). the padding buffer is big enough =) (256
- * bytes, and that is bigger than any current cipher block size + 64).
- */
-
- int r = rnd.nextInt();
- send_padding_buffer[i] = (byte) r;
- send_padding_buffer[i + 1] = (byte) (r >> 8);
- send_padding_buffer[i + 2] = (byte) (r >> 16);
- send_padding_buffer[i + 3] = (byte) (r >> 24);
- }
- }
- else
- {
- /* use zero padding for unencrypted traffic */
- for (int i = 0; i < padd_len; i++)
- send_padding_buffer[i] = 0;
- /* Actually this code is paranoid: we never filled any
- * bytes into the padding buffer so far, therefore it should
- * consist of zeros only.
- */
- }
-
- send_packet_header_buffer[0] = (byte) ((packet_len - 4) >> 24);
- send_packet_header_buffer[1] = (byte) ((packet_len - 4) >> 16);
- send_packet_header_buffer[2] = (byte) ((packet_len - 4) >> 8);
- send_packet_header_buffer[3] = (byte) ((packet_len - 4));
- send_packet_header_buffer[4] = (byte) padd_len;
-
- cos.write(send_packet_header_buffer, 0, 5);
- cos.write(message, off, len);
- cos.write(send_padding_buffer, 0, padd_len);
-
- if (send_mac != null)
- {
- send_mac.initMac(send_seq_number);
- send_mac.update(send_packet_header_buffer, 0, 5);
- send_mac.update(message, off, len);
- send_mac.update(send_padding_buffer, 0, padd_len);
-
- send_mac.getMac(send_mac_buffer, 0);
- cos.writePlain(send_mac_buffer, 0, send_mac_buffer.length);
- }
-
- cos.flush();
-
- if (log.isEnabled())
- {
- log.log(90, "Sent " + Packets.getMessageName(message[off] & 0xff) + " " + len + " bytes payload");
- }
-
- send_seq_number++;
- }
-
- public int peekNextMessageLength() throws IOException
- {
- if (recv_packet_header_present == false)
- {
- cis.read(recv_packet_header_buffer, 0, 5);
- recv_packet_header_present = true;
- }
-
- int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24)
- | ((recv_packet_header_buffer[1] & 0xff) << 16) | ((recv_packet_header_buffer[2] & 0xff) << 8)
- | ((recv_packet_header_buffer[3] & 0xff));
-
- int padding_length = recv_packet_header_buffer[4] & 0xff;
-
- if (packet_length > 35000 || packet_length < 12)
- throw new IOException("Illegal packet size! (" + packet_length + ")");
-
- int payload_length = packet_length - padding_length - 1;
-
- if (payload_length < 0)
- throw new IOException("Illegal padding_length in packet from remote (" + padding_length + ")");
-
- return payload_length;
- }
-
- public int receiveMessage(byte buffer[], int off, int len) throws IOException
- {
- if (recv_packet_header_present == false)
- {
- cis.read(recv_packet_header_buffer, 0, 5);
- }
- else
- recv_packet_header_present = false;
-
- int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24)
- | ((recv_packet_header_buffer[1] & 0xff) << 16) | ((recv_packet_header_buffer[2] & 0xff) << 8)
- | ((recv_packet_header_buffer[3] & 0xff));
-
- int padding_length = recv_packet_header_buffer[4] & 0xff;
-
- if (packet_length > 35000 || packet_length < 12)
- throw new IOException("Illegal packet size! (" + packet_length + ")");
-
- int payload_length = packet_length - padding_length - 1;
-
- if (payload_length < 0)
- throw new IOException("Illegal padding_length in packet from remote (" + padding_length + ")");
-
- if (payload_length >= len)
- throw new IOException("Receive buffer too small (" + len + ", need " + payload_length + ")");
-
- cis.read(buffer, off, payload_length);
- cis.read(recv_padding_buffer, 0, padding_length);
-
- if (recv_mac != null)
- {
- cis.readPlain(recv_mac_buffer, 0, recv_mac_buffer.length);
-
- recv_mac.initMac(recv_seq_number);
- recv_mac.update(recv_packet_header_buffer, 0, 5);
- recv_mac.update(buffer, off, payload_length);
- recv_mac.update(recv_padding_buffer, 0, padding_length);
- recv_mac.getMac(recv_mac_buffer_cmp, 0);
-
- for (int i = 0; i < recv_mac_buffer.length; i++)
- {
- if (recv_mac_buffer[i] != recv_mac_buffer_cmp[i])
- throw new IOException("Remote sent corrupt MAC.");
- }
- }
-
- recv_seq_number++;
-
- if (log.isEnabled())
- {
- log.log(90, "Received " + Packets.getMessageName(buffer[off] & 0xff) + " " + payload_length
- + " bytes payload");
- }
-
- if (recv_comp != null && can_compress) {
- int[] uncomp_len = new int[] { payload_length };
- buffer = recv_comp.uncompress(buffer, off, uncomp_len);
-
- if (buffer == null) {
- throw new IOException("Error while inflating remote data");
- } else {
- return uncomp_len[0];
- }
- } else {
- return payload_length;
- }
- }
-
- /**
- *
- */
- public void startCompression() {
- can_compress = true;
- }
-}
+
+package com.trilead.ssh2.transport;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.SecureRandom;
+
+import com.trilead.ssh2.compression.ICompressor;
+import com.trilead.ssh2.crypto.cipher.BlockCipher;
+import com.trilead.ssh2.crypto.cipher.CipherInputStream;
+import com.trilead.ssh2.crypto.cipher.CipherOutputStream;
+import com.trilead.ssh2.crypto.cipher.NullCipher;
+import com.trilead.ssh2.crypto.digest.MAC;
+import com.trilead.ssh2.log.Logger;
+import com.trilead.ssh2.packets.Packets;
+
+
+/**
+ * TransportConnection.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: TransportConnection.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
+ */
+public class TransportConnection
+{
+ private static final Logger log = Logger.getLogger(TransportConnection.class);
+
+ int send_seq_number = 0;
+
+ int recv_seq_number = 0;
+
+ CipherInputStream cis;
+
+ CipherOutputStream cos;
+
+ boolean useRandomPadding = false;
+
+ /* Depends on current MAC and CIPHER */
+
+ MAC send_mac;
+
+ byte[] send_mac_buffer;
+
+ int send_padd_blocksize = 8;
+
+ MAC recv_mac;
+
+ byte[] recv_mac_buffer;
+
+ byte[] recv_mac_buffer_cmp;
+
+ int recv_padd_blocksize = 8;
+
+ ICompressor recv_comp = null;
+
+ ICompressor send_comp = null;
+
+ boolean can_recv_compress = false;
+
+ boolean can_send_compress = false;
+
+ byte[] recv_comp_buffer;
+
+ byte[] send_comp_buffer;
+
+ /* won't change */
+
+ final byte[] send_padding_buffer = new byte[256];
+
+ final byte[] send_packet_header_buffer = new byte[5];
+
+ final byte[] recv_padding_buffer = new byte[256];
+
+ final byte[] recv_packet_header_buffer = new byte[5];
+
+ boolean recv_packet_header_present = false;
+
+ ClientServerHello csh;
+
+ final SecureRandom rnd;
+
+ public TransportConnection(InputStream is, OutputStream os, SecureRandom rnd)
+ {
+ this.cis = new CipherInputStream(new NullCipher(), is);
+ this.cos = new CipherOutputStream(new NullCipher(), os);
+ this.rnd = rnd;
+ }
+
+ public void changeRecvCipher(BlockCipher bc, MAC mac)
+ {
+ cis.changeCipher(bc);
+ recv_mac = mac;
+ recv_mac_buffer = (mac != null) ? new byte[mac.size()] : null;
+ recv_mac_buffer_cmp = (mac != null) ? new byte[mac.size()] : null;
+ recv_padd_blocksize = bc.getBlockSize();
+ if (recv_padd_blocksize < 8)
+ recv_padd_blocksize = 8;
+ }
+
+ public void changeSendCipher(BlockCipher bc, MAC mac)
+ {
+ if ((bc instanceof NullCipher) == false)
+ {
+ /* Only use zero byte padding for the first few packets */
+ useRandomPadding = true;
+ /* Once we start encrypting, there is no way back */
+ }
+
+ cos.changeCipher(bc);
+ send_mac = mac;
+ send_mac_buffer = (mac != null) ? new byte[mac.size()] : null;
+ send_padd_blocksize = bc.getBlockSize();
+ if (send_padd_blocksize < 8)
+ send_padd_blocksize = 8;
+ }
+
+ public void changeRecvCompression(ICompressor comp)
+ {
+ recv_comp = comp;
+
+ if (comp != null) {
+ recv_comp_buffer = new byte[comp.getBufferSize()];
+ can_recv_compress |= recv_comp.canCompressPreauth();
+ }
+ }
+
+ public void changeSendCompression(ICompressor comp)
+ {
+ send_comp = comp;
+
+ if (comp != null) {
+ send_comp_buffer = new byte[comp.getBufferSize()];
+ can_send_compress |= send_comp.canCompressPreauth();
+ }
+ }
+
+ public void sendMessage(byte[] message) throws IOException
+ {
+ sendMessage(message, 0, message.length, 0);
+ }
+
+ public void sendMessage(byte[] message, int off, int len) throws IOException
+ {
+ sendMessage(message, off, len, 0);
+ }
+
+ public int getPacketOverheadEstimate()
+ {
+ // return an estimate for the paket overhead (for send operations)
+ return 5 + 4 + (send_padd_blocksize - 1) + send_mac_buffer.length;
+ }
+
+ public void sendMessage(byte[] message, int off, int len, int padd) throws IOException
+ {
+ if (padd < 4)
+ padd = 4;
+ else if (padd > 64)
+ padd = 64;
+
+ if (send_comp != null && can_send_compress) {
+ if (send_comp_buffer.length < message.length + 1024)
+ send_comp_buffer = new byte[message.length + 1024];
+ len = send_comp.compress(message, off, len, send_comp_buffer);
+ message = send_comp_buffer;
+ }
+
+ int packet_len = 5 + len + padd; /* Minimum allowed padding is 4 */
+
+ int slack = packet_len % send_padd_blocksize;
+
+ if (slack != 0)
+ {
+ packet_len += (send_padd_blocksize - slack);
+ }
+
+ if (packet_len < 16)
+ packet_len = 16;
+
+ int padd_len = packet_len - (5 + len);
+
+ if (useRandomPadding)
+ {
+ for (int i = 0; i < padd_len; i = i + 4)
+ {
+ /*
+ * don't waste calls to rnd.nextInt() (by using only 8bit of the
+ * output). just believe me: even though we may write here up to 3
+ * bytes which won't be used, there is no "buffer overflow" (i.e.,
+ * arrayindexoutofbounds). the padding buffer is big enough =) (256
+ * bytes, and that is bigger than any current cipher block size + 64).
+ */
+
+ int r = rnd.nextInt();
+ send_padding_buffer[i] = (byte) r;
+ send_padding_buffer[i + 1] = (byte) (r >> 8);
+ send_padding_buffer[i + 2] = (byte) (r >> 16);
+ send_padding_buffer[i + 3] = (byte) (r >> 24);
+ }
+ }
+ else
+ {
+ /* use zero padding for unencrypted traffic */
+ for (int i = 0; i < padd_len; i++)
+ send_padding_buffer[i] = 0;
+ /* Actually this code is paranoid: we never filled any
+ * bytes into the padding buffer so far, therefore it should
+ * consist of zeros only.
+ */
+ }
+
+ send_packet_header_buffer[0] = (byte) ((packet_len - 4) >> 24);
+ send_packet_header_buffer[1] = (byte) ((packet_len - 4) >> 16);
+ send_packet_header_buffer[2] = (byte) ((packet_len - 4) >> 8);
+ send_packet_header_buffer[3] = (byte) ((packet_len - 4));
+ send_packet_header_buffer[4] = (byte) padd_len;
+
+ cos.write(send_packet_header_buffer, 0, 5);
+ cos.write(message, off, len);
+ cos.write(send_padding_buffer, 0, padd_len);
+
+ if (send_mac != null)
+ {
+ send_mac.initMac(send_seq_number);
+ send_mac.update(send_packet_header_buffer, 0, 5);
+ send_mac.update(message, off, len);
+ send_mac.update(send_padding_buffer, 0, padd_len);
+
+ send_mac.getMac(send_mac_buffer, 0);
+ cos.writePlain(send_mac_buffer, 0, send_mac_buffer.length);
+ }
+
+ cos.flush();
+
+ if (log.isEnabled())
+ {
+ log.log(90, "Sent " + Packets.getMessageName(message[off] & 0xff) + " " + len + " bytes payload");
+ }
+
+ send_seq_number++;
+ }
+
+ public int peekNextMessageLength() throws IOException
+ {
+ if (recv_packet_header_present == false)
+ {
+ cis.read(recv_packet_header_buffer, 0, 5);
+ recv_packet_header_present = true;
+ }
+
+ int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24)
+ | ((recv_packet_header_buffer[1] & 0xff) << 16) | ((recv_packet_header_buffer[2] & 0xff) << 8)
+ | ((recv_packet_header_buffer[3] & 0xff));
+
+ int padding_length = recv_packet_header_buffer[4] & 0xff;
+
+ if (packet_length > 35000 || packet_length < 12)
+ throw new IOException("Illegal packet size! (" + packet_length + ")");
+
+ int payload_length = packet_length - padding_length - 1;
+
+ if (payload_length < 0)
+ throw new IOException("Illegal padding_length in packet from remote (" + padding_length + ")");
+
+ return payload_length;
+ }
+
+ public int receiveMessage(byte buffer[], int off, int len) throws IOException
+ {
+ if (recv_packet_header_present == false)
+ {
+ cis.read(recv_packet_header_buffer, 0, 5);
+ }
+ else
+ recv_packet_header_present = false;
+
+ int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24)
+ | ((recv_packet_header_buffer[1] & 0xff) << 16) | ((recv_packet_header_buffer[2] & 0xff) << 8)
+ | ((recv_packet_header_buffer[3] & 0xff));
+
+ int padding_length = recv_packet_header_buffer[4] & 0xff;
+
+ if (packet_length > 35000 || packet_length < 12)
+ throw new IOException("Illegal packet size! (" + packet_length + ")");
+
+ int payload_length = packet_length - padding_length - 1;
+
+ if (payload_length < 0)
+ throw new IOException("Illegal padding_length in packet from remote (" + padding_length + ")");
+
+ if (payload_length >= len)
+ throw new IOException("Receive buffer too small (" + len + ", need " + payload_length + ")");
+
+ cis.read(buffer, off, payload_length);
+ cis.read(recv_padding_buffer, 0, padding_length);
+
+ if (recv_mac != null)
+ {
+ cis.readPlain(recv_mac_buffer, 0, recv_mac_buffer.length);
+
+ recv_mac.initMac(recv_seq_number);
+ recv_mac.update(recv_packet_header_buffer, 0, 5);
+ recv_mac.update(buffer, off, payload_length);
+ recv_mac.update(recv_padding_buffer, 0, padding_length);
+ recv_mac.getMac(recv_mac_buffer_cmp, 0);
+
+ for (int i = 0; i < recv_mac_buffer.length; i++)
+ {
+ if (recv_mac_buffer[i] != recv_mac_buffer_cmp[i])
+ throw new IOException("Remote sent corrupt MAC.");
+ }
+ }
+
+ recv_seq_number++;
+
+ if (log.isEnabled())
+ {
+ log.log(90, "Received " + Packets.getMessageName(buffer[off] & 0xff) + " " + payload_length
+ + " bytes payload");
+ }
+
+ if (recv_comp != null && can_recv_compress) {
+ int[] uncomp_len = new int[] { payload_length };
+ buffer = recv_comp.uncompress(buffer, off, uncomp_len);
+
+ if (buffer == null) {
+ throw new IOException("Error while inflating remote data");
+ } else {
+ return uncomp_len[0];
+ }
+ } else {
+ return payload_length;
+ }
+ }
+
+ /**
+ *
+ */
+ public void startCompression() {
+ can_recv_compress = true;
+ can_send_compress = true;
+ }
+}
diff --git a/src/com/trilead/ssh2/transport/TransportManager.java b/src/com/trilead/ssh2/transport/TransportManager.java
index c81dbdf..8f3406e 100644
--- a/src/com/trilead/ssh2/transport/TransportManager.java
+++ b/src/com/trilead/ssh2/transport/TransportManager.java
@@ -1,802 +1,750 @@
-
-package com.trilead.ssh2.transport;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-import java.net.UnknownHostException;
-import java.security.SecureRandom;
-import java.util.Vector;
-
-import com.trilead.ssh2.ConnectionInfo;
-import com.trilead.ssh2.ConnectionMonitor;
-import com.trilead.ssh2.DHGexParameters;
-import com.trilead.ssh2.HTTPProxyData;
-import com.trilead.ssh2.HTTPProxyException;
-import com.trilead.ssh2.ProxyData;
-import com.trilead.ssh2.ServerHostKeyVerifier;
-import com.trilead.ssh2.compression.ICompressor;
-import com.trilead.ssh2.crypto.Base64;
-import com.trilead.ssh2.crypto.CryptoWishList;
-import com.trilead.ssh2.crypto.cipher.BlockCipher;
-import com.trilead.ssh2.crypto.digest.MAC;
-import com.trilead.ssh2.log.Logger;
-import com.trilead.ssh2.packets.PacketDisconnect;
-import com.trilead.ssh2.packets.Packets;
-import com.trilead.ssh2.packets.TypesReader;
-import com.trilead.ssh2.util.Tokenizer;
-
-
-/*
- * Yes, the "standard" is a big mess. On one side, the say that arbitary channel
- * packets are allowed during kex exchange, on the other side we need to blindly
- * ignore the next _packet_ if the KEX guess was wrong. Where do we know from that
- * the next packet is not a channel data packet? Yes, we could check if it is in
- * the KEX range. But the standard says nothing about this. The OpenSSH guys
- * block local "normal" traffic during KEX. That's fine - however, they assume
- * that the other side is doing the same. During re-key, if they receive traffic
- * other than KEX, they become horribly irritated and kill the connection. Since
- * we are very likely going to communicate with OpenSSH servers, we have to play
- * the same game - even though we could do better.
- *
- * btw: having stdout and stderr on the same channel, with a shared window, is
- * also a VERY good idea... =(
- */
-
-/**
- * TransportManager.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: TransportManager.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
- */
-public class TransportManager
-{
- private static final Logger log = Logger.getLogger(TransportManager.class);
-
- class HandlerEntry
- {
- MessageHandler mh;
- int low;
- int high;
- }
-
- private final Vector<byte[]> asynchronousQueue = new Vector<byte[]>();
- private Thread asynchronousThread = null;
-
- class AsynchronousWorker extends Thread
- {
- public void run()
- {
- while (true)
- {
- byte[] msg = null;
-
- synchronized (asynchronousQueue)
- {
- if (asynchronousQueue.size() == 0)
- {
- /* After the queue is empty for about 2 seconds, stop this thread */
-
- try
- {
- asynchronousQueue.wait(2000);
- }
- catch (InterruptedException e)
- {
- /* OKOK, if somebody interrupts us, then we may die earlier. */
- }
-
- if (asynchronousQueue.size() == 0)
- {
- asynchronousThread = null;
- return;
- }
- }
-
- msg = asynchronousQueue.remove(0);
- }
-
- /* The following invocation may throw an IOException.
- * There is no point in handling it - it simply means
- * that the connection has a problem and we should stop
- * sending asynchronously messages. We do not need to signal that
- * we have exited (asynchronousThread = null): further
- * messages in the queue cannot be sent by this or any
- * other thread.
- * Other threads will sooner or later (when receiving or
- * sending the next message) get the same IOException and
- * get to the same conclusion.
- */
-
- try
- {
- sendMessage(msg);
- }
- catch (IOException e)
- {
- return;
- }
- }
- }
- }
-
- String hostname;
- int port;
- final Socket sock = new Socket();
-
- Object connectionSemaphore = new Object();
-
- boolean flagKexOngoing = false;
- boolean connectionClosed = false;
-
- Throwable reasonClosedCause = null;
-
- TransportConnection tc;
- KexManager km;
-
- Vector<HandlerEntry> messageHandlers = new Vector<HandlerEntry>();
-
- Thread receiveThread;
-
- Vector connectionMonitors = new Vector();
- boolean monitorsWereInformed = false;
-
- /**
- * There were reports that there are JDKs which use
- * the resolver even though one supplies a dotted IP
- * address in the Socket constructor. That is why we
- * try to generate the InetAdress "by hand".
- *
- * @param host
- * @return the InetAddress
- * @throws UnknownHostException
- */
- private InetAddress createInetAddress(String host) throws UnknownHostException
- {
- /* Check if it is a dotted IP4 address */
-
- InetAddress addr = parseIPv4Address(host);
-
- if (addr != null)
- return addr;
-
- return InetAddress.getByName(host);
- }
-
- private InetAddress parseIPv4Address(String host) throws UnknownHostException
- {
- if (host == null)
- return null;
-
- String[] quad = Tokenizer.parseTokens(host, '.');
-
- if ((quad == null) || (quad.length != 4))
- return null;
-
- byte[] addr = new byte[4];
-
- for (int i = 0; i < 4; i++)
- {
- int part = 0;
-
- if ((quad[i].length() == 0) || (quad[i].length() > 3))
- return null;
-
- for (int k = 0; k < quad[i].length(); k++)
- {
- char c = quad[i].charAt(k);
-
- /* No, Character.isDigit is not the same */
- if ((c < '0') || (c > '9'))
- return null;
-
- part = part * 10 + (c - '0');
- }
-
- if (part > 255) /* 300.1.2.3 is invalid =) */
- return null;
-
- addr[i] = (byte) part;
- }
-
- return InetAddress.getByAddress(host, addr);
- }
-
- public TransportManager(String host, int port) throws IOException
- {
- this.hostname = host;
- this.port = port;
- }
-
- public int getPacketOverheadEstimate()
- {
- return tc.getPacketOverheadEstimate();
- }
-
- public void setTcpNoDelay(boolean state) throws IOException
- {
- sock.setTcpNoDelay(state);
- }
-
- public void setSoTimeout(int timeout) throws IOException
- {
- sock.setSoTimeout(timeout);
- }
-
- public ConnectionInfo getConnectionInfo(int kexNumber) throws IOException
- {
- return km.getOrWaitForConnectionInfo(kexNumber);
- }
-
- public Throwable getReasonClosedCause()
- {
- synchronized (connectionSemaphore)
- {
- return reasonClosedCause;
- }
- }
-
- public byte[] getSessionIdentifier()
- {
- return km.sessionId;
- }
-
- public void close(Throwable cause, boolean useDisconnectPacket)
- {
- if (useDisconnectPacket == false)
- {
- /* OK, hard shutdown - do not aquire the semaphore,
- * perhaps somebody is inside (and waits until the remote
- * side is ready to accept new data). */
-
- try
- {
- sock.close();
- }
- catch (IOException ignore)
- {
- }
-
- /* OK, whoever tried to send data, should now agree that
- * there is no point in further waiting =)
- * It is safe now to aquire the semaphore.
- */
- }
-
- synchronized (connectionSemaphore)
- {
- if (connectionClosed == false)
- {
- if (useDisconnectPacket == true)
- {
- try
- {
- byte[] msg = new PacketDisconnect(Packets.SSH_DISCONNECT_BY_APPLICATION, cause.getMessage(), "")
- .getPayload();
- if (tc != null)
- tc.sendMessage(msg);
- }
- catch (IOException ignore)
- {
- }
-
- try
- {
- sock.close();
- }
- catch (IOException ignore)
- {
- }
- }
-
- connectionClosed = true;
- reasonClosedCause = cause; /* may be null */
- }
- connectionSemaphore.notifyAll();
- }
-
- /* No check if we need to inform the monitors */
-
- Vector monitors = null;
-
- synchronized (this)
- {
- /* Short term lock to protect "connectionMonitors"
- * and "monitorsWereInformed"
- * (they may be modified concurrently)
- */
-
- if (monitorsWereInformed == false)
- {
- monitorsWereInformed = true;
- monitors = (Vector) connectionMonitors.clone();
- }
- }
-
- if (monitors != null)
- {
- for (int i = 0; i < monitors.size(); i++)
- {
- try
- {
- ConnectionMonitor cmon = (ConnectionMonitor) monitors.elementAt(i);
- cmon.connectionLost(reasonClosedCause);
- }
- catch (Exception ignore)
- {
- }
- }
- }
- }
-
- private void establishConnection(ProxyData proxyData, int connectTimeout) throws IOException
- {
- /* See the comment for createInetAddress() */
-
- if (proxyData == null)
- {
- InetAddress addr = createInetAddress(hostname);
- sock.connect(new InetSocketAddress(addr, port), connectTimeout);
- sock.setSoTimeout(0);
- return;
- }
-
- if (proxyData instanceof HTTPProxyData)
- {
- HTTPProxyData pd = (HTTPProxyData) proxyData;
-
- /* At the moment, we only support HTTP proxies */
-
- InetAddress addr = createInetAddress(pd.proxyHost);
- sock.connect(new InetSocketAddress(addr, pd.proxyPort), connectTimeout);
- sock.setSoTimeout(0);
-
- /* OK, now tell the proxy where we actually want to connect to */
-
- StringBuffer sb = new StringBuffer();
-
- sb.append("CONNECT ");
- sb.append(hostname);
- sb.append(':');
- sb.append(port);
- sb.append(" HTTP/1.0\r\n");
-
- if ((pd.proxyUser != null) && (pd.proxyPass != null))
- {
- String credentials = pd.proxyUser + ":" + pd.proxyPass;
- char[] encoded = Base64.encode(credentials.getBytes("ISO-8859-1"));
- sb.append("Proxy-Authorization: Basic ");
- sb.append(encoded);
- sb.append("\r\n");
- }
-
- if (pd.requestHeaderLines != null)
- {
- for (int i = 0; i < pd.requestHeaderLines.length; i++)
- {
- if (pd.requestHeaderLines[i] != null)
- {
- sb.append(pd.requestHeaderLines[i]);
- sb.append("\r\n");
- }
- }
- }
-
- sb.append("\r\n");
-
- OutputStream out = sock.getOutputStream();
-
- out.write(sb.toString().getBytes("ISO-8859-1"));
- out.flush();
-
- /* Now parse the HTTP response */
-
- byte[] buffer = new byte[1024];
- InputStream in = sock.getInputStream();
-
- int len = ClientServerHello.readLineRN(in, buffer);
-
- String httpReponse = new String(buffer, 0, len, "ISO-8859-1");
-
- if (httpReponse.startsWith("HTTP/") == false)
- throw new IOException("The proxy did not send back a valid HTTP response.");
-
- /* "HTTP/1.X XYZ X" => 14 characters minimum */
-
- if ((httpReponse.length() < 14) || (httpReponse.charAt(8) != ' ') || (httpReponse.charAt(12) != ' '))
- throw new IOException("The proxy did not send back a valid HTTP response.");
-
- int errorCode = 0;
-
- try
- {
- errorCode = Integer.parseInt(httpReponse.substring(9, 12));
- }
- catch (NumberFormatException ignore)
- {
- throw new IOException("The proxy did not send back a valid HTTP response.");
- }
-
- if ((errorCode < 0) || (errorCode > 999))
- throw new IOException("The proxy did not send back a valid HTTP response.");
-
- if (errorCode != 200)
- {
- throw new HTTPProxyException(httpReponse.substring(13), errorCode);
- }
-
- /* OK, read until empty line */
-
- while (true)
- {
- len = ClientServerHello.readLineRN(in, buffer);
- if (len == 0)
- break;
- }
- return;
- }
-
- throw new IOException("Unsupported ProxyData");
- }
-
- public void initialize(CryptoWishList cwl, ServerHostKeyVerifier verifier, DHGexParameters dhgex,
- int connectTimeout, SecureRandom rnd, ProxyData proxyData) throws IOException
- {
- /* First, establish the TCP connection to the SSH-2 server */
-
- establishConnection(proxyData, connectTimeout);
-
- /* Parse the server line and say hello - important: this information is later needed for the
- * key exchange (to stop man-in-the-middle attacks) - that is why we wrap it into an object
- * for later use.
- */
-
- ClientServerHello csh = new ClientServerHello(sock.getInputStream(), sock.getOutputStream());
-
- tc = new TransportConnection(sock.getInputStream(), sock.getOutputStream(), rnd);
-
- km = new KexManager(this, csh, cwl, hostname, port, verifier, rnd);
- km.initiateKEX(cwl, dhgex);
-
- receiveThread = new Thread(new Runnable()
- {
- public void run()
- {
- try
- {
- receiveLoop();
- }
- catch (IOException e)
- {
- close(e, false);
-
- if (log.isEnabled())
- log.log(10, "Receive thread: error in receiveLoop: " + e.getMessage());
- }
-
- if (log.isEnabled())
- log.log(50, "Receive thread: back from receiveLoop");
-
- /* Tell all handlers that it is time to say goodbye */
-
- if (km != null)
- {
- try
- {
- km.handleMessage(null, 0);
- }
- catch (IOException e)
- {
- }
- }
-
- for (int i = 0; i < messageHandlers.size(); i++)
- {
- HandlerEntry he = messageHandlers.elementAt(i);
- try
- {
- he.mh.handleMessage(null, 0);
- }
- catch (Exception ignore)
- {
- }
- }
- }
- });
-
- receiveThread.setDaemon(true);
- receiveThread.start();
- }
-
- public void registerMessageHandler(MessageHandler mh, int low, int high)
- {
- HandlerEntry he = new HandlerEntry();
- he.mh = mh;
- he.low = low;
- he.high = high;
-
- synchronized (messageHandlers)
- {
- messageHandlers.addElement(he);
- }
- }
-
- public void removeMessageHandler(MessageHandler mh, int low, int high)
- {
- synchronized (messageHandlers)
- {
- for (int i = 0; i < messageHandlers.size(); i++)
- {
- HandlerEntry he = messageHandlers.elementAt(i);
- if ((he.mh == mh) && (he.low == low) && (he.high == high))
- {
- messageHandlers.removeElementAt(i);
- break;
- }
- }
- }
- }
-
- public void sendKexMessage(byte[] msg) throws IOException
- {
- synchronized (connectionSemaphore)
- {
- if (connectionClosed)
- {
- throw (IOException) new IOException("Sorry, this connection is closed.").initCause(reasonClosedCause);
- }
-
- flagKexOngoing = true;
-
- try
- {
- tc.sendMessage(msg);
- }
- catch (IOException e)
- {
- close(e, false);
- throw e;
- }
- }
- }
-
- public void kexFinished() throws IOException
- {
- synchronized (connectionSemaphore)
- {
- flagKexOngoing = false;
- connectionSemaphore.notifyAll();
- }
- }
-
- public void forceKeyExchange(CryptoWishList cwl, DHGexParameters dhgex) throws IOException
- {
- km.initiateKEX(cwl, dhgex);
- }
-
- public void changeRecvCipher(BlockCipher bc, MAC mac)
- {
- tc.changeRecvCipher(bc, mac);
- }
-
- public void changeSendCipher(BlockCipher bc, MAC mac)
- {
- tc.changeSendCipher(bc, mac);
- }
-
- /**
- * @param comp
- */
- public void changeRecvCompression(ICompressor comp) {
- tc.changeRecvCompression(comp);
- }
-
- /**
- * @param comp
- */
- public void changeSendCompression(ICompressor comp) {
- tc.changeSendCompression(comp);
- }
-
- /**
- *
- */
- public void startCompression() {
- tc.startCompression();
- }
-
- public void sendAsynchronousMessage(byte[] msg) throws IOException
- {
- synchronized (asynchronousQueue)
- {
- asynchronousQueue.addElement(msg);
-
- /* This limit should be flexible enough. We need this, otherwise the peer
- * can flood us with global requests (and other stuff where we have to reply
- * with an asynchronous message) and (if the server just sends data and does not
- * read what we send) this will probably put us in a low memory situation
- * (our send queue would grow and grow and...) */
-
- if (asynchronousQueue.size() > 100)
- throw new IOException("Error: the peer is not consuming our asynchronous replies.");
-
- /* Check if we have an asynchronous sending thread */
-
- if (asynchronousThread == null)
- {
- asynchronousThread = new AsynchronousWorker();
- asynchronousThread.setDaemon(true);
- asynchronousThread.start();
-
- /* The thread will stop after 2 seconds of inactivity (i.e., empty queue) */
- }
- }
- }
-
- public void setConnectionMonitors(Vector monitors)
- {
- synchronized (this)
- {
- connectionMonitors = (Vector) monitors.clone();
- }
- }
-
- public void sendMessage(byte[] msg) throws IOException
- {
- if (Thread.currentThread() == receiveThread)
- throw new IOException("Assertion error: sendMessage may never be invoked by the receiver thread!");
-
- synchronized (connectionSemaphore)
- {
- while (true)
- {
- if (connectionClosed)
- {
- throw (IOException) new IOException("Sorry, this connection is closed.")
- .initCause(reasonClosedCause);
- }
-
- if (flagKexOngoing == false)
- break;
-
- try
- {
- connectionSemaphore.wait();
- }
- catch (InterruptedException e)
- {
- }
- }
-
- try
- {
- tc.sendMessage(msg);
- }
- catch (IOException e)
- {
- close(e, false);
- throw e;
- }
- }
- }
-
- public void receiveLoop() throws IOException
- {
- byte[] msg = new byte[35000];
-
- while (true)
- {
- int msglen = tc.receiveMessage(msg, 0, msg.length);
-
- int type = msg[0] & 0xff;
-
- if (type == Packets.SSH_MSG_IGNORE)
- continue;
-
- if (type == Packets.SSH_MSG_DEBUG)
- {
- if (log.isEnabled())
- {
- TypesReader tr = new TypesReader(msg, 0, msglen);
- tr.readByte();
- tr.readBoolean();
- StringBuffer debugMessageBuffer = new StringBuffer();
- debugMessageBuffer.append(tr.readString("UTF-8"));
-
- for (int i = 0; i < debugMessageBuffer.length(); i++)
- {
- char c = debugMessageBuffer.charAt(i);
-
- if ((c >= 32) && (c <= 126))
- continue;
- debugMessageBuffer.setCharAt(i, '\uFFFD');
- }
-
- log.log(50, "DEBUG Message from remote: '" + debugMessageBuffer.toString() + "'");
- }
- continue;
- }
-
- if (type == Packets.SSH_MSG_UNIMPLEMENTED)
- {
- throw new IOException("Peer sent UNIMPLEMENTED message, that should not happen.");
- }
-
- if (type == Packets.SSH_MSG_DISCONNECT)
- {
- TypesReader tr = new TypesReader(msg, 0, msglen);
- tr.readByte();
- int reason_code = tr.readUINT32();
- StringBuffer reasonBuffer = new StringBuffer();
- reasonBuffer.append(tr.readString("UTF-8"));
-
- /*
- * Do not get fooled by servers that send abnormal long error
- * messages
- */
-
- if (reasonBuffer.length() > 255)
- {
- reasonBuffer.setLength(255);
- reasonBuffer.setCharAt(254, '.');
- reasonBuffer.setCharAt(253, '.');
- reasonBuffer.setCharAt(252, '.');
- }
-
- /*
- * Also, check that the server did not send charcaters that may
- * screw up the receiver -> restrict to reasonable US-ASCII
- * subset -> "printable characters" (ASCII 32 - 126). Replace
- * all others with 0xFFFD (UNICODE replacement character).
- */
-
- for (int i = 0; i < reasonBuffer.length(); i++)
- {
- char c = reasonBuffer.charAt(i);
-
- if ((c >= 32) && (c <= 126))
- continue;
- reasonBuffer.setCharAt(i, '\uFFFD');
- }
-
- throw new IOException("Peer sent DISCONNECT message (reason code " + reason_code + "): "
- + reasonBuffer.toString());
- }
-
- /*
- * Is it a KEX Packet?
- */
-
- if ((type == Packets.SSH_MSG_KEXINIT) || (type == Packets.SSH_MSG_NEWKEYS)
- || ((type >= 30) && (type <= 49)))
- {
- km.handleMessage(msg, msglen);
- continue;
- }
-
- if (type == Packets.SSH_MSG_USERAUTH_SUCCESS) {
- tc.startCompression();
- }
-
- MessageHandler mh = null;
-
- for (int i = 0; i < messageHandlers.size(); i++)
- {
- HandlerEntry he = messageHandlers.elementAt(i);
- if ((he.low <= type) && (type <= he.high))
- {
- mh = he.mh;
- break;
- }
- }
-
- if (mh == null)
- throw new IOException("Unexpected SSH message (type " + type + ")");
-
- mh.handleMessage(msg, msglen);
- }
- }
-}
+
+package com.trilead.ssh2.transport;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.net.UnknownHostException;
+import java.security.SecureRandom;
+import java.util.Vector;
+
+import com.trilead.ssh2.ConnectionInfo;
+import com.trilead.ssh2.ConnectionMonitor;
+import com.trilead.ssh2.DHGexParameters;
+import com.trilead.ssh2.HTTPProxyData;
+import com.trilead.ssh2.HTTPProxyException;
+import com.trilead.ssh2.ProxyData;
+import com.trilead.ssh2.ServerHostKeyVerifier;
+import com.trilead.ssh2.compression.ICompressor;
+import com.trilead.ssh2.crypto.Base64;
+import com.trilead.ssh2.crypto.CryptoWishList;
+import com.trilead.ssh2.crypto.cipher.BlockCipher;
+import com.trilead.ssh2.crypto.digest.MAC;
+import com.trilead.ssh2.log.Logger;
+import com.trilead.ssh2.packets.PacketDisconnect;
+import com.trilead.ssh2.packets.Packets;
+import com.trilead.ssh2.packets.TypesReader;
+import com.trilead.ssh2.util.Tokenizer;
+
+
+/*
+ * Yes, the "standard" is a big mess. On one side, the say that arbitary channel
+ * packets are allowed during kex exchange, on the other side we need to blindly
+ * ignore the next _packet_ if the KEX guess was wrong. Where do we know from that
+ * the next packet is not a channel data packet? Yes, we could check if it is in
+ * the KEX range. But the standard says nothing about this. The OpenSSH guys
+ * block local "normal" traffic during KEX. That's fine - however, they assume
+ * that the other side is doing the same. During re-key, if they receive traffic
+ * other than KEX, they become horribly irritated and kill the connection. Since
+ * we are very likely going to communicate with OpenSSH servers, we have to play
+ * the same game - even though we could do better.
+ *
+ * btw: having stdout and stderr on the same channel, with a shared window, is
+ * also a VERY good idea... =(
+ */
+
+/**
+ * TransportManager.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: TransportManager.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
+ */
+public class TransportManager
+{
+ private static final Logger log = Logger.getLogger(TransportManager.class);
+
+ class HandlerEntry
+ {
+ MessageHandler mh;
+ int low;
+ int high;
+ }
+
+ private final Vector<byte[]> asynchronousQueue = new Vector<byte[]>();
+ private Thread asynchronousThread = null;
+
+ class AsynchronousWorker extends Thread
+ {
+ public void run()
+ {
+ while (true)
+ {
+ byte[] msg = null;
+
+ synchronized (asynchronousQueue)
+ {
+ if (asynchronousQueue.size() == 0)
+ {
+ /* After the queue is empty for about 2 seconds, stop this thread */
+
+ try
+ {
+ asynchronousQueue.wait(2000);
+ }
+ catch (InterruptedException e)
+ {
+ /* OKOK, if somebody interrupts us, then we may die earlier. */
+ }
+
+ if (asynchronousQueue.size() == 0)
+ {
+ asynchronousThread = null;
+ return;
+ }
+ }
+
+ msg = asynchronousQueue.remove(0);
+ }
+
+ /* The following invocation may throw an IOException.
+ * There is no point in handling it - it simply means
+ * that the connection has a problem and we should stop
+ * sending asynchronously messages. We do not need to signal that
+ * we have exited (asynchronousThread = null): further
+ * messages in the queue cannot be sent by this or any
+ * other thread.
+ * Other threads will sooner or later (when receiving or
+ * sending the next message) get the same IOException and
+ * get to the same conclusion.
+ */
+
+ try
+ {
+ sendMessage(msg);
+ }
+ catch (IOException e)
+ {
+ return;
+ }
+ }
+ }
+ }
+
+ String hostname;
+ int port;
+ final Socket sock = new Socket();
+
+ Object connectionSemaphore = new Object();
+
+ boolean flagKexOngoing = false;
+ boolean connectionClosed = false;
+
+ Throwable reasonClosedCause = null;
+
+ TransportConnection tc;
+ KexManager km;
+
+ Vector<HandlerEntry> messageHandlers = new Vector<HandlerEntry>();
+
+ Thread receiveThread;
+
+ Vector connectionMonitors = new Vector();
+ boolean monitorsWereInformed = false;
+
+ public TransportManager(String host, int port) throws IOException
+ {
+ this.hostname = host;
+ this.port = port;
+ }
+
+ public int getPacketOverheadEstimate()
+ {
+ return tc.getPacketOverheadEstimate();
+ }
+
+ public void setTcpNoDelay(boolean state) throws IOException
+ {
+ sock.setTcpNoDelay(state);
+ }
+
+ public void setSoTimeout(int timeout) throws IOException
+ {
+ sock.setSoTimeout(timeout);
+ }
+
+ public ConnectionInfo getConnectionInfo(int kexNumber) throws IOException
+ {
+ return km.getOrWaitForConnectionInfo(kexNumber);
+ }
+
+ public Throwable getReasonClosedCause()
+ {
+ synchronized (connectionSemaphore)
+ {
+ return reasonClosedCause;
+ }
+ }
+
+ public byte[] getSessionIdentifier()
+ {
+ return km.sessionId;
+ }
+
+ public void close(Throwable cause, boolean useDisconnectPacket)
+ {
+ if (useDisconnectPacket == false)
+ {
+ /* OK, hard shutdown - do not aquire the semaphore,
+ * perhaps somebody is inside (and waits until the remote
+ * side is ready to accept new data). */
+
+ try
+ {
+ sock.close();
+ }
+ catch (IOException ignore)
+ {
+ }
+
+ /* OK, whoever tried to send data, should now agree that
+ * there is no point in further waiting =)
+ * It is safe now to aquire the semaphore.
+ */
+ }
+
+ synchronized (connectionSemaphore)
+ {
+ if (connectionClosed == false)
+ {
+ if (useDisconnectPacket == true)
+ {
+ try
+ {
+ byte[] msg = new PacketDisconnect(Packets.SSH_DISCONNECT_BY_APPLICATION, cause.getMessage(), "")
+ .getPayload();
+ if (tc != null)
+ tc.sendMessage(msg);
+ }
+ catch (IOException ignore)
+ {
+ }
+
+ try
+ {
+ sock.close();
+ }
+ catch (IOException ignore)
+ {
+ }
+ }
+
+ connectionClosed = true;
+ reasonClosedCause = cause; /* may be null */
+ }
+ connectionSemaphore.notifyAll();
+ }
+
+ /* No check if we need to inform the monitors */
+
+ Vector monitors = null;
+
+ synchronized (this)
+ {
+ /* Short term lock to protect "connectionMonitors"
+ * and "monitorsWereInformed"
+ * (they may be modified concurrently)
+ */
+
+ if (monitorsWereInformed == false)
+ {
+ monitorsWereInformed = true;
+ monitors = (Vector) connectionMonitors.clone();
+ }
+ }
+
+ if (monitors != null)
+ {
+ for (int i = 0; i < monitors.size(); i++)
+ {
+ try
+ {
+ ConnectionMonitor cmon = (ConnectionMonitor) monitors.elementAt(i);
+ cmon.connectionLost(reasonClosedCause);
+ }
+ catch (Exception ignore)
+ {
+ }
+ }
+ }
+ }
+
+ private static void tryAllAddresses(Socket sock, String host, int port, int connectTimeout) throws IOException {
+ InetAddress[] addresses = InetAddress.getAllByName(host);
+ for (InetAddress addr : addresses) {
+ try {
+ sock.connect(new InetSocketAddress(addr, port), connectTimeout);
+ return;
+ } catch (SocketTimeoutException e) {
+ }
+ }
+ throw new SocketTimeoutException("Could not connect; socket timed out");
+ }
+
+ private void establishConnection(ProxyData proxyData, int connectTimeout) throws IOException
+ {
+ if (proxyData == null)
+ {
+ tryAllAddresses(sock, hostname, port, connectTimeout);
+ sock.setSoTimeout(0);
+ return;
+ }
+
+ if (proxyData instanceof HTTPProxyData)
+ {
+ HTTPProxyData pd = (HTTPProxyData) proxyData;
+
+ /* At the moment, we only support HTTP proxies */
+
+ tryAllAddresses(sock, pd.proxyHost, pd.proxyPort, connectTimeout);
+ sock.setSoTimeout(0);
+
+ /* OK, now tell the proxy where we actually want to connect to */
+
+ StringBuffer sb = new StringBuffer();
+
+ sb.append("CONNECT ");
+ sb.append(hostname);
+ sb.append(':');
+ sb.append(port);
+ sb.append(" HTTP/1.0\r\n");
+
+ if ((pd.proxyUser != null) && (pd.proxyPass != null))
+ {
+ String credentials = pd.proxyUser + ":" + pd.proxyPass;
+ char[] encoded = Base64.encode(credentials.getBytes("ISO-8859-1"));
+ sb.append("Proxy-Authorization: Basic ");
+ sb.append(encoded);
+ sb.append("\r\n");
+ }
+
+ if (pd.requestHeaderLines != null)
+ {
+ for (int i = 0; i < pd.requestHeaderLines.length; i++)
+ {
+ if (pd.requestHeaderLines[i] != null)
+ {
+ sb.append(pd.requestHeaderLines[i]);
+ sb.append("\r\n");
+ }
+ }
+ }
+
+ sb.append("\r\n");
+
+ OutputStream out = sock.getOutputStream();
+
+ out.write(sb.toString().getBytes("ISO-8859-1"));
+ out.flush();
+
+ /* Now parse the HTTP response */
+
+ byte[] buffer = new byte[1024];
+ InputStream in = sock.getInputStream();
+
+ int len = ClientServerHello.readLineRN(in, buffer);
+
+ String httpReponse = new String(buffer, 0, len, "ISO-8859-1");
+
+ if (httpReponse.startsWith("HTTP/") == false)
+ throw new IOException("The proxy did not send back a valid HTTP response.");
+
+ /* "HTTP/1.X XYZ X" => 14 characters minimum */
+
+ if ((httpReponse.length() < 14) || (httpReponse.charAt(8) != ' ') || (httpReponse.charAt(12) != ' '))
+ throw new IOException("The proxy did not send back a valid HTTP response.");
+
+ int errorCode = 0;
+
+ try
+ {
+ errorCode = Integer.parseInt(httpReponse.substring(9, 12));
+ }
+ catch (NumberFormatException ignore)
+ {
+ throw new IOException("The proxy did not send back a valid HTTP response.");
+ }
+
+ if ((errorCode < 0) || (errorCode > 999))
+ throw new IOException("The proxy did not send back a valid HTTP response.");
+
+ if (errorCode != 200)
+ {
+ throw new HTTPProxyException(httpReponse.substring(13), errorCode);
+ }
+
+ /* OK, read until empty line */
+
+ while (true)
+ {
+ len = ClientServerHello.readLineRN(in, buffer);
+ if (len == 0)
+ break;
+ }
+ return;
+ }
+
+ throw new IOException("Unsupported ProxyData");
+ }
+
+ public void initialize(CryptoWishList cwl, ServerHostKeyVerifier verifier, DHGexParameters dhgex,
+ int connectTimeout, SecureRandom rnd, ProxyData proxyData) throws IOException
+ {
+ /* First, establish the TCP connection to the SSH-2 server */
+
+ establishConnection(proxyData, connectTimeout);
+
+ /* Parse the server line and say hello - important: this information is later needed for the
+ * key exchange (to stop man-in-the-middle attacks) - that is why we wrap it into an object
+ * for later use.
+ */
+
+ ClientServerHello csh = new ClientServerHello(sock.getInputStream(), sock.getOutputStream());
+
+ tc = new TransportConnection(sock.getInputStream(), sock.getOutputStream(), rnd);
+
+ km = new KexManager(this, csh, cwl, hostname, port, verifier, rnd);
+ km.initiateKEX(cwl, dhgex);
+
+ receiveThread = new Thread(new Runnable()
+ {
+ public void run()
+ {
+ try
+ {
+ receiveLoop();
+ }
+ catch (IOException e)
+ {
+ close(e, false);
+
+ if (log.isEnabled())
+ log.log(10, "Receive thread: error in receiveLoop: " + e.getMessage());
+ }
+
+ if (log.isEnabled())
+ log.log(50, "Receive thread: back from receiveLoop");
+
+ /* Tell all handlers that it is time to say goodbye */
+
+ if (km != null)
+ {
+ try
+ {
+ km.handleMessage(null, 0);
+ }
+ catch (IOException e)
+ {
+ }
+ }
+
+ for (int i = 0; i < messageHandlers.size(); i++)
+ {
+ HandlerEntry he = messageHandlers.elementAt(i);
+ try
+ {
+ he.mh.handleMessage(null, 0);
+ }
+ catch (Exception ignore)
+ {
+ }
+ }
+ }
+ });
+
+ receiveThread.setDaemon(true);
+ receiveThread.start();
+ }
+
+ public void registerMessageHandler(MessageHandler mh, int low, int high)
+ {
+ HandlerEntry he = new HandlerEntry();
+ he.mh = mh;
+ he.low = low;
+ he.high = high;
+
+ synchronized (messageHandlers)
+ {
+ messageHandlers.addElement(he);
+ }
+ }
+
+ public void removeMessageHandler(MessageHandler mh, int low, int high)
+ {
+ synchronized (messageHandlers)
+ {
+ for (int i = 0; i < messageHandlers.size(); i++)
+ {
+ HandlerEntry he = messageHandlers.elementAt(i);
+ if ((he.mh == mh) && (he.low == low) && (he.high == high))
+ {
+ messageHandlers.removeElementAt(i);
+ break;
+ }
+ }
+ }
+ }
+
+ public void sendKexMessage(byte[] msg) throws IOException
+ {
+ synchronized (connectionSemaphore)
+ {
+ if (connectionClosed)
+ {
+ throw (IOException) new IOException("Sorry, this connection is closed.").initCause(reasonClosedCause);
+ }
+
+ flagKexOngoing = true;
+
+ try
+ {
+ tc.sendMessage(msg);
+ }
+ catch (IOException e)
+ {
+ close(e, false);
+ throw e;
+ }
+ }
+ }
+
+ public void kexFinished() throws IOException
+ {
+ synchronized (connectionSemaphore)
+ {
+ flagKexOngoing = false;
+ connectionSemaphore.notifyAll();
+ }
+ }
+
+ public void forceKeyExchange(CryptoWishList cwl, DHGexParameters dhgex) throws IOException
+ {
+ km.initiateKEX(cwl, dhgex);
+ }
+
+ public void changeRecvCipher(BlockCipher bc, MAC mac)
+ {
+ tc.changeRecvCipher(bc, mac);
+ }
+
+ public void changeSendCipher(BlockCipher bc, MAC mac)
+ {
+ tc.changeSendCipher(bc, mac);
+ }
+
+ /**
+ * @param comp
+ */
+ public void changeRecvCompression(ICompressor comp) {
+ tc.changeRecvCompression(comp);
+ }
+
+ /**
+ * @param comp
+ */
+ public void changeSendCompression(ICompressor comp) {
+ tc.changeSendCompression(comp);
+ }
+
+ /**
+ *
+ */
+ public void startCompression() {
+ tc.startCompression();
+ }
+
+ public void sendAsynchronousMessage(byte[] msg) throws IOException
+ {
+ synchronized (asynchronousQueue)
+ {
+ asynchronousQueue.addElement(msg);
+
+ /* This limit should be flexible enough. We need this, otherwise the peer
+ * can flood us with global requests (and other stuff where we have to reply
+ * with an asynchronous message) and (if the server just sends data and does not
+ * read what we send) this will probably put us in a low memory situation
+ * (our send queue would grow and grow and...) */
+
+ if (asynchronousQueue.size() > 100)
+ throw new IOException("Error: the peer is not consuming our asynchronous replies.");
+
+ /* Check if we have an asynchronous sending thread */
+
+ if (asynchronousThread == null)
+ {
+ asynchronousThread = new AsynchronousWorker();
+ asynchronousThread.setDaemon(true);
+ asynchronousThread.start();
+
+ /* The thread will stop after 2 seconds of inactivity (i.e., empty queue) */
+ }
+ }
+ }
+
+ public void setConnectionMonitors(Vector monitors)
+ {
+ synchronized (this)
+ {
+ connectionMonitors = (Vector) monitors.clone();
+ }
+ }
+
+ public void sendMessage(byte[] msg) throws IOException
+ {
+ if (Thread.currentThread() == receiveThread)
+ throw new IOException("Assertion error: sendMessage may never be invoked by the receiver thread!");
+
+ synchronized (connectionSemaphore)
+ {
+ while (true)
+ {
+ if (connectionClosed)
+ {
+ throw (IOException) new IOException("Sorry, this connection is closed.")
+ .initCause(reasonClosedCause);
+ }
+
+ if (flagKexOngoing == false)
+ break;
+
+ try
+ {
+ connectionSemaphore.wait();
+ }
+ catch (InterruptedException e)
+ {
+ }
+ }
+
+ try
+ {
+ tc.sendMessage(msg);
+ }
+ catch (IOException e)
+ {
+ close(e, false);
+ throw e;
+ }
+ }
+ }
+
+ public void receiveLoop() throws IOException
+ {
+ byte[] msg = new byte[35000];
+
+ while (true)
+ {
+ int msglen = tc.receiveMessage(msg, 0, msg.length);
+
+ int type = msg[0] & 0xff;
+
+ if (type == Packets.SSH_MSG_IGNORE)
+ continue;
+
+ if (type == Packets.SSH_MSG_DEBUG)
+ {
+ if (log.isEnabled())
+ {
+ TypesReader tr = new TypesReader(msg, 0, msglen);
+ tr.readByte();
+ tr.readBoolean();
+ StringBuffer debugMessageBuffer = new StringBuffer();
+ debugMessageBuffer.append(tr.readString("UTF-8"));
+
+ for (int i = 0; i < debugMessageBuffer.length(); i++)
+ {
+ char c = debugMessageBuffer.charAt(i);
+
+ if ((c >= 32) && (c <= 126))
+ continue;
+ debugMessageBuffer.setCharAt(i, '\uFFFD');
+ }
+
+ log.log(50, "DEBUG Message from remote: '" + debugMessageBuffer.toString() + "'");
+ }
+ continue;
+ }
+
+ if (type == Packets.SSH_MSG_UNIMPLEMENTED)
+ {
+ throw new IOException("Peer sent UNIMPLEMENTED message, that should not happen.");
+ }
+
+ if (type == Packets.SSH_MSG_DISCONNECT)
+ {
+ TypesReader tr = new TypesReader(msg, 0, msglen);
+ tr.readByte();
+ int reason_code = tr.readUINT32();
+ StringBuffer reasonBuffer = new StringBuffer();
+ reasonBuffer.append(tr.readString("UTF-8"));
+
+ /*
+ * Do not get fooled by servers that send abnormal long error
+ * messages
+ */
+
+ if (reasonBuffer.length() > 255)
+ {
+ reasonBuffer.setLength(255);
+ reasonBuffer.setCharAt(254, '.');
+ reasonBuffer.setCharAt(253, '.');
+ reasonBuffer.setCharAt(252, '.');
+ }
+
+ /*
+ * Also, check that the server did not send charcaters that may
+ * screw up the receiver -> restrict to reasonable US-ASCII
+ * subset -> "printable characters" (ASCII 32 - 126). Replace
+ * all others with 0xFFFD (UNICODE replacement character).
+ */
+
+ for (int i = 0; i < reasonBuffer.length(); i++)
+ {
+ char c = reasonBuffer.charAt(i);
+
+ if ((c >= 32) && (c <= 126))
+ continue;
+ reasonBuffer.setCharAt(i, '\uFFFD');
+ }
+
+ throw new IOException("Peer sent DISCONNECT message (reason code " + reason_code + "): "
+ + reasonBuffer.toString());
+ }
+
+ /*
+ * Is it a KEX Packet?
+ */
+
+ if ((type == Packets.SSH_MSG_KEXINIT) || (type == Packets.SSH_MSG_NEWKEYS)
+ || ((type >= 30) && (type <= 49)))
+ {
+ km.handleMessage(msg, msglen);
+ continue;
+ }
+
+ if (type == Packets.SSH_MSG_USERAUTH_SUCCESS) {
+ tc.startCompression();
+ }
+
+ MessageHandler mh = null;
+
+ for (int i = 0; i < messageHandlers.size(); i++)
+ {
+ HandlerEntry he = messageHandlers.elementAt(i);
+ if ((he.low <= type) && (type <= he.high))
+ {
+ mh = he.mh;
+ break;
+ }
+ }
+
+ if (mh == null)
+ throw new IOException("Unexpected SSH message (type " + type + ")");
+
+ mh.handleMessage(msg, msglen);
+ }
+ }
+}
diff --git a/src/com/trilead/ssh2/util/TimeoutService.java b/src/com/trilead/ssh2/util/TimeoutService.java
index b09ed07..3d52161 100644
--- a/src/com/trilead/ssh2/util/TimeoutService.java
+++ b/src/com/trilead/ssh2/util/TimeoutService.java
@@ -1,149 +1,149 @@
-
-package com.trilead.ssh2.util;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.Collections;
-import java.util.LinkedList;
-
-import com.trilead.ssh2.log.Logger;
-
-
-/**
- * TimeoutService (beta). Here you can register a timeout.
- * <p>
- * Implemented having large scale programs in mind: if you open many concurrent SSH connections
- * that rely on timeouts, then there will be only one timeout thread. Once all timeouts
- * have expired/are cancelled, the thread will (sooner or later) exit.
- * Only after new timeouts arrive a new thread (singleton) will be instantiated.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: TimeoutService.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public class TimeoutService
-{
- private static final Logger log = Logger.getLogger(TimeoutService.class);
-
- public static class TimeoutToken implements Comparable
- {
- private long runTime;
- private Runnable handler;
-
- private TimeoutToken(long runTime, Runnable handler)
- {
- this.runTime = runTime;
- this.handler = handler;
- }
-
- public int compareTo(Object o)
- {
- TimeoutToken t = (TimeoutToken) o;
- if (runTime > t.runTime)
- return 1;
- if (runTime == t.runTime)
- return 0;
- return -1;
- }
- }
-
- private static class TimeoutThread extends Thread
- {
- public void run()
- {
- synchronized (todolist)
- {
- while (true)
- {
- if (todolist.size() == 0)
- {
- timeoutThread = null;
- return;
- }
-
- long now = System.currentTimeMillis();
-
- TimeoutToken tt = (TimeoutToken) todolist.getFirst();
-
- if (tt.runTime > now)
- {
- /* Not ready yet, sleep a little bit */
-
- try
- {
- todolist.wait(tt.runTime - now);
- }
- catch (InterruptedException e)
- {
- }
-
- /* We cannot simply go on, since it could be that the token
- * was removed (cancelled) or another one has been inserted in
- * the meantime.
- */
-
- continue;
- }
-
- todolist.removeFirst();
-
- try
- {
- tt.handler.run();
- }
- catch (Exception e)
- {
- StringWriter sw = new StringWriter();
- e.printStackTrace(new PrintWriter(sw));
- log.log(20, "Exeception in Timeout handler:" + e.getMessage() + "(" + sw.toString() + ")");
- }
- }
- }
- }
- }
-
- /* The list object is also used for locking purposes */
- private static final LinkedList todolist = new LinkedList();
-
- private static Thread timeoutThread = null;
-
- /**
- * It is assumed that the passed handler will not execute for a long time.
- *
- * @param runTime
- * @param handler
- * @return a TimeoutToken that can be used to cancel the timeout.
- */
- public static final TimeoutToken addTimeoutHandler(long runTime, Runnable handler)
- {
- TimeoutToken token = new TimeoutToken(runTime, handler);
-
- synchronized (todolist)
- {
- todolist.add(token);
- Collections.sort(todolist);
-
- if (timeoutThread != null)
- timeoutThread.interrupt();
- else
- {
- timeoutThread = new TimeoutThread();
- timeoutThread.setDaemon(true);
- timeoutThread.start();
- }
- }
-
- return token;
- }
-
- public static final void cancelTimeoutHandler(TimeoutToken token)
- {
- synchronized (todolist)
- {
- todolist.remove(token);
-
- if (timeoutThread != null)
- timeoutThread.interrupt();
- }
- }
-
-}
+
+package com.trilead.ssh2.util;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Collections;
+import java.util.LinkedList;
+
+import com.trilead.ssh2.log.Logger;
+
+
+/**
+ * TimeoutService (beta). Here you can register a timeout.
+ * <p>
+ * Implemented having large scale programs in mind: if you open many concurrent SSH connections
+ * that rely on timeouts, then there will be only one timeout thread. Once all timeouts
+ * have expired/are cancelled, the thread will (sooner or later) exit.
+ * Only after new timeouts arrive a new thread (singleton) will be instantiated.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: TimeoutService.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
+ */
+public class TimeoutService
+{
+ private static final Logger log = Logger.getLogger(TimeoutService.class);
+
+ public static class TimeoutToken implements Comparable
+ {
+ private long runTime;
+ private Runnable handler;
+
+ private TimeoutToken(long runTime, Runnable handler)
+ {
+ this.runTime = runTime;
+ this.handler = handler;
+ }
+
+ public int compareTo(Object o)
+ {
+ TimeoutToken t = (TimeoutToken) o;
+ if (runTime > t.runTime)
+ return 1;
+ if (runTime == t.runTime)
+ return 0;
+ return -1;
+ }
+ }
+
+ private static class TimeoutThread extends Thread
+ {
+ public void run()
+ {
+ synchronized (todolist)
+ {
+ while (true)
+ {
+ if (todolist.size() == 0)
+ {
+ timeoutThread = null;
+ return;
+ }
+
+ long now = System.currentTimeMillis();
+
+ TimeoutToken tt = (TimeoutToken) todolist.getFirst();
+
+ if (tt.runTime > now)
+ {
+ /* Not ready yet, sleep a little bit */
+
+ try
+ {
+ todolist.wait(tt.runTime - now);
+ }
+ catch (InterruptedException e)
+ {
+ }
+
+ /* We cannot simply go on, since it could be that the token
+ * was removed (cancelled) or another one has been inserted in
+ * the meantime.
+ */
+
+ continue;
+ }
+
+ todolist.removeFirst();
+
+ try
+ {
+ tt.handler.run();
+ }
+ catch (Exception e)
+ {
+ StringWriter sw = new StringWriter();
+ e.printStackTrace(new PrintWriter(sw));
+ log.log(20, "Exeception in Timeout handler:" + e.getMessage() + "(" + sw.toString() + ")");
+ }
+ }
+ }
+ }
+ }
+
+ /* The list object is also used for locking purposes */
+ private static final LinkedList todolist = new LinkedList();
+
+ private static Thread timeoutThread = null;
+
+ /**
+ * It is assumed that the passed handler will not execute for a long time.
+ *
+ * @param runTime
+ * @param handler
+ * @return a TimeoutToken that can be used to cancel the timeout.
+ */
+ public static final TimeoutToken addTimeoutHandler(long runTime, Runnable handler)
+ {
+ TimeoutToken token = new TimeoutToken(runTime, handler);
+
+ synchronized (todolist)
+ {
+ todolist.add(token);
+ Collections.sort(todolist);
+
+ if (timeoutThread != null)
+ timeoutThread.interrupt();
+ else
+ {
+ timeoutThread = new TimeoutThread();
+ timeoutThread.setDaemon(true);
+ timeoutThread.start();
+ }
+ }
+
+ return token;
+ }
+
+ public static final void cancelTimeoutHandler(TimeoutToken token)
+ {
+ synchronized (todolist)
+ {
+ todolist.remove(token);
+
+ if (timeoutThread != null)
+ timeoutThread.interrupt();
+ }
+ }
+
+}
diff --git a/src/com/trilead/ssh2/util/Tokenizer.java b/src/com/trilead/ssh2/util/Tokenizer.java
index ff3fcba..dfd480b 100644
--- a/src/com/trilead/ssh2/util/Tokenizer.java
+++ b/src/com/trilead/ssh2/util/Tokenizer.java
@@ -1,51 +1,51 @@
-
-package com.trilead.ssh2.util;
-
-/**
- * Tokenizer. Why? Because StringTokenizer is not available in J2ME.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: Tokenizer.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public class Tokenizer
-{
- /**
- * Exists because StringTokenizer is not available in J2ME.
- * Returns an array with at least 1 entry.
- *
- * @param source must be non-null
- * @param delimiter
- * @return an array of Strings
- */
- public static String[] parseTokens(String source, char delimiter)
- {
- int numtoken = 1;
-
- for (int i = 0; i < source.length(); i++)
- {
- if (source.charAt(i) == delimiter)
- numtoken++;
- }
-
- String list[] = new String[numtoken];
- int nextfield = 0;
-
- for (int i = 0; i < numtoken; i++)
- {
- if (nextfield >= source.length())
- {
- list[i] = "";
- }
- else
- {
- int idx = source.indexOf(delimiter, nextfield);
- if (idx == -1)
- idx = source.length();
- list[i] = source.substring(nextfield, idx);
- nextfield = idx + 1;
- }
- }
-
- return list;
- }
-}
+
+package com.trilead.ssh2.util;
+
+/**
+ * Tokenizer. Why? Because StringTokenizer is not available in J2ME.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: Tokenizer.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
+ */
+public class Tokenizer
+{
+ /**
+ * Exists because StringTokenizer is not available in J2ME.
+ * Returns an array with at least 1 entry.
+ *
+ * @param source must be non-null
+ * @param delimiter
+ * @return an array of Strings
+ */
+ public static String[] parseTokens(String source, char delimiter)
+ {
+ int numtoken = 1;
+
+ for (int i = 0; i < source.length(); i++)
+ {
+ if (source.charAt(i) == delimiter)
+ numtoken++;
+ }
+
+ String list[] = new String[numtoken];
+ int nextfield = 0;
+
+ for (int i = 0; i < numtoken; i++)
+ {
+ if (nextfield >= source.length())
+ {
+ list[i] = "";
+ }
+ else
+ {
+ int idx = source.indexOf(delimiter, nextfield);
+ if (idx == -1)
+ idx = source.length();
+ list[i] = source.substring(nextfield, idx);
+ nextfield = idx + 1;
+ }
+ }
+
+ return list;
+ }
+}
diff --git a/src/de/mud/terminal/vt320.java b/src/de/mud/terminal/vt320.java
index 80f3856..73369af 100644
--- a/src/de/mud/terminal/vt320.java
+++ b/src/de/mud/terminal/vt320.java
@@ -2590,7 +2590,7 @@ public void setScreenSize(int c, int r, boolean broadcast) {
debug("ESC [ " + DCEvars[0] + " C");
break;
case 'd': // CVA
- R = DCEvars[0];
+ R = DCEvars[0] - 1;
if (R < 0)
R = 0;
else if (R >= height)
@@ -2663,9 +2663,9 @@ public void setScreenSize(int c, int r, boolean broadcast) {
break;
case 'S': /* ind aka 'scroll forward' */
if (DCEvars[0] == 0)
- insertLine(rows - 1, SCROLL_UP);
+ insertLine(getBottomMargin(), SCROLL_UP);
else
- insertLine(rows - 1, DCEvars[0], SCROLL_UP);
+ insertLine(getBottomMargin(), DCEvars[0], SCROLL_UP);
break;
case 'L':
/* insert n lines */
@@ -2678,9 +2678,9 @@ public void setScreenSize(int c, int r, boolean broadcast) {
break;
case 'T': /* 'ri' aka scroll backward */
if (DCEvars[0] == 0)
- insertLine(0, SCROLL_DOWN);
+ insertLine(getTopMargin(), SCROLL_DOWN);
else
- insertLine(0, DCEvars[0], SCROLL_DOWN);
+ insertLine(getTopMargin(), DCEvars[0], SCROLL_DOWN);
break;
case 'M':
if (debug > 1)
@@ -2733,6 +2733,7 @@ public void setScreenSize(int c, int r, boolean broadcast) {
debug("ESC [ " + DCEvars[0] + " J");
break;
case '@':
+ if (DCEvars[0] == 0) DCEvars[0] = 1;
if (debug > 1)
debug("ESC [ " + DCEvars[0] + " @");
for (int i = 0; i < DCEvars[0]; i++)
diff --git a/src/net/sourceforge/jsocks/Authentication.java b/src/net/sourceforge/jsocks/Authentication.java
index 4e9eae1..18cc48b 100644
--- a/src/net/sourceforge/jsocks/Authentication.java
+++ b/src/net/sourceforge/jsocks/Authentication.java
@@ -1,34 +1,34 @@
-package net.sourceforge.jsocks;
-
-/**
- The Authentication interface provides for performing method specific
- authentication for SOCKS5 connections.
-*/
-public interface Authentication{
- /**
- This method is called when SOCKS5 server have selected a particular
- authentication method, for whch an implementaion have been registered.
-
- <p>
- This method should return an array {inputstream,outputstream
- [,UDPEncapsulation]}. The reason for that is that SOCKS5 protocol
- allows to have method specific encapsulation of data on the socket for
- purposes of integrity or security. And this encapsulation should be
- performed by those streams returned from the method. It is also possible
- to encapsulate datagrams. If authentication method supports such
- encapsulation an instance of the UDPEncapsulation interface should be
- returned as third element of the array, otherwise either null should be
- returned as third element, or array should contain only 2 elements.
-
- @param methodId Authentication method selected by the server.
- @param proxySocket Socket used to conect to the proxy.
- @return Two or three element array containing
- Input/Output streams which should be used on this connection.
- Third argument is optional and should contain an instance
- of UDPEncapsulation. It should be provided if the authentication
- method used requires any encapsulation to be done on the
- datagrams.
- */
- Object[] doSocksAuthentication(int methodId,java.net.Socket proxySocket)
- throws java.io.IOException;
-}
+package net.sourceforge.jsocks;
+
+/**
+ The Authentication interface provides for performing method specific
+ authentication for SOCKS5 connections.
+*/
+public interface Authentication{
+ /**
+ This method is called when SOCKS5 server have selected a particular
+ authentication method, for whch an implementaion have been registered.
+
+ <p>
+ This method should return an array {inputstream,outputstream
+ [,UDPEncapsulation]}. The reason for that is that SOCKS5 protocol
+ allows to have method specific encapsulation of data on the socket for
+ purposes of integrity or security. And this encapsulation should be
+ performed by those streams returned from the method. It is also possible
+ to encapsulate datagrams. If authentication method supports such
+ encapsulation an instance of the UDPEncapsulation interface should be
+ returned as third element of the array, otherwise either null should be
+ returned as third element, or array should contain only 2 elements.
+
+ @param methodId Authentication method selected by the server.
+ @param proxySocket Socket used to conect to the proxy.
+ @return Two or three element array containing
+ Input/Output streams which should be used on this connection.
+ Third argument is optional and should contain an instance
+ of UDPEncapsulation. It should be provided if the authentication
+ method used requires any encapsulation to be done on the
+ datagrams.
+ */
+ Object[] doSocksAuthentication(int methodId,java.net.Socket proxySocket)
+ throws java.io.IOException;
+}
diff --git a/src/net/sourceforge/jsocks/AuthenticationNone.java b/src/net/sourceforge/jsocks/AuthenticationNone.java
index 676b2da..f28193a 100644
--- a/src/net/sourceforge/jsocks/AuthenticationNone.java
+++ b/src/net/sourceforge/jsocks/AuthenticationNone.java
@@ -1,17 +1,17 @@
-package net.sourceforge.jsocks;
-
-/**
- SOCKS5 none authentication. Dummy class does almost nothing.
-*/
-public class AuthenticationNone implements Authentication{
-
- public Object[] doSocksAuthentication(int methodId,
- java.net.Socket proxySocket)
- throws java.io.IOException{
-
- if(methodId!=0) return null;
-
- return new Object[] { proxySocket.getInputStream(),
- proxySocket.getOutputStream()};
- }
-}
+package net.sourceforge.jsocks;
+
+/**
+ SOCKS5 none authentication. Dummy class does almost nothing.
+*/
+public class AuthenticationNone implements Authentication{
+
+ public Object[] doSocksAuthentication(int methodId,
+ java.net.Socket proxySocket)
+ throws java.io.IOException{
+
+ if(methodId!=0) return null;
+
+ return new Object[] { proxySocket.getInputStream(),
+ proxySocket.getOutputStream()};
+ }
+}
diff --git a/src/net/sourceforge/jsocks/Proxy.java b/src/net/sourceforge/jsocks/Proxy.java
index ed91516..381c0a0 100644
--- a/src/net/sourceforge/jsocks/Proxy.java
+++ b/src/net/sourceforge/jsocks/Proxy.java
@@ -1,404 +1,404 @@
-package net.sourceforge.jsocks;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InterruptedIOException;
-import java.io.OutputStream;
-import java.net.InetAddress;
-import java.net.Socket;
-import java.net.UnknownHostException;
-
-/**
- Abstract class Proxy, base for classes Socks4Proxy and Socks5Proxy.
- Defines methods for specifying default proxy, to be
- used by all classes of this package.
-*/
-
-public abstract class Proxy{
-
-//Data members
- //protected InetRange directHosts = new InetRange();
-
- protected InetAddress proxyIP = null;
- protected String proxyHost = null;
- protected int proxyPort;
- protected Socket proxySocket = null;
-
- protected InputStream in;
- protected OutputStream out;
-
- protected int version;
-
- protected Proxy chainProxy = null;
-
-
-//Protected static/class variables
- protected static Proxy defaultProxy = null;
-
-//Constructors
-//====================
- Proxy(String proxyHost, int proxyPort) throws UnknownHostException {
- this.proxyHost = proxyHost;
- this.proxyIP = InetAddress.getByName(proxyHost);
- this.proxyPort = proxyPort;
- }
-
- Proxy(Proxy chainProxy,InetAddress proxyIP,int proxyPort){
- this.chainProxy = chainProxy;
- this.proxyIP = proxyIP;
- this.proxyPort = proxyPort;
- }
-
- Proxy(InetAddress proxyIP,int proxyPort){
- this.proxyIP = proxyIP;
- this.proxyPort = proxyPort;
- }
-
- Proxy(Proxy p){
- this.proxyIP = p.proxyIP;
- this.proxyPort = p.proxyPort;
- this.version = p.version;
- }
-
-//Public instance methods
-//========================
-
- /**
- Get the port on which proxy server is running.
- * @return Proxy port.
- */
- public int getPort(){
- return proxyPort;
- }
- /**
- Get the ip address of the proxy server host.
- * @return Proxy InetAddress.
- */
- public InetAddress getInetAddress(){
- return proxyIP;
- }
-
- /**
- Get string representation of this proxy.
- * @returns string in the form:proxyHost:proxyPort \t Version versionNumber
- */
- public String toString(){
- return (""+proxyIP.getHostName()+":"+proxyPort+"\tVersion "+version);
- }
-
-
-//Public Static(Class) Methods
-//==============================
-
- /**
- * Sets SOCKS4 proxy as default.
- @param hostName Host name on which SOCKS4 server is running.
- @param port Port on which SOCKS4 server is running.
- @param user Username to use for communications with proxy.
- */
- public static void setDefaultProxy(String hostName,int port,String user)
- throws UnknownHostException{
- defaultProxy = new Socks4Proxy(hostName,port,user);
- }
-
- /**
- * Sets SOCKS4 proxy as default.
- @param ipAddress Host address on which SOCKS4 server is running.
- @param port Port on which SOCKS4 server is running.
- @param user Username to use for communications with proxy.
- */
- public static void setDefaultProxy(InetAddress ipAddress,int port,
- String user){
- defaultProxy = new Socks4Proxy(ipAddress,port,user);
- }
- /**
- * Sets SOCKS5 proxy as default.
- * Default proxy only supports no-authentication.
- @param hostName Host name on which SOCKS5 server is running.
- @param port Port on which SOCKS5 server is running.
- */
- public static void setDefaultProxy(String hostName,int port)
- throws UnknownHostException{
- defaultProxy = new Socks5Proxy(hostName,port);
- }
- /**
- * Sets SOCKS5 proxy as default.
- * Default proxy only supports no-authentication.
- @param ipAddress Host address on which SOCKS5 server is running.
- @param port Port on which SOCKS5 server is running.
- */
- public static void setDefaultProxy(InetAddress ipAddress,int port){
- defaultProxy = new Socks5Proxy(ipAddress,port);
- }
- /**
- * Sets default proxy.
- @param p Proxy to use as default proxy.
- */
- public static void setDefaultProxy(Proxy p){
- defaultProxy = p;
- }
-
- /**
- Get current default proxy.
- * @return Current default proxy, or null if none is set.
- */
- public static Proxy getDefaultProxy(){
- return defaultProxy;
- }
-
- /**
- Parses strings in the form: host[:port:user:password], and creates
- proxy from information obtained from parsing.
- <p>
- Defaults: port = 1080.<br>
- If user specified but not password, creates Socks4Proxy, if user
- not specified creates Socks5Proxy, if both user and password are
- speciefied creates Socks5Proxy with user/password authentication.
- @param proxy_entry String in the form host[:port:user:password]
- @return Proxy created from the string, null if entry was somehow
- invalid(host unknown for example, or empty string)
- */
- public static Proxy parseProxy(String proxy_entry){
-
- String proxy_host;
- int proxy_port = 1080;
- String proxy_user = null;
- String proxy_password = null;
- Proxy proxy;
-
- java.util.StringTokenizer st = new java.util.StringTokenizer(
- proxy_entry,":");
- if(st.countTokens() < 1) return null;
-
- proxy_host = st.nextToken();
- if(st.hasMoreTokens())
- try{
- proxy_port = Integer.parseInt(st.nextToken().trim());
- }catch(NumberFormatException nfe){}
-
- if(st.hasMoreTokens())
- proxy_user = st.nextToken();
-
- if(st.hasMoreTokens())
- proxy_password = st.nextToken();
-
- try{
- if(proxy_user == null)
- proxy = new Socks5Proxy(proxy_host,proxy_port);
- else if(proxy_password == null)
- proxy = new Socks4Proxy(proxy_host,proxy_port,proxy_user);
- else{
- proxy = new Socks5Proxy(proxy_host,proxy_port);
- /*
- UserPasswordAuthentication upa = new UserPasswordAuthentication(
- proxy_user, proxy_password);
-
- ((Socks5Proxy)proxy).setAuthenticationMethod(upa.METHOD_ID,upa);
- */
- }
- }catch(UnknownHostException uhe){
- return null;
- }
-
- return proxy;
- }
-
-
-//Protected Methods
-//=================
-
- protected void startSession()throws SocksException{
- try{
- proxySocket = new Socket(proxyIP,proxyPort);
- in = proxySocket.getInputStream();
- out = proxySocket.getOutputStream();
- }catch(SocksException se){
- throw se;
- }catch(IOException io_ex){
- throw new SocksException(SOCKS_PROXY_IO_ERROR,""+io_ex);
- }
- }
-
- protected abstract Proxy copy();
- protected abstract ProxyMessage formMessage(int cmd,InetAddress ip,int port);
- protected abstract ProxyMessage formMessage(int cmd,String host,int port)
- throws UnknownHostException;
- protected abstract ProxyMessage formMessage(InputStream in)
- throws SocksException,
- IOException;
-
-
- protected ProxyMessage connect(InetAddress ip,int port)
- throws SocksException{
- try{
- startSession();
- ProxyMessage request = formMessage(SOCKS_CMD_CONNECT,
- ip,port);
- return exchange(request);
- }catch(SocksException se){
- endSession();
- throw se;
- }
- }
- protected ProxyMessage connect(String host,int port)
- throws UnknownHostException,SocksException{
- try{
- startSession();
- ProxyMessage request = formMessage(SOCKS_CMD_CONNECT,
- host,port);
- return exchange(request);
- }catch(SocksException se){
- endSession();
- throw se;
- }
- }
-
- protected ProxyMessage bind(InetAddress ip,int port)
- throws SocksException{
- try{
- startSession();
- ProxyMessage request = formMessage(SOCKS_CMD_BIND,
- ip,port);
- return exchange(request);
- }catch(SocksException se){
- endSession();
- throw se;
- }
- }
- protected ProxyMessage bind(String host,int port)
- throws UnknownHostException,SocksException{
- try{
- startSession();
- ProxyMessage request = formMessage(SOCKS_CMD_BIND,
- host,port);
- return exchange(request);
- }catch(SocksException se){
- endSession();
- throw se;
- }
- }
-
- protected ProxyMessage accept()
- throws IOException,SocksException{
- ProxyMessage msg;
- try{
- msg = formMessage(in);
- }catch(InterruptedIOException iioe){
- throw iioe;
- }catch(IOException io_ex){
- endSession();
- throw new SocksException(SOCKS_PROXY_IO_ERROR,"While Trying accept:"
- +io_ex);
- }
- return msg;
- }
-
- protected ProxyMessage udpAssociate(InetAddress ip,int port)
- throws SocksException{
- try{
- startSession();
- ProxyMessage request = formMessage(SOCKS_CMD_UDP_ASSOCIATE,
- ip,port);
- if(request != null)
- return exchange(request);
- }catch(SocksException se){
- endSession();
- throw se;
- }
- //Only get here if request was null
- endSession();
- throw new SocksException(SOCKS_METHOD_NOTSUPPORTED,
- "This version of proxy does not support UDP associate, use version 5");
- }
- protected ProxyMessage udpAssociate(String host,int port)
- throws UnknownHostException,SocksException{
- try{
- startSession();
- ProxyMessage request = formMessage(SOCKS_CMD_UDP_ASSOCIATE,
- host,port);
- if(request != null) return exchange(request);
- }catch(SocksException se){
- endSession();
- throw se;
- }
- //Only get here if request was null
- endSession();
- throw new SocksException(SOCKS_METHOD_NOTSUPPORTED,
- "This version of proxy does not support UDP associate, use version 5");
- }
-
-
- protected void endSession(){
- try{
- if(proxySocket!=null) proxySocket.close();
- proxySocket = null;
- }catch(IOException io_ex){
- }
- }
-
- /**
- *Sends the request to SOCKS server
- */
- protected void sendMsg(ProxyMessage msg)throws SocksException,
- IOException{
- msg.write(out);
- }
-
- /**
- * Reads the reply from the SOCKS server
- */
- protected ProxyMessage readMsg()throws SocksException,
- IOException{
- return formMessage(in);
- }
- /**
- *Sends the request reads reply and returns it
- *throws exception if something wrong with IO
- *or the reply code is not zero
- */
- protected ProxyMessage exchange(ProxyMessage request)
- throws SocksException{
- ProxyMessage reply;
- try{
- request.write(out);
- reply = formMessage(in);
- }catch(SocksException s_ex){
- throw s_ex;
- }catch(IOException ioe){
- throw(new SocksException(SOCKS_PROXY_IO_ERROR,""+ioe));
- }
- return reply;
- }
-
-
-//Private methods
-//===============
-
-
-//Constants
-
- public static final int SOCKS_SUCCESS =0;
- public static final int SOCKS_FAILURE =1;
- public static final int SOCKS_BADCONNECT =2;
- public static final int SOCKS_BADNETWORK =3;
- public static final int SOCKS_HOST_UNREACHABLE =4;
- public static final int SOCKS_CONNECTION_REFUSED =5;
- public static final int SOCKS_TTL_EXPIRE =6;
- public static final int SOCKS_CMD_NOT_SUPPORTED =7;
- public static final int SOCKS_ADDR_NOT_SUPPORTED =8;
-
- public static final int SOCKS_NO_PROXY =1<<16;
- public static final int SOCKS_PROXY_NO_CONNECT =2<<16;
- public static final int SOCKS_PROXY_IO_ERROR =3<<16;
- public static final int SOCKS_AUTH_NOT_SUPPORTED =4<<16;
- public static final int SOCKS_AUTH_FAILURE =5<<16;
- public static final int SOCKS_JUST_ERROR =6<<16;
-
- public static final int SOCKS_DIRECT_FAILED =7<<16;
- public static final int SOCKS_METHOD_NOTSUPPORTED =8<<16;
-
-
- public static final int SOCKS_CMD_CONNECT =0x1;
- static final int SOCKS_CMD_BIND =0x2;
- static final int SOCKS_CMD_UDP_ASSOCIATE =0x3;
-
-}
+package net.sourceforge.jsocks;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+/**
+ Abstract class Proxy, base for classes Socks4Proxy and Socks5Proxy.
+ Defines methods for specifying default proxy, to be
+ used by all classes of this package.
+*/
+
+public abstract class Proxy{
+
+//Data members
+ //protected InetRange directHosts = new InetRange();
+
+ protected InetAddress proxyIP = null;
+ protected String proxyHost = null;
+ protected int proxyPort;
+ protected Socket proxySocket = null;
+
+ protected InputStream in;
+ protected OutputStream out;
+
+ protected int version;
+
+ protected Proxy chainProxy = null;
+
+
+//Protected static/class variables
+ protected static Proxy defaultProxy = null;
+
+//Constructors
+//====================
+ Proxy(String proxyHost, int proxyPort) throws UnknownHostException {
+ this.proxyHost = proxyHost;
+ this.proxyIP = InetAddress.getByName(proxyHost);
+ this.proxyPort = proxyPort;
+ }
+
+ Proxy(Proxy chainProxy,InetAddress proxyIP,int proxyPort){
+ this.chainProxy = chainProxy;
+ this.proxyIP = proxyIP;
+ this.proxyPort = proxyPort;
+ }
+
+ Proxy(InetAddress proxyIP,int proxyPort){
+ this.proxyIP = proxyIP;
+ this.proxyPort = proxyPort;
+ }
+
+ Proxy(Proxy p){
+ this.proxyIP = p.proxyIP;
+ this.proxyPort = p.proxyPort;
+ this.version = p.version;
+ }
+
+//Public instance methods
+//========================
+
+ /**
+ Get the port on which proxy server is running.
+ * @return Proxy port.
+ */
+ public int getPort(){
+ return proxyPort;
+ }
+ /**
+ Get the ip address of the proxy server host.
+ * @return Proxy InetAddress.
+ */
+ public InetAddress getInetAddress(){
+ return proxyIP;
+ }
+
+ /**
+ Get string representation of this proxy.
+ * @returns string in the form:proxyHost:proxyPort \t Version versionNumber
+ */
+ public String toString(){
+ return (""+proxyIP.getHostName()+":"+proxyPort+"\tVersion "+version);
+ }
+
+
+//Public Static(Class) Methods
+//==============================
+
+ /**
+ * Sets SOCKS4 proxy as default.
+ @param hostName Host name on which SOCKS4 server is running.
+ @param port Port on which SOCKS4 server is running.
+ @param user Username to use for communications with proxy.
+ */
+ public static void setDefaultProxy(String hostName,int port,String user)
+ throws UnknownHostException{
+ defaultProxy = new Socks4Proxy(hostName,port,user);
+ }
+
+ /**
+ * Sets SOCKS4 proxy as default.
+ @param ipAddress Host address on which SOCKS4 server is running.
+ @param port Port on which SOCKS4 server is running.
+ @param user Username to use for communications with proxy.
+ */
+ public static void setDefaultProxy(InetAddress ipAddress,int port,
+ String user){
+ defaultProxy = new Socks4Proxy(ipAddress,port,user);
+ }
+ /**
+ * Sets SOCKS5 proxy as default.
+ * Default proxy only supports no-authentication.
+ @param hostName Host name on which SOCKS5 server is running.
+ @param port Port on which SOCKS5 server is running.
+ */
+ public static void setDefaultProxy(String hostName,int port)
+ throws UnknownHostException{
+ defaultProxy = new Socks5Proxy(hostName,port);
+ }
+ /**
+ * Sets SOCKS5 proxy as default.
+ * Default proxy only supports no-authentication.
+ @param ipAddress Host address on which SOCKS5 server is running.
+ @param port Port on which SOCKS5 server is running.
+ */
+ public static void setDefaultProxy(InetAddress ipAddress,int port){
+ defaultProxy = new Socks5Proxy(ipAddress,port);
+ }
+ /**
+ * Sets default proxy.
+ @param p Proxy to use as default proxy.
+ */
+ public static void setDefaultProxy(Proxy p){
+ defaultProxy = p;
+ }
+
+ /**
+ Get current default proxy.
+ * @return Current default proxy, or null if none is set.
+ */
+ public static Proxy getDefaultProxy(){
+ return defaultProxy;
+ }
+
+ /**
+ Parses strings in the form: host[:port:user:password], and creates
+ proxy from information obtained from parsing.
+ <p>
+ Defaults: port = 1080.<br>
+ If user specified but not password, creates Socks4Proxy, if user
+ not specified creates Socks5Proxy, if both user and password are
+ speciefied creates Socks5Proxy with user/password authentication.
+ @param proxy_entry String in the form host[:port:user:password]
+ @return Proxy created from the string, null if entry was somehow
+ invalid(host unknown for example, or empty string)
+ */
+ public static Proxy parseProxy(String proxy_entry){
+
+ String proxy_host;
+ int proxy_port = 1080;
+ String proxy_user = null;
+ String proxy_password = null;
+ Proxy proxy;
+
+ java.util.StringTokenizer st = new java.util.StringTokenizer(
+ proxy_entry,":");
+ if(st.countTokens() < 1) return null;
+
+ proxy_host = st.nextToken();
+ if(st.hasMoreTokens())
+ try{
+ proxy_port = Integer.parseInt(st.nextToken().trim());
+ }catch(NumberFormatException nfe){}
+
+ if(st.hasMoreTokens())
+ proxy_user = st.nextToken();
+
+ if(st.hasMoreTokens())
+ proxy_password = st.nextToken();
+
+ try{
+ if(proxy_user == null)
+ proxy = new Socks5Proxy(proxy_host,proxy_port);
+ else if(proxy_password == null)
+ proxy = new Socks4Proxy(proxy_host,proxy_port,proxy_user);
+ else{
+ proxy = new Socks5Proxy(proxy_host,proxy_port);
+ /*
+ UserPasswordAuthentication upa = new UserPasswordAuthentication(
+ proxy_user, proxy_password);
+
+ ((Socks5Proxy)proxy).setAuthenticationMethod(upa.METHOD_ID,upa);
+ */
+ }
+ }catch(UnknownHostException uhe){
+ return null;
+ }
+
+ return proxy;
+ }
+
+
+//Protected Methods
+//=================
+
+ protected void startSession()throws SocksException{
+ try{
+ proxySocket = new Socket(proxyIP,proxyPort);
+ in = proxySocket.getInputStream();
+ out = proxySocket.getOutputStream();
+ }catch(SocksException se){
+ throw se;
+ }catch(IOException io_ex){
+ throw new SocksException(SOCKS_PROXY_IO_ERROR,""+io_ex);
+ }
+ }
+
+ protected abstract Proxy copy();
+ protected abstract ProxyMessage formMessage(int cmd,InetAddress ip,int port);
+ protected abstract ProxyMessage formMessage(int cmd,String host,int port)
+ throws UnknownHostException;
+ protected abstract ProxyMessage formMessage(InputStream in)
+ throws SocksException,
+ IOException;
+
+
+ protected ProxyMessage connect(InetAddress ip,int port)
+ throws SocksException{
+ try{
+ startSession();
+ ProxyMessage request = formMessage(SOCKS_CMD_CONNECT,
+ ip,port);
+ return exchange(request);
+ }catch(SocksException se){
+ endSession();
+ throw se;
+ }
+ }
+ protected ProxyMessage connect(String host,int port)
+ throws UnknownHostException,SocksException{
+ try{
+ startSession();
+ ProxyMessage request = formMessage(SOCKS_CMD_CONNECT,
+ host,port);
+ return exchange(request);
+ }catch(SocksException se){
+ endSession();
+ throw se;
+ }
+ }
+
+ protected ProxyMessage bind(InetAddress ip,int port)
+ throws SocksException{
+ try{
+ startSession();
+ ProxyMessage request = formMessage(SOCKS_CMD_BIND,
+ ip,port);
+ return exchange(request);
+ }catch(SocksException se){
+ endSession();
+ throw se;
+ }
+ }
+ protected ProxyMessage bind(String host,int port)
+ throws UnknownHostException,SocksException{
+ try{
+ startSession();
+ ProxyMessage request = formMessage(SOCKS_CMD_BIND,
+ host,port);
+ return exchange(request);
+ }catch(SocksException se){
+ endSession();
+ throw se;
+ }
+ }
+
+ protected ProxyMessage accept()
+ throws IOException,SocksException{
+ ProxyMessage msg;
+ try{
+ msg = formMessage(in);
+ }catch(InterruptedIOException iioe){
+ throw iioe;
+ }catch(IOException io_ex){
+ endSession();
+ throw new SocksException(SOCKS_PROXY_IO_ERROR,"While Trying accept:"
+ +io_ex);
+ }
+ return msg;
+ }
+
+ protected ProxyMessage udpAssociate(InetAddress ip,int port)
+ throws SocksException{
+ try{
+ startSession();
+ ProxyMessage request = formMessage(SOCKS_CMD_UDP_ASSOCIATE,
+ ip,port);
+ if(request != null)
+ return exchange(request);
+ }catch(SocksException se){
+ endSession();
+ throw se;
+ }
+ //Only get here if request was null
+ endSession();
+ throw new SocksException(SOCKS_METHOD_NOTSUPPORTED,
+ "This version of proxy does not support UDP associate, use version 5");
+ }
+ protected ProxyMessage udpAssociate(String host,int port)
+ throws UnknownHostException,SocksException{
+ try{
+ startSession();
+ ProxyMessage request = formMessage(SOCKS_CMD_UDP_ASSOCIATE,
+ host,port);
+ if(request != null) return exchange(request);
+ }catch(SocksException se){
+ endSession();
+ throw se;
+ }
+ //Only get here if request was null
+ endSession();
+ throw new SocksException(SOCKS_METHOD_NOTSUPPORTED,
+ "This version of proxy does not support UDP associate, use version 5");
+ }
+
+
+ protected void endSession(){
+ try{
+ if(proxySocket!=null) proxySocket.close();
+ proxySocket = null;
+ }catch(IOException io_ex){
+ }
+ }
+
+ /**
+ *Sends the request to SOCKS server
+ */
+ protected void sendMsg(ProxyMessage msg)throws SocksException,
+ IOException{
+ msg.write(out);
+ }
+
+ /**
+ * Reads the reply from the SOCKS server
+ */
+ protected ProxyMessage readMsg()throws SocksException,
+ IOException{
+ return formMessage(in);
+ }
+ /**
+ *Sends the request reads reply and returns it
+ *throws exception if something wrong with IO
+ *or the reply code is not zero
+ */
+ protected ProxyMessage exchange(ProxyMessage request)
+ throws SocksException{
+ ProxyMessage reply;
+ try{
+ request.write(out);
+ reply = formMessage(in);
+ }catch(SocksException s_ex){
+ throw s_ex;
+ }catch(IOException ioe){
+ throw(new SocksException(SOCKS_PROXY_IO_ERROR,""+ioe));
+ }
+ return reply;
+ }
+
+
+//Private methods
+//===============
+
+
+//Constants
+
+ public static final int SOCKS_SUCCESS =0;
+ public static final int SOCKS_FAILURE =1;
+ public static final int SOCKS_BADCONNECT =2;
+ public static final int SOCKS_BADNETWORK =3;
+ public static final int SOCKS_HOST_UNREACHABLE =4;
+ public static final int SOCKS_CONNECTION_REFUSED =5;
+ public static final int SOCKS_TTL_EXPIRE =6;
+ public static final int SOCKS_CMD_NOT_SUPPORTED =7;
+ public static final int SOCKS_ADDR_NOT_SUPPORTED =8;
+
+ public static final int SOCKS_NO_PROXY =1<<16;
+ public static final int SOCKS_PROXY_NO_CONNECT =2<<16;
+ public static final int SOCKS_PROXY_IO_ERROR =3<<16;
+ public static final int SOCKS_AUTH_NOT_SUPPORTED =4<<16;
+ public static final int SOCKS_AUTH_FAILURE =5<<16;
+ public static final int SOCKS_JUST_ERROR =6<<16;
+
+ public static final int SOCKS_DIRECT_FAILED =7<<16;
+ public static final int SOCKS_METHOD_NOTSUPPORTED =8<<16;
+
+
+ public static final int SOCKS_CMD_CONNECT =0x1;
+ static final int SOCKS_CMD_BIND =0x2;
+ static final int SOCKS_CMD_UDP_ASSOCIATE =0x3;
+
+}
diff --git a/src/net/sourceforge/jsocks/ProxyMessage.java b/src/net/sourceforge/jsocks/ProxyMessage.java
index 6ea8b4b..442c380 100644
--- a/src/net/sourceforge/jsocks/ProxyMessage.java
+++ b/src/net/sourceforge/jsocks/ProxyMessage.java
@@ -1,109 +1,109 @@
-package net.sourceforge.jsocks;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-/**
- Abstract class which describes SOCKS4/5 response/request.
-*/
-public abstract class ProxyMessage{
- /** Host as an IP address */
- public InetAddress ip=null;
- /** SOCKS version, or version of the response for SOCKS4*/
- public int version;
- /** Port field of the request/response*/
- public int port;
- /** Request/response code as an int*/
- public int command;
- /** Host as string.*/
- public String host=null;
- /** User field for SOCKS4 request messages*/
- public String user=null;
-
- ProxyMessage(int command,InetAddress ip,int port){
- this.command = command;
- this.ip = ip;
- this.port = port;
- }
-
- ProxyMessage(){
- }
-
-
- /**
- Initialises Message from the stream. Reads server response from
- given stream.
- @param in Input stream to read response from.
- @throws SocksException If server response code is not SOCKS_SUCCESS(0), or
- if any error with protocol occurs.
- @throws IOException If any error happens with I/O.
- */
- public abstract void read(InputStream in)
- throws SocksException,
- IOException;
-
-
- /**
- Initialises Message from the stream. Reads server response or client
- request from given stream.
-
- @param in Input stream to read response from.
- @param clinetMode If true read server response, else read client request.
- @throws SocksException If server response code is not SOCKS_SUCCESS(0) and
- reading in client mode, or if any error with protocol occurs.
- @throws IOException If any error happens with I/O.
- */
- public abstract void read(InputStream in,boolean client_mode)
- throws SocksException,
- IOException;
-
-
- /**
- Writes the message to the stream.
- @param out Output stream to which message should be written.
- */
- public abstract void write(OutputStream out)throws SocksException,
- IOException;
-
- /**
- Get the Address field of this message as InetAddress object.
- @return Host address or null, if one can't be determined.
- */
- public InetAddress getInetAddress() throws UnknownHostException{
- return ip;
- }
-
-
- /**
- Get string representaion of this message.
- @return string representation of this message.
- */
- public String toString(){
- return
- "Proxy Message:\n"+
- "Version:"+ version+"\n"+
- "Command:"+ command+"\n"+
- "IP: "+ ip+"\n"+
- "Port: "+ port+"\n"+
- "User: "+ user+"\n" ;
- }
-
-//Package methods
-//////////////////
-
- static final String bytes2IPV4(byte[] addr,int offset){
- String hostName = ""+(addr[offset] & 0xFF);
- for(int i = offset+1;i<offset+4;++i)
- hostName+="."+(addr[i] & 0xFF);
- return hostName;
- }
-
- static final String bytes2IPV6(byte[] addr,int offset){
- //Have no idea how they look like!
- return null;
- }
-
-}
+package net.sourceforge.jsocks;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ Abstract class which describes SOCKS4/5 response/request.
+*/
+public abstract class ProxyMessage{
+ /** Host as an IP address */
+ public InetAddress ip=null;
+ /** SOCKS version, or version of the response for SOCKS4*/
+ public int version;
+ /** Port field of the request/response*/
+ public int port;
+ /** Request/response code as an int*/
+ public int command;
+ /** Host as string.*/
+ public String host=null;
+ /** User field for SOCKS4 request messages*/
+ public String user=null;
+
+ ProxyMessage(int command,InetAddress ip,int port){
+ this.command = command;
+ this.ip = ip;
+ this.port = port;
+ }
+
+ ProxyMessage(){
+ }
+
+
+ /**
+ Initialises Message from the stream. Reads server response from
+ given stream.
+ @param in Input stream to read response from.
+ @throws SocksException If server response code is not SOCKS_SUCCESS(0), or
+ if any error with protocol occurs.
+ @throws IOException If any error happens with I/O.
+ */
+ public abstract void read(InputStream in)
+ throws SocksException,
+ IOException;
+
+
+ /**
+ Initialises Message from the stream. Reads server response or client
+ request from given stream.
+
+ @param in Input stream to read response from.
+ @param clinetMode If true read server response, else read client request.
+ @throws SocksException If server response code is not SOCKS_SUCCESS(0) and
+ reading in client mode, or if any error with protocol occurs.
+ @throws IOException If any error happens with I/O.
+ */
+ public abstract void read(InputStream in,boolean client_mode)
+ throws SocksException,
+ IOException;
+
+
+ /**
+ Writes the message to the stream.
+ @param out Output stream to which message should be written.
+ */
+ public abstract void write(OutputStream out)throws SocksException,
+ IOException;
+
+ /**
+ Get the Address field of this message as InetAddress object.
+ @return Host address or null, if one can't be determined.
+ */
+ public InetAddress getInetAddress() throws UnknownHostException{
+ return ip;
+ }
+
+
+ /**
+ Get string representaion of this message.
+ @return string representation of this message.
+ */
+ public String toString(){
+ return
+ "Proxy Message:\n"+
+ "Version:"+ version+"\n"+
+ "Command:"+ command+"\n"+
+ "IP: "+ ip+"\n"+
+ "Port: "+ port+"\n"+
+ "User: "+ user+"\n" ;
+ }
+
+//Package methods
+//////////////////
+
+ static final String bytes2IPV4(byte[] addr,int offset){
+ String hostName = ""+(addr[offset] & 0xFF);
+ for(int i = offset+1;i<offset+4;++i)
+ hostName+="."+(addr[i] & 0xFF);
+ return hostName;
+ }
+
+ static final String bytes2IPV6(byte[] addr,int offset){
+ //Have no idea how they look like!
+ return null;
+ }
+
+}
diff --git a/src/net/sourceforge/jsocks/ProxyServer.java b/src/net/sourceforge/jsocks/ProxyServer.java
index 39f6383..225149d 100644
--- a/src/net/sourceforge/jsocks/ProxyServer.java
+++ b/src/net/sourceforge/jsocks/ProxyServer.java
@@ -1,591 +1,591 @@
-package net.sourceforge.jsocks;
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InterruptedIOException;
-import java.io.OutputStream;
-import java.io.PrintStream;
-import java.io.PushbackInputStream;
-import java.net.ConnectException;
-import java.net.InetAddress;
-import java.net.NoRouteToHostException;
-import java.net.ServerSocket;
-import java.net.Socket;
-
-import net.sourceforge.jsocks.server.ServerAuthenticator;
-
-/**
- SOCKS4 and SOCKS5 proxy, handles both protocols simultaniously.
- Implements all SOCKS commands, including UDP relaying.
- <p>
- In order to use it you will need to implement ServerAuthenticator
- interface. There is an implementation of this interface which does
- no authentication ServerAuthenticatorNone, but it is very dangerous
- to use, as it will give access to your local network to anybody
- in the world. One should never use this authentication scheme unless
- one have pretty good reason to do so.
- There is a couple of other authentication schemes in socks.server package.
- @see socks.server.ServerAuthenticator
-*/
-public class ProxyServer implements Runnable{
-
- ServerAuthenticator auth;
- ProxyMessage msg = null;
-
- Socket sock=null,remote_sock=null;
- ServerSocket ss=null;
- UDPRelayServer relayServer = null;
- InputStream in,remote_in;
- OutputStream out,remote_out;
-
- int mode;
- static final int START_MODE = 0;
- static final int ACCEPT_MODE = 1;
- static final int PIPE_MODE = 2;
- static final int ABORT_MODE = 3;
-
- static final int BUF_SIZE = 8192;
-
- Thread pipe_thread1,pipe_thread2;
- long lastReadTime;
-
- protected static int iddleTimeout = 180000; //3 minutes
- static int acceptTimeout = 180000; //3 minutes
-
- static PrintStream log = null;
- static Proxy proxy;
-
-
-//Public Constructors
-/////////////////////
-
-
- /**
- Creates a proxy server with given Authentication scheme.
- @param auth Authentication scheme to be used.
- */
- public ProxyServer(ServerAuthenticator auth){
- this.auth = auth;
- }
-
-//Other constructors
-////////////////////
-
- protected ProxyServer(ServerAuthenticator auth,Socket s){
- this.auth = auth;
- this.sock = s;
- mode = START_MODE;
- }
-
-//Public methods
-/////////////////
-
- /**
- Set the logging stream. Specifying null disables logging.
- */
- public static void setLog(OutputStream out){
- if(out == null){
- log = null;
- }else{
- log = new PrintStream(out,true);
- }
-
- UDPRelayServer.log = log;
- }
-
- /**
- Set proxy.
- <p>
- Allows Proxy chaining so that one Proxy server is connected to another
- and so on. If proxy supports SOCKSv4, then only some SOCKSv5 requests
- can be handled, UDP would not work, however CONNECT and BIND will be
- translated.
-
- @param p Proxy which should be used to handle user requests.
- */
- public static void setProxy(Proxy p){
- proxy =p;
- UDPRelayServer.proxy = proxy;
- }
-
- /**
- Get proxy.
- @return Proxy wich is used to handle user requests.
- */
- public static Proxy getProxy(){
- return proxy;
- }
-
- /**
- Sets the timeout for connections, how long shoud server wait
- for data to arrive before dropping the connection.<br>
- Zero timeout implies infinity.<br>
- Default timeout is 3 minutes.
- */
- public static void setIddleTimeout(int timeout){
- iddleTimeout = timeout;
- }
- /**
- Sets the timeout for BIND command, how long the server should
- wait for the incoming connection.<br>
- Zero timeout implies infinity.<br>
- Default timeout is 3 minutes.
- */
- public static void setAcceptTimeout(int timeout){
- acceptTimeout = timeout;
- }
-
- /**
- Sets the timeout for UDPRelay server.<br>
- Zero timeout implies infinity.<br>
- Default timeout is 3 minutes.
- */
- public static void setUDPTimeout(int timeout){
- UDPRelayServer.setTimeout(timeout);
- }
-
- /**
- Sets the size of the datagrams used in the UDPRelayServer.<br>
- Default size is 64K, a bit more than maximum possible size of the
- datagram.
- */
- public static void setDatagramSize(int size){
- UDPRelayServer.setDatagramSize(size);
- }
-
-
- /**
- Start the Proxy server at given port.<br>
- This methods blocks.
- */
- public void start(int port){
- start(port,5,null);
- }
-
- /**
- Create a server with the specified port, listen backlog, and local
- IP address to bind to. The localIP argument can be used on a multi-homed
- host for a ServerSocket that will only accept connect requests to one of
- its addresses. If localIP is null, it will default accepting connections
- on any/all local addresses. The port must be between 0 and 65535,
- inclusive. <br>
- This methods blocks.
- */
- public void start(int port,int backlog,InetAddress localIP){
- try{
- ss = new ServerSocket(port,backlog,localIP);
- log("Starting SOCKS Proxy on:"+ss.getInetAddress().getHostAddress()+":"
- +ss.getLocalPort());
- while(true){
- Socket s = ss.accept();
- log("Accepted from:"+s.getInetAddress().getHostName()+":"
- +s.getPort());
- ProxyServer ps = new ProxyServer(auth,s);
- (new Thread(ps)).start();
- }
- }catch(IOException ioe){
- ioe.printStackTrace();
- }finally{
- }
- }
-
- /**
- Stop server operation.It would be wise to interrupt thread running the
- server afterwards.
- */
- public void stop(){
- try{
- if(ss != null) ss.close();
- }catch(IOException ioe){
- }
- }
-
-//Runnable interface
-////////////////////
- public void run(){
- switch(mode){
- case START_MODE:
- try{
- startSession();
- }catch(IOException ioe){
- handleException(ioe);
- //ioe.printStackTrace();
- }finally{
- abort();
- if(auth!=null) auth.endSession();
- log("Main thread(client->remote)stopped.");
- }
- break;
- case ACCEPT_MODE:
- try{
- doAccept();
- mode = PIPE_MODE;
- pipe_thread1.interrupt(); //Tell other thread that connection have
- //been accepted.
- pipe(remote_in,out);
- }catch(IOException ioe){
- //log("Accept exception:"+ioe);
- handleException(ioe);
- }finally{
- abort();
- log("Accept thread(remote->client) stopped");
- }
- break;
- case PIPE_MODE:
- try{
- pipe(remote_in,out);
- }catch(IOException ioe){
- }finally{
- abort();
- log("Support thread(remote->client) stopped");
- }
- break;
- case ABORT_MODE:
- break;
- default:
- log("Unexpected MODE "+mode);
- }
- }
-
-//Private methods
-/////////////////
- private void startSession() throws IOException{
- sock.setSoTimeout(iddleTimeout);
-
- try{
- auth = auth.startSession(sock);
- }catch(IOException ioe){
- log("Auth throwed exception:"+ioe);
- auth = null;
- return;
- }
-
- if(auth == null){ //Authentication failed
- log("Authentication failed");
- return;
- }
-
- in = auth.getInputStream();
- out = auth.getOutputStream();
-
- msg = readMsg(in);
- handleRequest(msg);
- }
-
- protected void handleRequest(ProxyMessage msg)
- throws IOException{
- if(!auth.checkRequest(msg)) throw new
- SocksException(Proxy.SOCKS_FAILURE);
-
- if(msg.ip == null){
- if(msg instanceof Socks5Message){
- msg.ip = InetAddress.getByName(msg.host);
- }else
- throw new SocksException(Proxy.SOCKS_FAILURE);
- }
- log(msg);
-
- switch(msg.command){
- case Proxy.SOCKS_CMD_CONNECT:
- onConnect(msg);
- break;
- case Proxy.SOCKS_CMD_BIND:
- onBind(msg);
- break;
- case Proxy.SOCKS_CMD_UDP_ASSOCIATE:
- onUDP(msg);
- break;
- default:
- throw new SocksException(Proxy.SOCKS_CMD_NOT_SUPPORTED);
- }
- }
-
- private void handleException(IOException ioe){
- //If we couldn't read the request, return;
- if(msg == null) return;
- //If have been aborted by other thread
- if(mode == ABORT_MODE) return;
- //If the request was successfully completed, but exception happened later
- if(mode == PIPE_MODE) return;
-
- int error_code = Proxy.SOCKS_FAILURE;
-
- if(ioe instanceof SocksException)
- error_code = ((SocksException)ioe).errCode;
- else if(ioe instanceof NoRouteToHostException)
- error_code = Proxy.SOCKS_HOST_UNREACHABLE;
- else if(ioe instanceof ConnectException)
- error_code = Proxy.SOCKS_CONNECTION_REFUSED;
- else if(ioe instanceof InterruptedIOException)
- error_code = Proxy.SOCKS_TTL_EXPIRE;
-
- if(error_code > Proxy.SOCKS_ADDR_NOT_SUPPORTED || error_code < 0){
- error_code = Proxy.SOCKS_FAILURE;
- }
-
- sendErrorMessage(error_code);
- }
-
- private void onConnect(ProxyMessage msg) throws IOException{
- Socket s;
- ProxyMessage response = null;
-
- s = new Socket(msg.ip,msg.port);
-
- log("Connected to "+s.getInetAddress()+":"+s.getPort());
-
- if(msg instanceof Socks5Message){
- response = new Socks5Message(Proxy.SOCKS_SUCCESS,
- s.getLocalAddress(),
- s.getLocalPort());
- }else{
- response = new Socks4Message(Socks4Message.REPLY_OK,
- s.getLocalAddress(),s.getLocalPort());
-
- }
- response.write(out);
- startPipe(s);
- }
-
- private void onBind(ProxyMessage msg) throws IOException{
- ProxyMessage response = null;
-
- if(proxy == null)
- ss = new ServerSocket(0);
- else
- ss = new SocksServerSocket(proxy, msg.ip, msg.port);
-
- ss.setSoTimeout(acceptTimeout);
-
- log("Trying accept on "+ss.getInetAddress()+":"+ss.getLocalPort());
-
- if(msg.version == 5)
- response = new Socks5Message(Proxy.SOCKS_SUCCESS,ss.getInetAddress(),
- ss.getLocalPort());
- else
- response = new Socks4Message(Socks4Message.REPLY_OK,
- ss.getInetAddress(),
- ss.getLocalPort());
- response.write(out);
-
- mode = ACCEPT_MODE;
-
- pipe_thread1 = Thread.currentThread();
- pipe_thread2 = new Thread(this);
- pipe_thread2.start();
-
- //Make timeout infinit.
- sock.setSoTimeout(0);
- int eof=0;
-
- try{
- while((eof=in.read())>=0){
- if(mode != ACCEPT_MODE){
- if(mode != PIPE_MODE) return;//Accept failed
-
- remote_out.write(eof);
- break;
- }
- }
- }catch(EOFException eofe){
- //System.out.println("EOF exception");
- return;//Connection closed while we were trying to accept.
- }catch(InterruptedIOException iioe){
- //Accept thread interrupted us.
- //System.out.println("Interrupted");
- if(mode != PIPE_MODE)
- return;//If accept thread was not successfull return.
- }finally{
- //System.out.println("Finnaly!");
- }
-
- if(eof < 0)//Connection closed while we were trying to accept;
- return;
-
- //Do not restore timeout, instead timeout is set on the
- //remote socket. It does not make any difference.
-
- pipe(in,remote_out);
- }
-
- private void onUDP(ProxyMessage msg) throws IOException{
- if(msg.ip.getHostAddress().equals("0.0.0.0"))
- msg.ip = sock.getInetAddress();
- log("Creating UDP relay server for "+msg.ip+":"+msg.port);
- relayServer = new UDPRelayServer(msg.ip,msg.port,
- Thread.currentThread(),sock,auth);
-
- ProxyMessage response;
-
- response = new Socks5Message(Proxy.SOCKS_SUCCESS,
- relayServer.relayIP,relayServer.relayPort);
-
- response.write(out);
-
- relayServer.start();
-
- //Make timeout infinit.
- sock.setSoTimeout(0);
- try{
- while(in.read()>=0) /*do nothing*/;
- }catch(EOFException eofe){
- }
- }
-
-//Private methods
-//////////////////
-
- private void doAccept() throws IOException{
- Socket s;
- long startTime = System.currentTimeMillis();
-
- while(true){
- s = ss.accept();
- if(s.getInetAddress().equals(msg.ip)){
- //got the connection from the right host
- //Close listenning socket.
- ss.close();
- break;
- }else if(ss instanceof SocksServerSocket){
- //We can't accept more then one connection
- s.close();
- ss.close();
- throw new SocksException(Proxy.SOCKS_FAILURE);
- }else{
- if(acceptTimeout!=0){ //If timeout is not infinit
- int newTimeout = acceptTimeout-(int)(System.currentTimeMillis()-
- startTime);
- if(newTimeout <= 0) throw new InterruptedIOException(
- "In doAccept()");
- ss.setSoTimeout(newTimeout);
- }
- s.close(); //Drop all connections from other hosts
- }
- }
-
- //Accepted connection
- remote_sock = s;
- remote_in = s.getInputStream();
- remote_out = s.getOutputStream();
-
- //Set timeout
- remote_sock.setSoTimeout(iddleTimeout);
-
- log("Accepted from "+s.getInetAddress()+":"+s.getPort());
-
- ProxyMessage response;
-
- if(msg.version == 5)
- response = new Socks5Message(Proxy.SOCKS_SUCCESS, s.getInetAddress(),
- s.getPort());
- else
- response = new Socks4Message(Socks4Message.REPLY_OK,
- s.getInetAddress(), s.getPort());
- response.write(out);
- }
-
- protected ProxyMessage readMsg(InputStream in) throws IOException{
- PushbackInputStream push_in;
- if(in instanceof PushbackInputStream)
- push_in = (PushbackInputStream) in;
- else
- push_in = new PushbackInputStream(in);
-
- int version = push_in.read();
- push_in.unread(version);
-
-
- ProxyMessage msg;
-
- if(version == 5){
- msg = new Socks5Message(push_in,false);
- }else if(version == 4){
- msg = new Socks4Message(push_in,false);
- }else{
- throw new SocksException(Proxy.SOCKS_FAILURE);
- }
- return msg;
- }
-
- private void startPipe(Socket s){
- mode = PIPE_MODE;
- remote_sock = s;
- try{
- remote_in = s.getInputStream();
- remote_out = s.getOutputStream();
- pipe_thread1 = Thread.currentThread();
- pipe_thread2 = new Thread(this);
- pipe_thread2.start();
- pipe(in,remote_out);
- }catch(IOException ioe){
- }
- }
-
- private void sendErrorMessage(int error_code){
- ProxyMessage err_msg;
- if(msg instanceof Socks4Message)
- err_msg = new Socks4Message(Socks4Message.REPLY_REJECTED);
- else
- err_msg = new Socks5Message(error_code);
- try{
- err_msg.write(out);
- }catch(IOException ioe){}
- }
-
- private synchronized void abort(){
- if(mode == ABORT_MODE) return;
- mode = ABORT_MODE;
- try{
- log("Aborting operation");
- if(remote_sock != null) remote_sock.close();
- if(sock != null) sock.close();
- if(relayServer!=null) relayServer.stop();
- if(ss!=null) ss.close();
- if(pipe_thread1 != null) pipe_thread1.interrupt();
- if(pipe_thread2 != null) pipe_thread2.interrupt();
- }catch(IOException ioe){}
- }
-
- static final void log(String s){
- if(log != null){
- log.println(s);
- log.flush();
- }
- }
-
- static final void log(ProxyMessage msg){
- log("Request version:"+msg.version+
- "\tCommand: "+command2String(msg.command));
- log("IP:"+msg.ip +"\tPort:"+msg.port+
- (msg.version==4?"\tUser:"+msg.user:""));
- }
-
- private void pipe(InputStream in,OutputStream out) throws IOException{
- lastReadTime = System.currentTimeMillis();
- byte[] buf = new byte[BUF_SIZE];
- int len = 0;
- while(len >= 0){
- try{
- if(len!=0){
- out.write(buf,0,len);
- out.flush();
- }
- len= in.read(buf);
- lastReadTime = System.currentTimeMillis();
- }catch(InterruptedIOException iioe){
- if(iddleTimeout == 0) return;//Other thread interrupted us.
- long timeSinceRead = System.currentTimeMillis() - lastReadTime;
- if(timeSinceRead >= iddleTimeout - 1000) //-1s for adjustment.
- return;
- len = 0;
-
- }
- }
- }
- static final String command_names[] = {"CONNECT","BIND","UDP_ASSOCIATE"};
-
- static final String command2String(int cmd){
- if(cmd > 0 && cmd < 4) return command_names[cmd-1];
- else return "Unknown Command "+cmd;
- }
-}
+package net.sourceforge.jsocks;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.PushbackInputStream;
+import java.net.ConnectException;
+import java.net.InetAddress;
+import java.net.NoRouteToHostException;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+import net.sourceforge.jsocks.server.ServerAuthenticator;
+
+/**
+ SOCKS4 and SOCKS5 proxy, handles both protocols simultaniously.
+ Implements all SOCKS commands, including UDP relaying.
+ <p>
+ In order to use it you will need to implement ServerAuthenticator
+ interface. There is an implementation of this interface which does
+ no authentication ServerAuthenticatorNone, but it is very dangerous
+ to use, as it will give access to your local network to anybody
+ in the world. One should never use this authentication scheme unless
+ one have pretty good reason to do so.
+ There is a couple of other authentication schemes in socks.server package.
+ @see socks.server.ServerAuthenticator
+*/
+public class ProxyServer implements Runnable{
+
+ ServerAuthenticator auth;
+ ProxyMessage msg = null;
+
+ Socket sock=null,remote_sock=null;
+ ServerSocket ss=null;
+ UDPRelayServer relayServer = null;
+ InputStream in,remote_in;
+ OutputStream out,remote_out;
+
+ int mode;
+ static final int START_MODE = 0;
+ static final int ACCEPT_MODE = 1;
+ static final int PIPE_MODE = 2;
+ static final int ABORT_MODE = 3;
+
+ static final int BUF_SIZE = 8192;
+
+ Thread pipe_thread1,pipe_thread2;
+ long lastReadTime;
+
+ protected static int iddleTimeout = 180000; //3 minutes
+ static int acceptTimeout = 180000; //3 minutes
+
+ static PrintStream log = null;
+ static Proxy proxy;
+
+
+//Public Constructors
+/////////////////////
+
+
+ /**
+ Creates a proxy server with given Authentication scheme.
+ @param auth Authentication scheme to be used.
+ */
+ public ProxyServer(ServerAuthenticator auth){
+ this.auth = auth;
+ }
+
+//Other constructors
+////////////////////
+
+ protected ProxyServer(ServerAuthenticator auth,Socket s){
+ this.auth = auth;
+ this.sock = s;
+ mode = START_MODE;
+ }
+
+//Public methods
+/////////////////
+
+ /**
+ Set the logging stream. Specifying null disables logging.
+ */
+ public static void setLog(OutputStream out){
+ if(out == null){
+ log = null;
+ }else{
+ log = new PrintStream(out,true);
+ }
+
+ UDPRelayServer.log = log;
+ }
+
+ /**
+ Set proxy.
+ <p>
+ Allows Proxy chaining so that one Proxy server is connected to another
+ and so on. If proxy supports SOCKSv4, then only some SOCKSv5 requests
+ can be handled, UDP would not work, however CONNECT and BIND will be
+ translated.
+
+ @param p Proxy which should be used to handle user requests.
+ */
+ public static void setProxy(Proxy p){
+ proxy =p;
+ UDPRelayServer.proxy = proxy;
+ }
+
+ /**
+ Get proxy.
+ @return Proxy wich is used to handle user requests.
+ */
+ public static Proxy getProxy(){
+ return proxy;
+ }
+
+ /**
+ Sets the timeout for connections, how long shoud server wait
+ for data to arrive before dropping the connection.<br>
+ Zero timeout implies infinity.<br>
+ Default timeout is 3 minutes.
+ */
+ public static void setIddleTimeout(int timeout){
+ iddleTimeout = timeout;
+ }
+ /**
+ Sets the timeout for BIND command, how long the server should
+ wait for the incoming connection.<br>
+ Zero timeout implies infinity.<br>
+ Default timeout is 3 minutes.
+ */
+ public static void setAcceptTimeout(int timeout){
+ acceptTimeout = timeout;
+ }
+
+ /**
+ Sets the timeout for UDPRelay server.<br>
+ Zero timeout implies infinity.<br>
+ Default timeout is 3 minutes.
+ */
+ public static void setUDPTimeout(int timeout){
+ UDPRelayServer.setTimeout(timeout);
+ }
+
+ /**
+ Sets the size of the datagrams used in the UDPRelayServer.<br>
+ Default size is 64K, a bit more than maximum possible size of the
+ datagram.
+ */
+ public static void setDatagramSize(int size){
+ UDPRelayServer.setDatagramSize(size);
+ }
+
+
+ /**
+ Start the Proxy server at given port.<br>
+ This methods blocks.
+ */
+ public void start(int port){
+ start(port,5,null);
+ }
+
+ /**
+ Create a server with the specified port, listen backlog, and local
+ IP address to bind to. The localIP argument can be used on a multi-homed
+ host for a ServerSocket that will only accept connect requests to one of
+ its addresses. If localIP is null, it will default accepting connections
+ on any/all local addresses. The port must be between 0 and 65535,
+ inclusive. <br>
+ This methods blocks.
+ */
+ public void start(int port,int backlog,InetAddress localIP){
+ try{
+ ss = new ServerSocket(port,backlog,localIP);
+ log("Starting SOCKS Proxy on:"+ss.getInetAddress().getHostAddress()+":"
+ +ss.getLocalPort());
+ while(true){
+ Socket s = ss.accept();
+ log("Accepted from:"+s.getInetAddress().getHostName()+":"
+ +s.getPort());
+ ProxyServer ps = new ProxyServer(auth,s);
+ (new Thread(ps)).start();
+ }
+ }catch(IOException ioe){
+ ioe.printStackTrace();
+ }finally{
+ }
+ }
+
+ /**
+ Stop server operation.It would be wise to interrupt thread running the
+ server afterwards.
+ */
+ public void stop(){
+ try{
+ if(ss != null) ss.close();
+ }catch(IOException ioe){
+ }
+ }
+
+//Runnable interface
+////////////////////
+ public void run(){
+ switch(mode){
+ case START_MODE:
+ try{
+ startSession();
+ }catch(IOException ioe){
+ handleException(ioe);
+ //ioe.printStackTrace();
+ }finally{
+ abort();
+ if(auth!=null) auth.endSession();
+ log("Main thread(client->remote)stopped.");
+ }
+ break;
+ case ACCEPT_MODE:
+ try{
+ doAccept();
+ mode = PIPE_MODE;
+ pipe_thread1.interrupt(); //Tell other thread that connection have
+ //been accepted.
+ pipe(remote_in,out);
+ }catch(IOException ioe){
+ //log("Accept exception:"+ioe);
+ handleException(ioe);
+ }finally{
+ abort();
+ log("Accept thread(remote->client) stopped");
+ }
+ break;
+ case PIPE_MODE:
+ try{
+ pipe(remote_in,out);
+ }catch(IOException ioe){
+ }finally{
+ abort();
+ log("Support thread(remote->client) stopped");
+ }
+ break;
+ case ABORT_MODE:
+ break;
+ default:
+ log("Unexpected MODE "+mode);
+ }
+ }
+
+//Private methods
+/////////////////
+ private void startSession() throws IOException{
+ sock.setSoTimeout(iddleTimeout);
+
+ try{
+ auth = auth.startSession(sock);
+ }catch(IOException ioe){
+ log("Auth throwed exception:"+ioe);
+ auth = null;
+ return;
+ }
+
+ if(auth == null){ //Authentication failed
+ log("Authentication failed");
+ return;
+ }
+
+ in = auth.getInputStream();
+ out = auth.getOutputStream();
+
+ msg = readMsg(in);
+ handleRequest(msg);
+ }
+
+ protected void handleRequest(ProxyMessage msg)
+ throws IOException{
+ if(!auth.checkRequest(msg)) throw new
+ SocksException(Proxy.SOCKS_FAILURE);
+
+ if(msg.ip == null){
+ if(msg instanceof Socks5Message){
+ msg.ip = InetAddress.getByName(msg.host);
+ }else
+ throw new SocksException(Proxy.SOCKS_FAILURE);
+ }
+ log(msg);
+
+ switch(msg.command){
+ case Proxy.SOCKS_CMD_CONNECT:
+ onConnect(msg);
+ break;
+ case Proxy.SOCKS_CMD_BIND:
+ onBind(msg);
+ break;
+ case Proxy.SOCKS_CMD_UDP_ASSOCIATE:
+ onUDP(msg);
+ break;
+ default:
+ throw new SocksException(Proxy.SOCKS_CMD_NOT_SUPPORTED);
+ }
+ }
+
+ private void handleException(IOException ioe){
+ //If we couldn't read the request, return;
+ if(msg == null) return;
+ //If have been aborted by other thread
+ if(mode == ABORT_MODE) return;
+ //If the request was successfully completed, but exception happened later
+ if(mode == PIPE_MODE) return;
+
+ int error_code = Proxy.SOCKS_FAILURE;
+
+ if(ioe instanceof SocksException)
+ error_code = ((SocksException)ioe).errCode;
+ else if(ioe instanceof NoRouteToHostException)
+ error_code = Proxy.SOCKS_HOST_UNREACHABLE;
+ else if(ioe instanceof ConnectException)
+ error_code = Proxy.SOCKS_CONNECTION_REFUSED;
+ else if(ioe instanceof InterruptedIOException)
+ error_code = Proxy.SOCKS_TTL_EXPIRE;
+
+ if(error_code > Proxy.SOCKS_ADDR_NOT_SUPPORTED || error_code < 0){
+ error_code = Proxy.SOCKS_FAILURE;
+ }
+
+ sendErrorMessage(error_code);
+ }
+
+ private void onConnect(ProxyMessage msg) throws IOException{
+ Socket s;
+ ProxyMessage response = null;
+
+ s = new Socket(msg.ip,msg.port);
+
+ log("Connected to "+s.getInetAddress()+":"+s.getPort());
+
+ if(msg instanceof Socks5Message){
+ response = new Socks5Message(Proxy.SOCKS_SUCCESS,
+ s.getLocalAddress(),
+ s.getLocalPort());
+ }else{
+ response = new Socks4Message(Socks4Message.REPLY_OK,
+ s.getLocalAddress(),s.getLocalPort());
+
+ }
+ response.write(out);
+ startPipe(s);
+ }
+
+ private void onBind(ProxyMessage msg) throws IOException{
+ ProxyMessage response = null;
+
+ if(proxy == null)
+ ss = new ServerSocket(0);
+ else
+ ss = new SocksServerSocket(proxy, msg.ip, msg.port);
+
+ ss.setSoTimeout(acceptTimeout);
+
+ log("Trying accept on "+ss.getInetAddress()+":"+ss.getLocalPort());
+
+ if(msg.version == 5)
+ response = new Socks5Message(Proxy.SOCKS_SUCCESS,ss.getInetAddress(),
+ ss.getLocalPort());
+ else
+ response = new Socks4Message(Socks4Message.REPLY_OK,
+ ss.getInetAddress(),
+ ss.getLocalPort());
+ response.write(out);
+
+ mode = ACCEPT_MODE;
+
+ pipe_thread1 = Thread.currentThread();
+ pipe_thread2 = new Thread(this);
+ pipe_thread2.start();
+
+ //Make timeout infinit.
+ sock.setSoTimeout(0);
+ int eof=0;
+
+ try{
+ while((eof=in.read())>=0){
+ if(mode != ACCEPT_MODE){
+ if(mode != PIPE_MODE) return;//Accept failed
+
+ remote_out.write(eof);
+ break;
+ }
+ }
+ }catch(EOFException eofe){
+ //System.out.println("EOF exception");
+ return;//Connection closed while we were trying to accept.
+ }catch(InterruptedIOException iioe){
+ //Accept thread interrupted us.
+ //System.out.println("Interrupted");
+ if(mode != PIPE_MODE)
+ return;//If accept thread was not successfull return.
+ }finally{
+ //System.out.println("Finnaly!");
+ }
+
+ if(eof < 0)//Connection closed while we were trying to accept;
+ return;
+
+ //Do not restore timeout, instead timeout is set on the
+ //remote socket. It does not make any difference.
+
+ pipe(in,remote_out);
+ }
+
+ private void onUDP(ProxyMessage msg) throws IOException{
+ if(msg.ip.getHostAddress().equals("0.0.0.0"))
+ msg.ip = sock.getInetAddress();
+ log("Creating UDP relay server for "+msg.ip+":"+msg.port);
+ relayServer = new UDPRelayServer(msg.ip,msg.port,
+ Thread.currentThread(),sock,auth);
+
+ ProxyMessage response;
+
+ response = new Socks5Message(Proxy.SOCKS_SUCCESS,
+ relayServer.relayIP,relayServer.relayPort);
+
+ response.write(out);
+
+ relayServer.start();
+
+ //Make timeout infinit.
+ sock.setSoTimeout(0);
+ try{
+ while(in.read()>=0) /*do nothing*/;
+ }catch(EOFException eofe){
+ }
+ }
+
+//Private methods
+//////////////////
+
+ private void doAccept() throws IOException{
+ Socket s;
+ long startTime = System.currentTimeMillis();
+
+ while(true){
+ s = ss.accept();
+ if(s.getInetAddress().equals(msg.ip)){
+ //got the connection from the right host
+ //Close listenning socket.
+ ss.close();
+ break;
+ }else if(ss instanceof SocksServerSocket){
+ //We can't accept more then one connection
+ s.close();
+ ss.close();
+ throw new SocksException(Proxy.SOCKS_FAILURE);
+ }else{
+ if(acceptTimeout!=0){ //If timeout is not infinit
+ int newTimeout = acceptTimeout-(int)(System.currentTimeMillis()-
+ startTime);
+ if(newTimeout <= 0) throw new InterruptedIOException(
+ "In doAccept()");
+ ss.setSoTimeout(newTimeout);
+ }
+ s.close(); //Drop all connections from other hosts
+ }
+ }
+
+ //Accepted connection
+ remote_sock = s;
+ remote_in = s.getInputStream();
+ remote_out = s.getOutputStream();
+
+ //Set timeout
+ remote_sock.setSoTimeout(iddleTimeout);
+
+ log("Accepted from "+s.getInetAddress()+":"+s.getPort());
+
+ ProxyMessage response;
+
+ if(msg.version == 5)
+ response = new Socks5Message(Proxy.SOCKS_SUCCESS, s.getInetAddress(),
+ s.getPort());
+ else
+ response = new Socks4Message(Socks4Message.REPLY_OK,
+ s.getInetAddress(), s.getPort());
+ response.write(out);
+ }
+
+ protected ProxyMessage readMsg(InputStream in) throws IOException{
+ PushbackInputStream push_in;
+ if(in instanceof PushbackInputStream)
+ push_in = (PushbackInputStream) in;
+ else
+ push_in = new PushbackInputStream(in);
+
+ int version = push_in.read();
+ push_in.unread(version);
+
+
+ ProxyMessage msg;
+
+ if(version == 5){
+ msg = new Socks5Message(push_in,false);
+ }else if(version == 4){
+ msg = new Socks4Message(push_in,false);
+ }else{
+ throw new SocksException(Proxy.SOCKS_FAILURE);
+ }
+ return msg;
+ }
+
+ private void startPipe(Socket s){
+ mode = PIPE_MODE;
+ remote_sock = s;
+ try{
+ remote_in = s.getInputStream();
+ remote_out = s.getOutputStream();
+ pipe_thread1 = Thread.currentThread();
+ pipe_thread2 = new Thread(this);
+ pipe_thread2.start();
+ pipe(in,remote_out);
+ }catch(IOException ioe){
+ }
+ }
+
+ private void sendErrorMessage(int error_code){
+ ProxyMessage err_msg;
+ if(msg instanceof Socks4Message)
+ err_msg = new Socks4Message(Socks4Message.REPLY_REJECTED);
+ else
+ err_msg = new Socks5Message(error_code);
+ try{
+ err_msg.write(out);
+ }catch(IOException ioe){}
+ }
+
+ private synchronized void abort(){
+ if(mode == ABORT_MODE) return;
+ mode = ABORT_MODE;
+ try{
+ log("Aborting operation");
+ if(remote_sock != null) remote_sock.close();
+ if(sock != null) sock.close();
+ if(relayServer!=null) relayServer.stop();
+ if(ss!=null) ss.close();
+ if(pipe_thread1 != null) pipe_thread1.interrupt();
+ if(pipe_thread2 != null) pipe_thread2.interrupt();
+ }catch(IOException ioe){}
+ }
+
+ static final void log(String s){
+ if(log != null){
+ log.println(s);
+ log.flush();
+ }
+ }
+
+ static final void log(ProxyMessage msg){
+ log("Request version:"+msg.version+
+ "\tCommand: "+command2String(msg.command));
+ log("IP:"+msg.ip +"\tPort:"+msg.port+
+ (msg.version==4?"\tUser:"+msg.user:""));
+ }
+
+ private void pipe(InputStream in,OutputStream out) throws IOException{
+ lastReadTime = System.currentTimeMillis();
+ byte[] buf = new byte[BUF_SIZE];
+ int len = 0;
+ while(len >= 0){
+ try{
+ if(len!=0){
+ out.write(buf,0,len);
+ out.flush();
+ }
+ len= in.read(buf);
+ lastReadTime = System.currentTimeMillis();
+ }catch(InterruptedIOException iioe){
+ if(iddleTimeout == 0) return;//Other thread interrupted us.
+ long timeSinceRead = System.currentTimeMillis() - lastReadTime;
+ if(timeSinceRead >= iddleTimeout - 1000) //-1s for adjustment.
+ return;
+ len = 0;
+
+ }
+ }
+ }
+ static final String command_names[] = {"CONNECT","BIND","UDP_ASSOCIATE"};
+
+ static final String command2String(int cmd){
+ if(cmd > 0 && cmd < 4) return command_names[cmd-1];
+ else return "Unknown Command "+cmd;
+ }
+}
diff --git a/src/net/sourceforge/jsocks/Socks4Message.java b/src/net/sourceforge/jsocks/Socks4Message.java
index 1d68cb7..99fb211 100644
--- a/src/net/sourceforge/jsocks/Socks4Message.java
+++ b/src/net/sourceforge/jsocks/Socks4Message.java
@@ -1,171 +1,171 @@
-package net.sourceforge.jsocks;
-import java.io.DataInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-/**
- SOCKS4 Reply/Request message.
-*/
-
-public class Socks4Message extends ProxyMessage{
-
- private byte[] msgBytes;
- private int msgLength;
-
- /**
- * Server failed reply, cmd command for failed request
- */
- public Socks4Message(int cmd){
- super(cmd,null,0);
- this.user = null;
-
- msgLength = 2;
- msgBytes = new byte[2];
-
- msgBytes[0] = (byte) 0;
- msgBytes[1] = (byte) command;
- }
-
- /**
- * Server successfull reply
- */
- public Socks4Message(int cmd,InetAddress ip,int port){
- this(0,cmd,ip,port,null);
- }
-
- /**
- * Client request
- */
- public Socks4Message(int cmd,InetAddress ip,int port,String user){
- this(SOCKS_VERSION,cmd,ip,port,user);
- }
-
- /**
- * Most general constructor
- */
- public Socks4Message(int version, int cmd,
- InetAddress ip,int port,String user){
- super(cmd,ip,port);
- this.user = user;
- this.version = version;
-
- msgLength = user == null?8:9+user.length();
- msgBytes = new byte[msgLength];
-
- msgBytes[0] = (byte) version;
- msgBytes[1] = (byte) command;
- msgBytes[2] = (byte) (port >> 8);
- msgBytes[3] = (byte) port;
-
- byte[] addr;
-
- if(ip != null)
- addr = ip.getAddress();
- else{
- addr = new byte[4];
- addr[0]=addr[1]=addr[2]=addr[3]=0;
- }
- System.arraycopy(addr,0,msgBytes,4,4);
-
- if(user != null){
- byte[] buf = user.getBytes();
- System.arraycopy(buf,0,msgBytes,8,buf.length);
- msgBytes[msgBytes.length -1 ] = 0;
- }
- }
-
- /**
- *Initialise from the stream
- *If clientMode is true attempts to read a server response
- *otherwise reads a client request
- *see read for more detail
- */
- public Socks4Message(InputStream in, boolean clientMode) throws IOException{
- msgBytes = null;
- read(in,clientMode);
- }
-
- @Override
-public void read(InputStream in) throws IOException{
- read(in,true);
- }
-
- @Override
-public void read(InputStream in, boolean clientMode) throws IOException{
- boolean mode4a = false;
- DataInputStream d_in = new DataInputStream(in);
- version= d_in.readUnsignedByte();
- command = d_in.readUnsignedByte();
- if(clientMode && command != REPLY_OK){
- String errMsg;
- if(command >REPLY_OK && command < REPLY_BAD_IDENTD)
- errMsg = replyMessage[command-REPLY_OK];
- else
- errMsg = "Unknown Reply Code";
- throw new SocksException(command,errMsg);
- }
- port = d_in.readUnsignedShort();
- byte[] addr = new byte[4];
- d_in.readFully(addr);
- if (addr[0] == 0 && addr[1] == 0 && addr[2] == 0 && addr[3] != 0)
- mode4a = true;
- else {
- ip=bytes2IP(addr);
- host = ip.getHostName();
- }
- if(!clientMode){
- StringBuilder sb = new StringBuilder();
- int b;
- while ((b = in.read()) != 0)
- sb.append((char) b);
- user = sb.toString();
- if (mode4a) {
- sb.setLength(0);
- while ((b = in.read()) != 0)
- sb.append((char) b);
- host = sb.toString();
- }
- }
- }
- @Override
-public void write(OutputStream out) throws IOException{
- if(msgBytes == null){
- Socks4Message msg = new Socks4Message(version,command,ip,port,user);
- msgBytes = msg.msgBytes;
- msgLength = msg.msgLength;
- }
- out.write(msgBytes);
- }
-
- //Class methods
- static InetAddress bytes2IP(byte[] addr){
- String s = bytes2IPV4(addr,0);
- try{
- return InetAddress.getByName(s);
- }catch(UnknownHostException uh_ex){
- return null;
- }
- }
-
- //Constants
-
- static final String[] replyMessage ={
- "Request Granted",
- "Request Rejected or Failed",
- "Failed request, can't connect to Identd",
- "Failed request, bad user name"};
-
- static final int SOCKS_VERSION = 4;
-
- public final static int REQUEST_CONNECT = 1;
- public final static int REQUEST_BIND = 2;
-
- public final static int REPLY_OK = 90;
- public final static int REPLY_REJECTED = 91;
- public final static int REPLY_NO_CONNECT = 92;
- public final static int REPLY_BAD_IDENTD = 93;
-
-}
+package net.sourceforge.jsocks;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ SOCKS4 Reply/Request message.
+*/
+
+public class Socks4Message extends ProxyMessage{
+
+ private byte[] msgBytes;
+ private int msgLength;
+
+ /**
+ * Server failed reply, cmd command for failed request
+ */
+ public Socks4Message(int cmd){
+ super(cmd,null,0);
+ this.user = null;
+
+ msgLength = 2;
+ msgBytes = new byte[2];
+
+ msgBytes[0] = (byte) 0;
+ msgBytes[1] = (byte) command;
+ }
+
+ /**
+ * Server successfull reply
+ */
+ public Socks4Message(int cmd,InetAddress ip,int port){
+ this(0,cmd,ip,port,null);
+ }
+
+ /**
+ * Client request
+ */
+ public Socks4Message(int cmd,InetAddress ip,int port,String user){
+ this(SOCKS_VERSION,cmd,ip,port,user);
+ }
+
+ /**
+ * Most general constructor
+ */
+ public Socks4Message(int version, int cmd,
+ InetAddress ip,int port,String user){
+ super(cmd,ip,port);
+ this.user = user;
+ this.version = version;
+
+ msgLength = user == null?8:9+user.length();
+ msgBytes = new byte[msgLength];
+
+ msgBytes[0] = (byte) version;
+ msgBytes[1] = (byte) command;
+ msgBytes[2] = (byte) (port >> 8);
+ msgBytes[3] = (byte) port;
+
+ byte[] addr;
+
+ if(ip != null)
+ addr = ip.getAddress();
+ else{
+ addr = new byte[4];
+ addr[0]=addr[1]=addr[2]=addr[3]=0;
+ }
+ System.arraycopy(addr,0,msgBytes,4,4);
+
+ if(user != null){
+ byte[] buf = user.getBytes();
+ System.arraycopy(buf,0,msgBytes,8,buf.length);
+ msgBytes[msgBytes.length -1 ] = 0;
+ }
+ }
+
+ /**
+ *Initialise from the stream
+ *If clientMode is true attempts to read a server response
+ *otherwise reads a client request
+ *see read for more detail
+ */
+ public Socks4Message(InputStream in, boolean clientMode) throws IOException{
+ msgBytes = null;
+ read(in,clientMode);
+ }
+
+ @Override
+public void read(InputStream in) throws IOException{
+ read(in,true);
+ }
+
+ @Override
+public void read(InputStream in, boolean clientMode) throws IOException{
+ boolean mode4a = false;
+ DataInputStream d_in = new DataInputStream(in);
+ version= d_in.readUnsignedByte();
+ command = d_in.readUnsignedByte();
+ if(clientMode && command != REPLY_OK){
+ String errMsg;
+ if(command >REPLY_OK && command < REPLY_BAD_IDENTD)
+ errMsg = replyMessage[command-REPLY_OK];
+ else
+ errMsg = "Unknown Reply Code";
+ throw new SocksException(command,errMsg);
+ }
+ port = d_in.readUnsignedShort();
+ byte[] addr = new byte[4];
+ d_in.readFully(addr);
+ if (addr[0] == 0 && addr[1] == 0 && addr[2] == 0 && addr[3] != 0)
+ mode4a = true;
+ else {
+ ip=bytes2IP(addr);
+ host = ip.getHostName();
+ }
+ if(!clientMode){
+ StringBuilder sb = new StringBuilder();
+ int b;
+ while ((b = in.read()) != 0)
+ sb.append((char) b);
+ user = sb.toString();
+ if (mode4a) {
+ sb.setLength(0);
+ while ((b = in.read()) != 0)
+ sb.append((char) b);
+ host = sb.toString();
+ }
+ }
+ }
+ @Override
+public void write(OutputStream out) throws IOException{
+ if(msgBytes == null){
+ Socks4Message msg = new Socks4Message(version,command,ip,port,user);
+ msgBytes = msg.msgBytes;
+ msgLength = msg.msgLength;
+ }
+ out.write(msgBytes);
+ }
+
+ //Class methods
+ static InetAddress bytes2IP(byte[] addr){
+ String s = bytes2IPV4(addr,0);
+ try{
+ return InetAddress.getByName(s);
+ }catch(UnknownHostException uh_ex){
+ return null;
+ }
+ }
+
+ //Constants
+
+ static final String[] replyMessage ={
+ "Request Granted",
+ "Request Rejected or Failed",
+ "Failed request, can't connect to Identd",
+ "Failed request, bad user name"};
+
+ static final int SOCKS_VERSION = 4;
+
+ public final static int REQUEST_CONNECT = 1;
+ public final static int REQUEST_BIND = 2;
+
+ public final static int REPLY_OK = 90;
+ public final static int REPLY_REJECTED = 91;
+ public final static int REPLY_NO_CONNECT = 92;
+ public final static int REPLY_BAD_IDENTD = 93;
+
+}
diff --git a/src/net/sourceforge/jsocks/Socks4Proxy.java b/src/net/sourceforge/jsocks/Socks4Proxy.java
index f920b75..9a17fc2 100644
--- a/src/net/sourceforge/jsocks/Socks4Proxy.java
+++ b/src/net/sourceforge/jsocks/Socks4Proxy.java
@@ -1,107 +1,107 @@
-package net.sourceforge.jsocks;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-/**
- Proxy which describes SOCKS4 proxy.
-*/
-
-public class Socks4Proxy extends Proxy implements Cloneable{
-
-//Data members
- String user;
-
-//Public Constructors
-//====================
-
- /**
- Creates the SOCKS4 proxy
- @param p Proxy to use to connect to this proxy, allows proxy chaining.
- @param proxyHost Address of the proxy server.
- @param proxyPort Port of the proxy server
- @param user User name to use for identification purposes.
- @throws UnknownHostException If proxyHost can't be resolved.
- */
- public Socks4Proxy(String proxyHost,int proxyPort,String user)
- throws UnknownHostException{
- super(proxyHost,proxyPort);
- this.user = new String(user);
- version = 4;
- }
-
- /**
- Creates the SOCKS4 proxy
- @param p Proxy to use to connect to this proxy, allows proxy chaining.
- @param proxyIP Address of the proxy server.
- @param proxyPort Port of the proxy server
- @param user User name to use for identification purposes.
- */
- public Socks4Proxy(Proxy p,InetAddress proxyIP,int proxyPort,String user){
- super(p,proxyIP,proxyPort);
- this.user = new String(user);
- version = 4;
- }
-
- /**
- Creates the SOCKS4 proxy
- @param proxyIP Address of the proxy server.
- @param proxyPort Port of the proxy server
- @param user User name to use for identification purposes.
- */
- public Socks4Proxy(InetAddress proxyIP,int proxyPort,String user){
- this(null,proxyIP,proxyPort,user);
- }
-
-//Public instance methods
-//========================
-
- /**
- * Creates a clone of this proxy. Changes made to the clone should not
- * affect this object.
- */
- public Object clone(){
- Socks4Proxy newProxy = new Socks4Proxy(proxyIP,proxyPort,user);
- newProxy.chainProxy = chainProxy;
- return newProxy;
- }
-
-
-//Public Static(Class) Methods
-//==============================
-
-
-//Protected Methods
-//=================
-
- protected Proxy copy(){
- Socks4Proxy copy = new Socks4Proxy(proxyIP,proxyPort,user);
- copy.chainProxy = chainProxy;
- return copy;
- }
-
- protected ProxyMessage formMessage(int cmd,InetAddress ip,int port){
- switch(cmd){
- case SOCKS_CMD_CONNECT:
- cmd = Socks4Message.REQUEST_CONNECT;
- break;
- case SOCKS_CMD_BIND:
- cmd = Socks4Message.REQUEST_BIND;
- break;
- default:
- return null;
- }
- return new Socks4Message(cmd,ip,port,user);
- }
- protected ProxyMessage formMessage(int cmd,String host,int port)
- throws UnknownHostException{
- return formMessage(cmd,InetAddress.getByName(host),port);
- }
- protected ProxyMessage formMessage(InputStream in)
- throws SocksException,
- IOException{
- return new Socks4Message(in,true);
- }
-
-}
+package net.sourceforge.jsocks;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ Proxy which describes SOCKS4 proxy.
+*/
+
+public class Socks4Proxy extends Proxy implements Cloneable{
+
+//Data members
+ String user;
+
+//Public Constructors
+//====================
+
+ /**
+ Creates the SOCKS4 proxy
+ @param p Proxy to use to connect to this proxy, allows proxy chaining.
+ @param proxyHost Address of the proxy server.
+ @param proxyPort Port of the proxy server
+ @param user User name to use for identification purposes.
+ @throws UnknownHostException If proxyHost can't be resolved.
+ */
+ public Socks4Proxy(String proxyHost,int proxyPort,String user)
+ throws UnknownHostException{
+ super(proxyHost,proxyPort);
+ this.user = new String(user);
+ version = 4;
+ }
+
+ /**
+ Creates the SOCKS4 proxy
+ @param p Proxy to use to connect to this proxy, allows proxy chaining.
+ @param proxyIP Address of the proxy server.
+ @param proxyPort Port of the proxy server
+ @param user User name to use for identification purposes.
+ */
+ public Socks4Proxy(Proxy p,InetAddress proxyIP,int proxyPort,String user){
+ super(p,proxyIP,proxyPort);
+ this.user = new String(user);
+ version = 4;
+ }
+
+ /**
+ Creates the SOCKS4 proxy
+ @param proxyIP Address of the proxy server.
+ @param proxyPort Port of the proxy server
+ @param user User name to use for identification purposes.
+ */
+ public Socks4Proxy(InetAddress proxyIP,int proxyPort,String user){
+ this(null,proxyIP,proxyPort,user);
+ }
+
+//Public instance methods
+//========================
+
+ /**
+ * Creates a clone of this proxy. Changes made to the clone should not
+ * affect this object.
+ */
+ public Object clone(){
+ Socks4Proxy newProxy = new Socks4Proxy(proxyIP,proxyPort,user);
+ newProxy.chainProxy = chainProxy;
+ return newProxy;
+ }
+
+
+//Public Static(Class) Methods
+//==============================
+
+
+//Protected Methods
+//=================
+
+ protected Proxy copy(){
+ Socks4Proxy copy = new Socks4Proxy(proxyIP,proxyPort,user);
+ copy.chainProxy = chainProxy;
+ return copy;
+ }
+
+ protected ProxyMessage formMessage(int cmd,InetAddress ip,int port){
+ switch(cmd){
+ case SOCKS_CMD_CONNECT:
+ cmd = Socks4Message.REQUEST_CONNECT;
+ break;
+ case SOCKS_CMD_BIND:
+ cmd = Socks4Message.REQUEST_BIND;
+ break;
+ default:
+ return null;
+ }
+ return new Socks4Message(cmd,ip,port,user);
+ }
+ protected ProxyMessage formMessage(int cmd,String host,int port)
+ throws UnknownHostException{
+ return formMessage(cmd,InetAddress.getByName(host),port);
+ }
+ protected ProxyMessage formMessage(InputStream in)
+ throws SocksException,
+ IOException{
+ return new Socks4Message(in,true);
+ }
+
+}
diff --git a/src/net/sourceforge/jsocks/Socks5DatagramSocket.java b/src/net/sourceforge/jsocks/Socks5DatagramSocket.java
index bb73ccd..b847400 100644
--- a/src/net/sourceforge/jsocks/Socks5DatagramSocket.java
+++ b/src/net/sourceforge/jsocks/Socks5DatagramSocket.java
@@ -1,460 +1,460 @@
-package net.sourceforge.jsocks;
-import java.net.*;
-import java.io.*;
-
-/**
- Datagram socket to interract through the firewall.<BR>
- Can be used same way as the normal DatagramSocket. One should
- be carefull though with the datagram sizes used, as additional data
- is present in both incomming and outgoing datagrams.
- <p>
- SOCKS5 protocol allows to send host address as either:
- <ul>
- <li> IPV4, normal 4 byte address. (10 bytes header size)
- <li> IPV6, version 6 ip address (not supported by Java as for now).
- 22 bytes header size.
- <li> Host name,(7+length of the host name bytes header size).
- </ul>
- As with other Socks equivalents, direct addresses are handled
- transparently, that is data will be send directly when required
- by the proxy settings.
- <p>
- <b>NOTE:</b><br>
- Unlike other SOCKS Sockets, it <b>does not</b> support proxy chaining,
- and will throw an exception if proxy has a chain proxy attached. The
- reason for that is not my laziness, but rather the restrictions of
- the SOCKSv5 protocol. Basicaly SOCKSv5 proxy server, needs to know from
- which host:port datagrams will be send for association, and returns address
- to which datagrams should be send by the client, but it does not
- inform client from which host:port it is going to send datagrams, in fact
- there is even no guarantee they will be send at all and from the same address
- each time.
-
- */
-public class Socks5DatagramSocket extends DatagramSocket{
-
- InetAddress relayIP;
- int relayPort;
- Socks5Proxy proxy;
- private boolean server_mode = false;
- UDPEncapsulation encapsulation;
-
-
- /**
- Construct Datagram socket for communication over SOCKS5 proxy
- server. This constructor uses default proxy, the one set with
- Proxy.setDefaultProxy() method. If default proxy is not set or
- it is set to version4 proxy, which does not support datagram
- forwarding, throws SocksException.
-
- */
- public Socks5DatagramSocket() throws SocksException,
- IOException{
- this(Proxy.defaultProxy,0,null);
- }
- /**
- Construct Datagram socket for communication over SOCKS5 proxy
- server. And binds it to the specified local port.
- This constructor uses default proxy, the one set with
- Proxy.setDefaultProxy() method. If default proxy is not set or
- it is set to version4 proxy, which does not support datagram
- forwarding, throws SocksException.
- */
- public Socks5DatagramSocket(int port) throws SocksException,
- IOException{
- this(Proxy.defaultProxy,port,null);
- }
- /**
- Construct Datagram socket for communication over SOCKS5 proxy
- server. And binds it to the specified local port and address.
- This constructor uses default proxy, the one set with
- Proxy.setDefaultProxy() method. If default proxy is not set or
- it is set to version4 proxy, which does not support datagram
- forwarding, throws SocksException.
- */
- public Socks5DatagramSocket(int port,InetAddress ip) throws SocksException,
- IOException{
- this(Proxy.defaultProxy,port,ip);
- }
-
- /**
- Constructs datagram socket for communication over specified proxy.
- And binds it to the given local address and port. Address of null
- and port of 0, signify any availabale port/address.
- Might throw SocksException, if:
- <ol>
- <li> Given version of proxy does not support UDP_ASSOCIATE.
- <li> Proxy can't be reached.
- <li> Authorization fails.
- <li> Proxy does not want to perform udp forwarding, for any reason.
- </ol>
- Might throw IOException if binding dtagram socket to given address/port
- fails.
- See java.net.DatagramSocket for more details.
- */
- public Socks5DatagramSocket(Proxy p,int port,InetAddress ip)
- throws SocksException,
- IOException{
- super(port,ip);
- if(p == null) throw new SocksException(Proxy.SOCKS_NO_PROXY);
- if(!(p instanceof Socks5Proxy))
- throw new SocksException(-1,"Datagram Socket needs Proxy version 5");
-
- if(p.chainProxy != null)
- throw new SocksException(Proxy.SOCKS_JUST_ERROR,
- "Datagram Sockets do not support proxy chaining.");
-
- proxy =(Socks5Proxy) p.copy();
-
- ProxyMessage msg = proxy.udpAssociate(super.getLocalAddress(),
- super.getLocalPort());
- relayIP = msg.ip;
- if(relayIP.getHostAddress().equals("0.0.0.0")) relayIP = proxy.proxyIP;
- relayPort = msg.port;
-
- encapsulation = proxy.udp_encapsulation;
-
- //debug("Datagram Socket:"+getLocalAddress()+":"+getLocalPort()+"\n");
- //debug("Socks5Datagram: "+relayIP+":"+relayPort+"\n");
- }
-
- /**
- Used by UDPRelayServer.
- */
- Socks5DatagramSocket(boolean server_mode,UDPEncapsulation encapsulation,
- InetAddress relayIP,int relayPort)
- throws IOException{
- super();
- this.server_mode = server_mode;
- this.relayIP = relayIP;
- this.relayPort = relayPort;
- this.encapsulation = encapsulation;
- this.proxy = null;
- }
-
- /**
- Sends the Datagram either through the proxy or directly depending
- on current proxy settings and destination address. <BR>
-
- <B> NOTE: </B> DatagramPacket size should be at least 10 bytes less
- than the systems limit.
-
- <P>
- See documentation on java.net.DatagramSocket
- for full details on how to use this method.
- @param dp Datagram to send.
- @throws IOException If error happens with I/O.
- */
- public void send(DatagramPacket dp) throws IOException{
- //If the host should be accessed directly, send it as is.
- if(!server_mode){
- super.send(dp);
- //debug("Sending directly:");
- return;
- }
-
- byte[] head = formHeader(dp.getAddress(),dp.getPort());
- byte[] buf = new byte[head.length + dp.getLength()];
- byte[] data = dp.getData();
- //Merge head and data
- System.arraycopy(head,0,buf,0,head.length);
- //System.arraycopy(data,dp.getOffset(),buf,head.length,dp.getLength());
- System.arraycopy(data,0,buf,head.length,dp.getLength());
-
- if(encapsulation != null)
- buf = encapsulation.udpEncapsulate(buf,true);
-
- super.send(new DatagramPacket(buf,buf.length,relayIP,relayPort));
- }
- /**
- This method allows to send datagram packets with address type DOMAINNAME.
- SOCKS5 allows to specify host as names rather than ip addresses.Using
- this method one can send udp datagrams through the proxy, without having
- to know the ip address of the destination host.
- <p>
- If proxy specified for that socket has an option resolveAddrLocally set
- to true host will be resolved, and the datagram will be send with address
- type IPV4, if resolve fails, UnknownHostException is thrown.
- @param dp Datagram to send, it should contain valid port and data
- @param host Host name to which datagram should be send.
- @throws IOException If error happens with I/O, or the host can't be
- resolved when proxy settings say that hosts should be resolved locally.
- @see Socks5Proxy#resolveAddrLocally(boolean)
- */
- public void send(DatagramPacket dp, String host) throws IOException {
- dp.setAddress(InetAddress.getByName(host));
- super.send(dp);
- }
-
- /**
- * Receives udp packet. If packet have arrived from the proxy relay server,
- * it is processed and address and port of the packet are set to the
- * address and port of sending host.<BR>
- * If the packet arrived from anywhere else it is not changed.<br>
- * <B> NOTE: </B> DatagramPacket size should be at least 10 bytes bigger
- * than the largest packet you expect (this is for IPV4 addresses).
- * For hostnames and IPV6 it is even more.
- @param dp Datagram in which all relevent information will be copied.
- */
- public void receive(DatagramPacket dp) throws IOException{
- super.receive(dp);
-
- if(server_mode){
- //Drop all datagrams not from relayIP/relayPort
- int init_length = dp.getLength();
- int initTimeout = getSoTimeout();
- long startTime = System.currentTimeMillis();
-
- while(!relayIP.equals(dp.getAddress()) ||
- relayPort != dp.getPort()){
-
- //Restore datagram size
- dp.setLength(init_length);
-
- //If there is a non-infinit timeout on this socket
- //Make sure that it happens no matter how often unexpected
- //packets arrive.
- if(initTimeout != 0){
- int newTimeout = initTimeout - (int)(System.currentTimeMillis() -
- startTime);
- if(newTimeout <= 0) throw new InterruptedIOException(
- "In Socks5DatagramSocket->receive()");
- setSoTimeout(newTimeout);
- }
-
- super.receive(dp);
- }
-
- //Restore timeout settings
- if(initTimeout != 0) setSoTimeout(initTimeout);
-
- }else if(!relayIP.equals(dp.getAddress()) ||
- relayPort != dp.getPort())
- return; // Recieved direct packet
- //If the datagram is not from the relay server, return it it as is.
-
- byte[] data;
- data = dp.getData();
-
- if(encapsulation != null)
- data = encapsulation.udpEncapsulate(data,false);
-
- int offset = 0; //Java 1.1
- //int offset = dp.getOffset(); //Java 1.2
-
- ByteArrayInputStream bIn = new ByteArrayInputStream(data,offset,
- dp.getLength());
-
-
- ProxyMessage msg = new Socks5Message(bIn);
- dp.setPort(msg.port);
- dp.setAddress(msg.getInetAddress());
-
- //what wasn't read by the Message is the data
- int data_length = bIn.available();
- //Shift data to the left
- System.arraycopy(data,offset+dp.getLength()-data_length,
- data,offset,data_length);
-
-
- dp.setLength(data_length);
- }
-
- /**
- * Returns port assigned by the proxy, to which datagrams are relayed.
- * It is not the same port to which other party should send datagrams.
- @return Port assigned by socks server to which datagrams are send
- for association.
- */
- public int getLocalPort(){
- if(server_mode) return super.getLocalPort();
- return relayPort;
- }
- /**
- * Address assigned by the proxy, to which datagrams are send for relay.
- * It is not necesseraly the same address, to which other party should send
- * datagrams.
- @return Address to which datagrams are send for association.
- */
- public InetAddress getLocalAddress(){
- if(server_mode) return super.getLocalAddress();
- return relayIP;
- }
-
- /**
- * Closes datagram socket, and proxy connection.
- */
- public void close(){
- if(!server_mode) proxy.endSession();
- super.close();
- }
-
- /**
- This method checks wether proxy still runs udp forwarding service
- for this socket.
- <p>
- This methods checks wether the primary connection to proxy server
- is active. If it is, chances are that proxy continues to forward
- datagrams being send from this socket. If it was closed, most likely
- datagrams are no longer being forwarded by the server.
- <p>
- Proxy might decide to stop forwarding datagrams, in which case it
- should close primary connection. This method allows to check, wether
- this have been done.
- <p>
- You can specify timeout for which we should be checking EOF condition
- on the primary connection. Timeout is in milliseconds. Specifying 0 as
- timeout implies infinity, in which case method will block, until
- connection to proxy is closed or an error happens, and then return false.
- <p>
- One possible scenario is to call isProxyactive(0) in separate thread,
- and once it returned notify other threads about this event.
-
- @param timeout For how long this method should block, before returning.
- @return true if connection to proxy is active, false if eof or error
- condition have been encountered on the connection.
- */
- public boolean isProxyAlive(int timeout){
- if(server_mode) return false;
- if(proxy != null){
- try{
- proxy.proxySocket.setSoTimeout(timeout);
-
- int eof = proxy.in.read();
- if(eof < 0) return false; // EOF encountered.
- else return true; // This really should not happen
-
- }catch(InterruptedIOException iioe){
- return true; // read timed out.
- }catch(IOException ioe){
- return false;
- }
- }
- return false;
- }
-
-//PRIVATE METHODS
-//////////////////
-
-
- private byte[] formHeader(InetAddress ip, int port){
- Socks5Message request = new Socks5Message(0,ip,port);
- request.data[0] = 0;
- return request.data;
- }
-
-
-/*======================================================================
-
-//Mainly Test functions
-//////////////////////
-
- private String bytes2String(byte[] b){
- String s="";
- char[] hex_digit = { '0','1','2','3','4','5','6','7','8','9',
- 'A','B','C','D','E','F'};
- for(int i=0;i<b.length;++i){
- int i1 = (b[i] & 0xF0) >> 4;
- int i2 = b[i] & 0xF;
- s+=hex_digit[i1];
- s+=hex_digit[i2];
- s+=" ";
- }
- return s;
- }
- private static final void debug(String s){
- if(DEBUG)
- System.out.print(s);
- }
-
- private static final boolean DEBUG = true;
-
-
- public static void usage(){
- System.err.print(
- "Usage: java Socks.SocksDatagramSocket host port [socksHost socksPort]\n");
- }
-
- static final int defaultProxyPort = 1080; //Default Port
- static final String defaultProxyHost = "www-proxy"; //Default proxy
-
- public static void main(String args[]){
- int port;
- String host;
- int proxyPort;
- String proxyHost;
- InetAddress ip;
-
- if(args.length > 1 && args.length < 5){
- try{
-
- host = args[0];
- port = Integer.parseInt(args[1]);
-
- proxyPort =(args.length > 3)? Integer.parseInt(args[3])
- : defaultProxyPort;
-
- host = args[0];
- ip = InetAddress.getByName(host);
-
- proxyHost =(args.length > 2)? args[2]
- : defaultProxyHost;
-
- Proxy.setDefaultProxy(proxyHost,proxyPort);
- Proxy p = Proxy.getDefaultProxy();
- p.addDirect("lux");
-
-
- DatagramSocket ds = new Socks5DatagramSocket();
-
-
- BufferedReader in = new BufferedReader(
- new InputStreamReader(System.in));
- String s;
-
- System.out.print("Enter line:");
- s = in.readLine();
-
- while(s != null){
- byte[] data = (s+"\r\n").getBytes();
- DatagramPacket dp = new DatagramPacket(data,0,data.length,
- ip,port);
- System.out.println("Sending to: "+ip+":"+port);
- ds.send(dp);
- dp = new DatagramPacket(new byte[1024],1024);
-
- System.out.println("Trying to recieve on port:"+
- ds.getLocalPort());
- ds.receive(dp);
- System.out.print("Recieved:\n"+
- "From:"+dp.getAddress()+":"+dp.getPort()+
- "\n\n"+
- new String(dp.getData(),dp.getOffset(),dp.getLength())+"\n"
- );
- System.out.print("Enter line:");
- s = in.readLine();
-
- }
- ds.close();
- System.exit(1);
-
- }catch(SocksException s_ex){
- System.err.println("SocksException:"+s_ex);
- s_ex.printStackTrace();
- System.exit(1);
- }catch(IOException io_ex){
- io_ex.printStackTrace();
- System.exit(1);
- }catch(NumberFormatException num_ex){
- usage();
- num_ex.printStackTrace();
- System.exit(1);
- }
-
- }else{
- usage();
- }
- }
-*/
-
-}
+package net.sourceforge.jsocks;
+import java.net.*;
+import java.io.*;
+
+/**
+ Datagram socket to interract through the firewall.<BR>
+ Can be used same way as the normal DatagramSocket. One should
+ be carefull though with the datagram sizes used, as additional data
+ is present in both incomming and outgoing datagrams.
+ <p>
+ SOCKS5 protocol allows to send host address as either:
+ <ul>
+ <li> IPV4, normal 4 byte address. (10 bytes header size)
+ <li> IPV6, version 6 ip address (not supported by Java as for now).
+ 22 bytes header size.
+ <li> Host name,(7+length of the host name bytes header size).
+ </ul>
+ As with other Socks equivalents, direct addresses are handled
+ transparently, that is data will be send directly when required
+ by the proxy settings.
+ <p>
+ <b>NOTE:</b><br>
+ Unlike other SOCKS Sockets, it <b>does not</b> support proxy chaining,
+ and will throw an exception if proxy has a chain proxy attached. The
+ reason for that is not my laziness, but rather the restrictions of
+ the SOCKSv5 protocol. Basicaly SOCKSv5 proxy server, needs to know from
+ which host:port datagrams will be send for association, and returns address
+ to which datagrams should be send by the client, but it does not
+ inform client from which host:port it is going to send datagrams, in fact
+ there is even no guarantee they will be send at all and from the same address
+ each time.
+
+ */
+public class Socks5DatagramSocket extends DatagramSocket{
+
+ InetAddress relayIP;
+ int relayPort;
+ Socks5Proxy proxy;
+ private boolean server_mode = false;
+ UDPEncapsulation encapsulation;
+
+
+ /**
+ Construct Datagram socket for communication over SOCKS5 proxy
+ server. This constructor uses default proxy, the one set with
+ Proxy.setDefaultProxy() method. If default proxy is not set or
+ it is set to version4 proxy, which does not support datagram
+ forwarding, throws SocksException.
+
+ */
+ public Socks5DatagramSocket() throws SocksException,
+ IOException{
+ this(Proxy.defaultProxy,0,null);
+ }
+ /**
+ Construct Datagram socket for communication over SOCKS5 proxy
+ server. And binds it to the specified local port.
+ This constructor uses default proxy, the one set with
+ Proxy.setDefaultProxy() method. If default proxy is not set or
+ it is set to version4 proxy, which does not support datagram
+ forwarding, throws SocksException.
+ */
+ public Socks5DatagramSocket(int port) throws SocksException,
+ IOException{
+ this(Proxy.defaultProxy,port,null);
+ }
+ /**
+ Construct Datagram socket for communication over SOCKS5 proxy
+ server. And binds it to the specified local port and address.
+ This constructor uses default proxy, the one set with
+ Proxy.setDefaultProxy() method. If default proxy is not set or
+ it is set to version4 proxy, which does not support datagram
+ forwarding, throws SocksException.
+ */
+ public Socks5DatagramSocket(int port,InetAddress ip) throws SocksException,
+ IOException{
+ this(Proxy.defaultProxy,port,ip);
+ }
+
+ /**
+ Constructs datagram socket for communication over specified proxy.
+ And binds it to the given local address and port. Address of null
+ and port of 0, signify any availabale port/address.
+ Might throw SocksException, if:
+ <ol>
+ <li> Given version of proxy does not support UDP_ASSOCIATE.
+ <li> Proxy can't be reached.
+ <li> Authorization fails.
+ <li> Proxy does not want to perform udp forwarding, for any reason.
+ </ol>
+ Might throw IOException if binding dtagram socket to given address/port
+ fails.
+ See java.net.DatagramSocket for more details.
+ */
+ public Socks5DatagramSocket(Proxy p,int port,InetAddress ip)
+ throws SocksException,
+ IOException{
+ super(port,ip);
+ if(p == null) throw new SocksException(Proxy.SOCKS_NO_PROXY);
+ if(!(p instanceof Socks5Proxy))
+ throw new SocksException(-1,"Datagram Socket needs Proxy version 5");
+
+ if(p.chainProxy != null)
+ throw new SocksException(Proxy.SOCKS_JUST_ERROR,
+ "Datagram Sockets do not support proxy chaining.");
+
+ proxy =(Socks5Proxy) p.copy();
+
+ ProxyMessage msg = proxy.udpAssociate(super.getLocalAddress(),
+ super.getLocalPort());
+ relayIP = msg.ip;
+ if(relayIP.getHostAddress().equals("0.0.0.0")) relayIP = proxy.proxyIP;
+ relayPort = msg.port;
+
+ encapsulation = proxy.udp_encapsulation;
+
+ //debug("Datagram Socket:"+getLocalAddress()+":"+getLocalPort()+"\n");
+ //debug("Socks5Datagram: "+relayIP+":"+relayPort+"\n");
+ }
+
+ /**
+ Used by UDPRelayServer.
+ */
+ Socks5DatagramSocket(boolean server_mode,UDPEncapsulation encapsulation,
+ InetAddress relayIP,int relayPort)
+ throws IOException{
+ super();
+ this.server_mode = server_mode;
+ this.relayIP = relayIP;
+ this.relayPort = relayPort;
+ this.encapsulation = encapsulation;
+ this.proxy = null;
+ }
+
+ /**
+ Sends the Datagram either through the proxy or directly depending
+ on current proxy settings and destination address. <BR>
+
+ <B> NOTE: </B> DatagramPacket size should be at least 10 bytes less
+ than the systems limit.
+
+ <P>
+ See documentation on java.net.DatagramSocket
+ for full details on how to use this method.
+ @param dp Datagram to send.
+ @throws IOException If error happens with I/O.
+ */
+ public void send(DatagramPacket dp) throws IOException{
+ //If the host should be accessed directly, send it as is.
+ if(!server_mode){
+ super.send(dp);
+ //debug("Sending directly:");
+ return;
+ }
+
+ byte[] head = formHeader(dp.getAddress(),dp.getPort());
+ byte[] buf = new byte[head.length + dp.getLength()];
+ byte[] data = dp.getData();
+ //Merge head and data
+ System.arraycopy(head,0,buf,0,head.length);
+ //System.arraycopy(data,dp.getOffset(),buf,head.length,dp.getLength());
+ System.arraycopy(data,0,buf,head.length,dp.getLength());
+
+ if(encapsulation != null)
+ buf = encapsulation.udpEncapsulate(buf,true);
+
+ super.send(new DatagramPacket(buf,buf.length,relayIP,relayPort));
+ }
+ /**
+ This method allows to send datagram packets with address type DOMAINNAME.
+ SOCKS5 allows to specify host as names rather than ip addresses.Using
+ this method one can send udp datagrams through the proxy, without having
+ to know the ip address of the destination host.
+ <p>
+ If proxy specified for that socket has an option resolveAddrLocally set
+ to true host will be resolved, and the datagram will be send with address
+ type IPV4, if resolve fails, UnknownHostException is thrown.
+ @param dp Datagram to send, it should contain valid port and data
+ @param host Host name to which datagram should be send.
+ @throws IOException If error happens with I/O, or the host can't be
+ resolved when proxy settings say that hosts should be resolved locally.
+ @see Socks5Proxy#resolveAddrLocally(boolean)
+ */
+ public void send(DatagramPacket dp, String host) throws IOException {
+ dp.setAddress(InetAddress.getByName(host));
+ super.send(dp);
+ }
+
+ /**
+ * Receives udp packet. If packet have arrived from the proxy relay server,
+ * it is processed and address and port of the packet are set to the
+ * address and port of sending host.<BR>
+ * If the packet arrived from anywhere else it is not changed.<br>
+ * <B> NOTE: </B> DatagramPacket size should be at least 10 bytes bigger
+ * than the largest packet you expect (this is for IPV4 addresses).
+ * For hostnames and IPV6 it is even more.
+ @param dp Datagram in which all relevent information will be copied.
+ */
+ public void receive(DatagramPacket dp) throws IOException{
+ super.receive(dp);
+
+ if(server_mode){
+ //Drop all datagrams not from relayIP/relayPort
+ int init_length = dp.getLength();
+ int initTimeout = getSoTimeout();
+ long startTime = System.currentTimeMillis();
+
+ while(!relayIP.equals(dp.getAddress()) ||
+ relayPort != dp.getPort()){
+
+ //Restore datagram size
+ dp.setLength(init_length);
+
+ //If there is a non-infinit timeout on this socket
+ //Make sure that it happens no matter how often unexpected
+ //packets arrive.
+ if(initTimeout != 0){
+ int newTimeout = initTimeout - (int)(System.currentTimeMillis() -
+ startTime);
+ if(newTimeout <= 0) throw new InterruptedIOException(
+ "In Socks5DatagramSocket->receive()");
+ setSoTimeout(newTimeout);
+ }
+
+ super.receive(dp);
+ }
+
+ //Restore timeout settings
+ if(initTimeout != 0) setSoTimeout(initTimeout);
+
+ }else if(!relayIP.equals(dp.getAddress()) ||
+ relayPort != dp.getPort())
+ return; // Recieved direct packet
+ //If the datagram is not from the relay server, return it it as is.
+
+ byte[] data;
+ data = dp.getData();
+
+ if(encapsulation != null)
+ data = encapsulation.udpEncapsulate(data,false);
+
+ int offset = 0; //Java 1.1
+ //int offset = dp.getOffset(); //Java 1.2
+
+ ByteArrayInputStream bIn = new ByteArrayInputStream(data,offset,
+ dp.getLength());
+
+
+ ProxyMessage msg = new Socks5Message(bIn);
+ dp.setPort(msg.port);
+ dp.setAddress(msg.getInetAddress());
+
+ //what wasn't read by the Message is the data
+ int data_length = bIn.available();
+ //Shift data to the left
+ System.arraycopy(data,offset+dp.getLength()-data_length,
+ data,offset,data_length);
+
+
+ dp.setLength(data_length);
+ }
+
+ /**
+ * Returns port assigned by the proxy, to which datagrams are relayed.
+ * It is not the same port to which other party should send datagrams.
+ @return Port assigned by socks server to which datagrams are send
+ for association.
+ */
+ public int getLocalPort(){
+ if(server_mode) return super.getLocalPort();
+ return relayPort;
+ }
+ /**
+ * Address assigned by the proxy, to which datagrams are send for relay.
+ * It is not necesseraly the same address, to which other party should send
+ * datagrams.
+ @return Address to which datagrams are send for association.
+ */
+ public InetAddress getLocalAddress(){
+ if(server_mode) return super.getLocalAddress();
+ return relayIP;
+ }
+
+ /**
+ * Closes datagram socket, and proxy connection.
+ */
+ public void close(){
+ if(!server_mode) proxy.endSession();
+ super.close();
+ }
+
+ /**
+ This method checks wether proxy still runs udp forwarding service
+ for this socket.
+ <p>
+ This methods checks wether the primary connection to proxy server
+ is active. If it is, chances are that proxy continues to forward
+ datagrams being send from this socket. If it was closed, most likely
+ datagrams are no longer being forwarded by the server.
+ <p>
+ Proxy might decide to stop forwarding datagrams, in which case it
+ should close primary connection. This method allows to check, wether
+ this have been done.
+ <p>
+ You can specify timeout for which we should be checking EOF condition
+ on the primary connection. Timeout is in milliseconds. Specifying 0 as
+ timeout implies infinity, in which case method will block, until
+ connection to proxy is closed or an error happens, and then return false.
+ <p>
+ One possible scenario is to call isProxyactive(0) in separate thread,
+ and once it returned notify other threads about this event.
+
+ @param timeout For how long this method should block, before returning.
+ @return true if connection to proxy is active, false if eof or error
+ condition have been encountered on the connection.
+ */
+ public boolean isProxyAlive(int timeout){
+ if(server_mode) return false;
+ if(proxy != null){
+ try{
+ proxy.proxySocket.setSoTimeout(timeout);
+
+ int eof = proxy.in.read();
+ if(eof < 0) return false; // EOF encountered.
+ else return true; // This really should not happen
+
+ }catch(InterruptedIOException iioe){
+ return true; // read timed out.
+ }catch(IOException ioe){
+ return false;
+ }
+ }
+ return false;
+ }
+
+//PRIVATE METHODS
+//////////////////
+
+
+ private byte[] formHeader(InetAddress ip, int port){
+ Socks5Message request = new Socks5Message(0,ip,port);
+ request.data[0] = 0;
+ return request.data;
+ }
+
+
+/*======================================================================
+
+//Mainly Test functions
+//////////////////////
+
+ private String bytes2String(byte[] b){
+ String s="";
+ char[] hex_digit = { '0','1','2','3','4','5','6','7','8','9',
+ 'A','B','C','D','E','F'};
+ for(int i=0;i<b.length;++i){
+ int i1 = (b[i] & 0xF0) >> 4;
+ int i2 = b[i] & 0xF;
+ s+=hex_digit[i1];
+ s+=hex_digit[i2];
+ s+=" ";
+ }
+ return s;
+ }
+ private static final void debug(String s){
+ if(DEBUG)
+ System.out.print(s);
+ }
+
+ private static final boolean DEBUG = true;
+
+
+ public static void usage(){
+ System.err.print(
+ "Usage: java Socks.SocksDatagramSocket host port [socksHost socksPort]\n");
+ }
+
+ static final int defaultProxyPort = 1080; //Default Port
+ static final String defaultProxyHost = "www-proxy"; //Default proxy
+
+ public static void main(String args[]){
+ int port;
+ String host;
+ int proxyPort;
+ String proxyHost;
+ InetAddress ip;
+
+ if(args.length > 1 && args.length < 5){
+ try{
+
+ host = args[0];
+ port = Integer.parseInt(args[1]);
+
+ proxyPort =(args.length > 3)? Integer.parseInt(args[3])
+ : defaultProxyPort;
+
+ host = args[0];
+ ip = InetAddress.getByName(host);
+
+ proxyHost =(args.length > 2)? args[2]
+ : defaultProxyHost;
+
+ Proxy.setDefaultProxy(proxyHost,proxyPort);
+ Proxy p = Proxy.getDefaultProxy();
+ p.addDirect("lux");
+
+
+ DatagramSocket ds = new Socks5DatagramSocket();
+
+
+ BufferedReader in = new BufferedReader(
+ new InputStreamReader(System.in));
+ String s;
+
+ System.out.print("Enter line:");
+ s = in.readLine();
+
+ while(s != null){
+ byte[] data = (s+"\r\n").getBytes();
+ DatagramPacket dp = new DatagramPacket(data,0,data.length,
+ ip,port);
+ System.out.println("Sending to: "+ip+":"+port);
+ ds.send(dp);
+ dp = new DatagramPacket(new byte[1024],1024);
+
+ System.out.println("Trying to recieve on port:"+
+ ds.getLocalPort());
+ ds.receive(dp);
+ System.out.print("Recieved:\n"+
+ "From:"+dp.getAddress()+":"+dp.getPort()+
+ "\n\n"+
+ new String(dp.getData(),dp.getOffset(),dp.getLength())+"\n"
+ );
+ System.out.print("Enter line:");
+ s = in.readLine();
+
+ }
+ ds.close();
+ System.exit(1);
+
+ }catch(SocksException s_ex){
+ System.err.println("SocksException:"+s_ex);
+ s_ex.printStackTrace();
+ System.exit(1);
+ }catch(IOException io_ex){
+ io_ex.printStackTrace();
+ System.exit(1);
+ }catch(NumberFormatException num_ex){
+ usage();
+ num_ex.printStackTrace();
+ System.exit(1);
+ }
+
+ }else{
+ usage();
+ }
+ }
+*/
+
+}
diff --git a/src/net/sourceforge/jsocks/Socks5Message.java b/src/net/sourceforge/jsocks/Socks5Message.java
index 49539c4..ea2a321 100644
--- a/src/net/sourceforge/jsocks/Socks5Message.java
+++ b/src/net/sourceforge/jsocks/Socks5Message.java
@@ -1,292 +1,292 @@
-package net.sourceforge.jsocks;
-
-import java.io.DataInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-/**
- SOCKS5 request/response message.
-*/
-
-public class Socks5Message extends ProxyMessage{
- /** Address type of given message*/
- public int addrType;
-
- byte[] data;
-
- /**
- Server error response.
- @param cmd Error code.
- */
- public Socks5Message(int cmd){
- super(cmd,null,0);
- data = new byte[3];
- data[0] = SOCKS_VERSION; //Version.
- data[1] = (byte)cmd; //Reply code for some kind of failure.
- data[2] = 0; //Reserved byte.
- }
-
- /**
- Construct client request or server response.
- @param cmd - Request/Response code.
- @param ip - IP field.
- @paarm port - port field.
- */
- public Socks5Message(int cmd,InetAddress ip,int port){
- super(cmd,ip,port);
- this.host = ip==null?"0.0.0.0":ip.getHostName();
- this.version = SOCKS_VERSION;
-
- byte[] addr;
-
- if(ip == null){
- addr = new byte[4];
- addr[0]=addr[1]=addr[2]=addr[3]=0;
- }else
- addr = ip.getAddress();
-
- addrType = addr.length == 4 ? SOCKS_ATYP_IPV4
- : SOCKS_ATYP_IPV6;
-
- data = new byte[6+addr.length];
- data[0] = (byte) SOCKS_VERSION; //Version
- data[1] = (byte) command; //Command
- data[2] = (byte) 0; //Reserved byte
- data[3] = (byte) addrType; //Address type
-
- //Put Address
- System.arraycopy(addr,0,data,4,addr.length);
- //Put port
- data[data.length-2] = (byte)(port>>8);
- data[data.length-1] = (byte)(port);
- }
-
-
- /**
- Construct client request or server response.
- @param cmd - Request/Response code.
- @param hostName - IP field as hostName, uses ADDR_TYPE of HOSTNAME.
- @paarm port - port field.
- */
- public Socks5Message(int cmd,String hostName,int port){
- super(cmd,null,port);
- this.host = hostName;
- this.version = SOCKS_VERSION;
-
- //System.out.println("Doing ATYP_DOMAINNAME");
-
- addrType = SOCKS_ATYP_DOMAINNAME;
- byte addr[] = hostName.getBytes();
-
- data =new byte[7+addr.length];
- data[0] = (byte) SOCKS_VERSION; //Version
- data[1] = (byte) command; //Command
- data[2] = (byte) 0; //Reserved byte
- data[3] = (byte) SOCKS_ATYP_DOMAINNAME; //Address type
- data[4] = (byte) addr.length; //Length of the address
-
- //Put Address
- System.arraycopy(addr,0,data,5,addr.length);
- //Put port
- data[data.length-2] = (byte)(port >>8);
- data[data.length-1] = (byte)(port);
- }
-
- /**
- Initialises Message from the stream. Reads server response from
- given stream.
- @param in Input stream to read response from.
- @throws SocksException If server response code is not SOCKS_SUCCESS(0), or
- if any error with protocol occurs.
- @throws IOException If any error happens with I/O.
- */
- public Socks5Message(InputStream in) throws SocksException,
- IOException{
- this(in,true);
- }
-
- /**
- Initialises Message from the stream. Reads server response or client
- request from given stream.
-
- @param in Input stream to read response from.
- @param clinetMode If true read server response, else read client request.
- @throws SocksException If server response code is not SOCKS_SUCCESS(0) and
- reading in client mode, or if any error with protocol occurs.
- @throws IOException If any error happens with I/O.
- */
- public Socks5Message(InputStream in,boolean clientMode)throws SocksException,
- IOException{
- read(in,clientMode);
- }
-
-
- /**
- Initialises Message from the stream. Reads server response from
- given stream.
- @param in Input stream to read response from.
- @throws SocksException If server response code is not SOCKS_SUCCESS(0), or
- if any error with protocol occurs.
- @throws IOException If any error happens with I/O.
- */
- public void read(InputStream in) throws SocksException,
- IOException{
- read(in,true);
- }
-
-
- /**
- Initialises Message from the stream. Reads server response or client
- request from given stream.
-
- @param in Input stream to read response from.
- @param clinetMode If true read server response, else read client request.
- @throws SocksException If server response code is not SOCKS_SUCCESS(0) and
- reading in client mode, or if any error with protocol occurs.
- @throws IOException If any error happens with I/O.
- */
- public void read(InputStream in,boolean clientMode) throws SocksException,
- IOException{
- data = null;
- ip = null;
-
- DataInputStream di = new DataInputStream(in);
-
- version = di.readUnsignedByte();
- command = di.readUnsignedByte();
- if(clientMode && command != 0)
- throw new SocksException(command);
-
- @SuppressWarnings("unused")
- int reserved = di.readUnsignedByte();
- addrType = di.readUnsignedByte();
-
- byte addr[];
-
- switch(addrType){
- case SOCKS_ATYP_IPV4:
- addr = new byte[4];
- di.readFully(addr);
- host = bytes2IPV4(addr,0);
- break;
- case SOCKS_ATYP_IPV6:
- addr = new byte[SOCKS_IPV6_LENGTH];//I believe it is 16 bytes,huge!
- di.readFully(addr);
- host = bytes2IPV6(addr,0);
- break;
- case SOCKS_ATYP_DOMAINNAME:
- //System.out.println("Reading ATYP_DOMAINNAME");
- addr = new byte[di.readUnsignedByte()];//Next byte shows the length
- di.readFully(addr);
- host = new String(addr);
- break;
- default:
- throw(new SocksException(Proxy.SOCKS_JUST_ERROR));
- }
-
- port = di.readUnsignedShort();
-
- if(addrType != SOCKS_ATYP_DOMAINNAME && doResolveIP){
- try{
- ip = InetAddress.getByName(host);
- }catch(UnknownHostException uh_ex){
- }
- }
- }
-
- /**
- Writes the message to the stream.
- @param out Output stream to which message should be written.
- */
- public void write(OutputStream out)throws SocksException,
- IOException{
- if(data == null){
- Socks5Message msg;
-
- if(addrType == SOCKS_ATYP_DOMAINNAME)
- msg = new Socks5Message(command,host,port);
- else{
- if(ip == null){
- try{
- ip = InetAddress.getByName(host);
- }catch(UnknownHostException uh_ex){
- throw new SocksException(Proxy.SOCKS_JUST_ERROR);
- }
- }
- msg = new Socks5Message(command,ip,port);
- }
- data = msg.data;
- }
- out.write(data);
- }
-
- /**
- Returns IP field of the message as IP, if the message was created
- with ATYP of HOSTNAME, it will attempt to resolve the hostname,
- which might fail.
- @throws UnknownHostException if host can't be resolved.
- */
- public InetAddress getInetAddress() throws UnknownHostException{
- if(ip!=null) return ip;
-
- return (ip=InetAddress.getByName(host));
- }
-
- /**
- Returns string representation of the message.
- */
- public String toString(){
- String s=
- "Socks5Message:"+"\n"+
- "VN "+version+"\n"+
- "CMD "+command+"\n"+
- "ATYP "+addrType+"\n"+
- "ADDR "+host+"\n"+
- "PORT "+port+"\n";
- return s;
- }
-
-
- /**
- *Wether to resolve hostIP returned from SOCKS server
- *that is wether to create InetAddress object from the
- *hostName string
- */
- static public boolean resolveIP(){ return doResolveIP;}
-
- /**
- *Wether to resolve hostIP returned from SOCKS server
- *that is wether to create InetAddress object from the
- *hostName string
- *@param doResolve Wether to resolve hostIP from SOCKS server.
- *@return Previous value.
- */
- static public boolean resolveIP(boolean doResolve){
- boolean old = doResolveIP;
- doResolveIP = doResolve;
- return old;
- }
-
- /*
- private static final void debug(String s){
- if(DEBUG)
- System.out.print(s);
- }
- private static final boolean DEBUG = false;
- */
-
- //SOCKS5 constants
- public static final int SOCKS_VERSION =5;
-
- public static final int SOCKS_ATYP_IPV4 =0x1; //Where is 2??
- public static final int SOCKS_ATYP_DOMAINNAME =0x3; //!!!!rfc1928
- public static final int SOCKS_ATYP_IPV6 =0x4;
-
- public static final int SOCKS_IPV6_LENGTH =16;
-
- static boolean doResolveIP = true;
-
-}
+package net.sourceforge.jsocks;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ SOCKS5 request/response message.
+*/
+
+public class Socks5Message extends ProxyMessage{
+ /** Address type of given message*/
+ public int addrType;
+
+ byte[] data;
+
+ /**
+ Server error response.
+ @param cmd Error code.
+ */
+ public Socks5Message(int cmd){
+ super(cmd,null,0);
+ data = new byte[3];
+ data[0] = SOCKS_VERSION; //Version.
+ data[1] = (byte)cmd; //Reply code for some kind of failure.
+ data[2] = 0; //Reserved byte.
+ }
+
+ /**
+ Construct client request or server response.
+ @param cmd - Request/Response code.
+ @param ip - IP field.
+ @paarm port - port field.
+ */
+ public Socks5Message(int cmd,InetAddress ip,int port){
+ super(cmd,ip,port);
+ this.host = ip==null?"0.0.0.0":ip.getHostName();
+ this.version = SOCKS_VERSION;
+
+ byte[] addr;
+
+ if(ip == null){
+ addr = new byte[4];
+ addr[0]=addr[1]=addr[2]=addr[3]=0;
+ }else
+ addr = ip.getAddress();
+
+ addrType = addr.length == 4 ? SOCKS_ATYP_IPV4
+ : SOCKS_ATYP_IPV6;
+
+ data = new byte[6+addr.length];
+ data[0] = (byte) SOCKS_VERSION; //Version
+ data[1] = (byte) command; //Command
+ data[2] = (byte) 0; //Reserved byte
+ data[3] = (byte) addrType; //Address type
+
+ //Put Address
+ System.arraycopy(addr,0,data,4,addr.length);
+ //Put port
+ data[data.length-2] = (byte)(port>>8);
+ data[data.length-1] = (byte)(port);
+ }
+
+
+ /**
+ Construct client request or server response.
+ @param cmd - Request/Response code.
+ @param hostName - IP field as hostName, uses ADDR_TYPE of HOSTNAME.
+ @paarm port - port field.
+ */
+ public Socks5Message(int cmd,String hostName,int port){
+ super(cmd,null,port);
+ this.host = hostName;
+ this.version = SOCKS_VERSION;
+
+ //System.out.println("Doing ATYP_DOMAINNAME");
+
+ addrType = SOCKS_ATYP_DOMAINNAME;
+ byte addr[] = hostName.getBytes();
+
+ data =new byte[7+addr.length];
+ data[0] = (byte) SOCKS_VERSION; //Version
+ data[1] = (byte) command; //Command
+ data[2] = (byte) 0; //Reserved byte
+ data[3] = (byte) SOCKS_ATYP_DOMAINNAME; //Address type
+ data[4] = (byte) addr.length; //Length of the address
+
+ //Put Address
+ System.arraycopy(addr,0,data,5,addr.length);
+ //Put port
+ data[data.length-2] = (byte)(port >>8);
+ data[data.length-1] = (byte)(port);
+ }
+
+ /**
+ Initialises Message from the stream. Reads server response from
+ given stream.
+ @param in Input stream to read response from.
+ @throws SocksException If server response code is not SOCKS_SUCCESS(0), or
+ if any error with protocol occurs.
+ @throws IOException If any error happens with I/O.
+ */
+ public Socks5Message(InputStream in) throws SocksException,
+ IOException{
+ this(in,true);
+ }
+
+ /**
+ Initialises Message from the stream. Reads server response or client
+ request from given stream.
+
+ @param in Input stream to read response from.
+ @param clinetMode If true read server response, else read client request.
+ @throws SocksException If server response code is not SOCKS_SUCCESS(0) and
+ reading in client mode, or if any error with protocol occurs.
+ @throws IOException If any error happens with I/O.
+ */
+ public Socks5Message(InputStream in,boolean clientMode)throws SocksException,
+ IOException{
+ read(in,clientMode);
+ }
+
+
+ /**
+ Initialises Message from the stream. Reads server response from
+ given stream.
+ @param in Input stream to read response from.
+ @throws SocksException If server response code is not SOCKS_SUCCESS(0), or
+ if any error with protocol occurs.
+ @throws IOException If any error happens with I/O.
+ */
+ public void read(InputStream in) throws SocksException,
+ IOException{
+ read(in,true);
+ }
+
+
+ /**
+ Initialises Message from the stream. Reads server response or client
+ request from given stream.
+
+ @param in Input stream to read response from.
+ @param clinetMode If true read server response, else read client request.
+ @throws SocksException If server response code is not SOCKS_SUCCESS(0) and
+ reading in client mode, or if any error with protocol occurs.
+ @throws IOException If any error happens with I/O.
+ */
+ public void read(InputStream in,boolean clientMode) throws SocksException,
+ IOException{
+ data = null;
+ ip = null;
+
+ DataInputStream di = new DataInputStream(in);
+
+ version = di.readUnsignedByte();
+ command = di.readUnsignedByte();
+ if(clientMode && command != 0)
+ throw new SocksException(command);
+
+ @SuppressWarnings("unused")
+ int reserved = di.readUnsignedByte();
+ addrType = di.readUnsignedByte();
+
+ byte addr[];
+
+ switch(addrType){
+ case SOCKS_ATYP_IPV4:
+ addr = new byte[4];
+ di.readFully(addr);
+ host = bytes2IPV4(addr,0);
+ break;
+ case SOCKS_ATYP_IPV6:
+ addr = new byte[SOCKS_IPV6_LENGTH];//I believe it is 16 bytes,huge!
+ di.readFully(addr);
+ host = bytes2IPV6(addr,0);
+ break;
+ case SOCKS_ATYP_DOMAINNAME:
+ //System.out.println("Reading ATYP_DOMAINNAME");
+ addr = new byte[di.readUnsignedByte()];//Next byte shows the length
+ di.readFully(addr);
+ host = new String(addr);
+ break;
+ default:
+ throw(new SocksException(Proxy.SOCKS_JUST_ERROR));
+ }
+
+ port = di.readUnsignedShort();
+
+ if(addrType != SOCKS_ATYP_DOMAINNAME && doResolveIP){
+ try{
+ ip = InetAddress.getByName(host);
+ }catch(UnknownHostException uh_ex){
+ }
+ }
+ }
+
+ /**
+ Writes the message to the stream.
+ @param out Output stream to which message should be written.
+ */
+ public void write(OutputStream out)throws SocksException,
+ IOException{
+ if(data == null){
+ Socks5Message msg;
+
+ if(addrType == SOCKS_ATYP_DOMAINNAME)
+ msg = new Socks5Message(command,host,port);
+ else{
+ if(ip == null){
+ try{
+ ip = InetAddress.getByName(host);
+ }catch(UnknownHostException uh_ex){
+ throw new SocksException(Proxy.SOCKS_JUST_ERROR);
+ }
+ }
+ msg = new Socks5Message(command,ip,port);
+ }
+ data = msg.data;
+ }
+ out.write(data);
+ }
+
+ /**
+ Returns IP field of the message as IP, if the message was created
+ with ATYP of HOSTNAME, it will attempt to resolve the hostname,
+ which might fail.
+ @throws UnknownHostException if host can't be resolved.
+ */
+ public InetAddress getInetAddress() throws UnknownHostException{
+ if(ip!=null) return ip;
+
+ return (ip=InetAddress.getByName(host));
+ }
+
+ /**
+ Returns string representation of the message.
+ */
+ public String toString(){
+ String s=
+ "Socks5Message:"+"\n"+
+ "VN "+version+"\n"+
+ "CMD "+command+"\n"+
+ "ATYP "+addrType+"\n"+
+ "ADDR "+host+"\n"+
+ "PORT "+port+"\n";
+ return s;
+ }
+
+
+ /**
+ *Wether to resolve hostIP returned from SOCKS server
+ *that is wether to create InetAddress object from the
+ *hostName string
+ */
+ static public boolean resolveIP(){ return doResolveIP;}
+
+ /**
+ *Wether to resolve hostIP returned from SOCKS server
+ *that is wether to create InetAddress object from the
+ *hostName string
+ *@param doResolve Wether to resolve hostIP from SOCKS server.
+ *@return Previous value.
+ */
+ static public boolean resolveIP(boolean doResolve){
+ boolean old = doResolveIP;
+ doResolveIP = doResolve;
+ return old;
+ }
+
+ /*
+ private static final void debug(String s){
+ if(DEBUG)
+ System.out.print(s);
+ }
+ private static final boolean DEBUG = false;
+ */
+
+ //SOCKS5 constants
+ public static final int SOCKS_VERSION =5;
+
+ public static final int SOCKS_ATYP_IPV4 =0x1; //Where is 2??
+ public static final int SOCKS_ATYP_DOMAINNAME =0x3; //!!!!rfc1928
+ public static final int SOCKS_ATYP_IPV6 =0x4;
+
+ public static final int SOCKS_IPV6_LENGTH =16;
+
+ static boolean doResolveIP = true;
+
+}
diff --git a/src/net/sourceforge/jsocks/Socks5Proxy.java b/src/net/sourceforge/jsocks/Socks5Proxy.java
index 8b1d946..aa9c643 100644
--- a/src/net/sourceforge/jsocks/Socks5Proxy.java
+++ b/src/net/sourceforge/jsocks/Socks5Proxy.java
@@ -1,231 +1,231 @@
-package net.sourceforge.jsocks;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.InetAddress;
-import java.net.Socket;
-import java.net.SocketException;
-import java.net.UnknownHostException;
-import java.util.Enumeration;
-import java.util.Hashtable;
-
-/**
- SOCKS5 Proxy.
-*/
-
-public class Socks5Proxy extends Proxy implements Cloneable{
-
-//Data members
- private Hashtable<Integer, Authentication> authMethods = new Hashtable<Integer, Authentication>();
- private int selectedMethod;
-
- boolean resolveAddrLocally = true;
- UDPEncapsulation udp_encapsulation=null;
-
-
-//Public Constructors
-//====================
-
- /**
- Creates SOCKS5 proxy.
- @param proxyHost Host on which a Proxy server runs.
- @param proxyPort Port on which a Proxy server listens for connections.
- @throws UnknownHostException If proxyHost can't be resolved.
- */
- public Socks5Proxy(String proxyHost,int proxyPort)
- throws UnknownHostException{
- super(proxyHost,proxyPort);
- version = 5;
- setAuthenticationMethod(0,new AuthenticationNone());
- }
-
-
- /**
- Creates SOCKS5 proxy.
- @param proxyIP Host on which a Proxy server runs.
- @param proxyPort Port on which a Proxy server listens for connections.
- */
- public Socks5Proxy(InetAddress proxyIP,int proxyPort){
- super(proxyIP,proxyPort);
- version = 5;
- setAuthenticationMethod(0,new AuthenticationNone());
- }
-
-
-//Public instance methods
-//========================
-
-
- /**
- * Wether to resolve address locally or to let proxy do so.
- <p>
- SOCKS5 protocol allows to send host names rather then IPs in the
- requests, this option controls wether the hostnames should be send
- to the proxy server as names, or should they be resolved locally.
- @param doResolve Wether to perform resolution locally.
- @return Previous settings.
- */
- public boolean resolveAddrLocally(boolean doResolve){
- boolean old = resolveAddrLocally;
- resolveAddrLocally = doResolve;
- return old;
- }
- /**
- Get current setting on how the addresses should be handled.
- @return Current setting for address resolution.
- @see Socks5Proxy#resolveAddrLocally(boolean doResolve)
- */
- public boolean resolveAddrLocally(){
- return resolveAddrLocally;
- }
-
- /**
- Adds another authentication method.
- @param methodId Authentication method id, see rfc1928
- @param method Implementation of Authentication
- @see Authentication
- */
- public boolean setAuthenticationMethod(int methodId,
- Authentication method){
- if(methodId<0 || methodId > 255)
- return false;
- if(method == null){
- //Want to remove a particular method
- return (authMethods.remove(new Integer(methodId)) != null);
- }else{//Add the method, or rewrite old one
- authMethods.put(new Integer(methodId),method);
- }
- return true;
- }
-
- /**
- Get authentication method, which corresponds to given method id
- @param methodId Authentication method id.
- @return Implementation for given method or null, if one was not set.
- */
- public Authentication getAuthenticationMethod(int methodId){
- Object method = authMethods.get(new Integer(methodId));
- if(method == null) return null;
- return (Authentication)method;
- }
-
- /**
- Creates a clone of this Proxy.
- */
- @SuppressWarnings("unchecked")
-public Object clone(){
- Socks5Proxy newProxy = new Socks5Proxy(proxyIP,proxyPort);
- newProxy.authMethods = (Hashtable<Integer, Authentication>) this.authMethods.clone();
- newProxy.resolveAddrLocally = resolveAddrLocally;
- newProxy.chainProxy = chainProxy;
- return newProxy;
- }
-
-//Public Static(Class) Methods
-//==============================
-
-
-//Protected Methods
-//=================
-
- protected Proxy copy(){
- Socks5Proxy copy = new Socks5Proxy(proxyIP,proxyPort);
- copy.authMethods = this.authMethods; //same Hash, no copy
- copy.chainProxy = this.chainProxy;
- copy.resolveAddrLocally = this.resolveAddrLocally;
- return copy;
- }
- /**
- *
- *
- */
- protected void startSession()throws SocksException{
- super.startSession();
- Authentication auth;
- Socket ps = proxySocket; //The name is too long
-
- try{
-
- byte nMethods = (byte) authMethods.size(); //Number of methods
-
- byte[] buf = new byte[2+nMethods]; //2 is for VER,NMETHODS
- buf[0] = (byte) version;
- buf[1] = nMethods; //Number of methods
- int i=2;
-
- Enumeration<Integer> ids = authMethods.keys();
- while(ids.hasMoreElements())
- buf[i++] = (byte)((Integer)ids.nextElement()).intValue();
-
- out.write(buf);
- out.flush();
-
- int versionNumber = in.read();
- selectedMethod = in.read();
-
- if(versionNumber < 0 || selectedMethod < 0){
- //EOF condition was reached
- endSession();
- throw(new SocksException(SOCKS_PROXY_IO_ERROR,
- "Connection to proxy lost."));
- }
- if(versionNumber < version){
- //What should we do??
- }
- if(selectedMethod == 0xFF){ //No method selected
- ps.close();
- throw ( new SocksException(SOCKS_AUTH_NOT_SUPPORTED));
- }
-
- auth = getAuthenticationMethod(selectedMethod);
- if(auth == null){
- //This shouldn't happen, unless method was removed by other
- //thread, or the server stuffed up
- throw(new SocksException(SOCKS_JUST_ERROR,
- "Speciefied Authentication not found!"));
- }
- Object[] in_out = auth.doSocksAuthentication(selectedMethod,ps);
- if(in_out == null){
- //Authentication failed by some reason
- throw(new SocksException(SOCKS_AUTH_FAILURE));
- }
- //Most authentication methods are expected to return
- //simply the input/output streams associated with
- //the socket. However if the auth. method requires
- //some kind of encryption/decryption being done on the
- //connection it should provide classes to handle I/O.
-
- in = (InputStream) in_out[0];
- out = (OutputStream) in_out[1];
- if(in_out.length > 2)
- udp_encapsulation = (UDPEncapsulation) in_out[2];
-
- }catch(SocksException s_ex){
- throw s_ex;
- }catch(UnknownHostException uh_ex){
- throw(new SocksException(SOCKS_PROXY_NO_CONNECT));
- }catch(SocketException so_ex){
- throw(new SocksException(SOCKS_PROXY_NO_CONNECT));
- }catch(IOException io_ex){
- //System.err.println(io_ex);
- throw(new SocksException(SOCKS_PROXY_IO_ERROR,""+io_ex));
- }
- }
-
- protected ProxyMessage formMessage(int cmd,InetAddress ip,int port){
- return new Socks5Message(cmd,ip,port);
- }
- protected ProxyMessage formMessage(int cmd,String host,int port)
- throws UnknownHostException{
- if(resolveAddrLocally)
- return formMessage(cmd,InetAddress.getByName(host),port);
- else
- return new Socks5Message(cmd,host,port);
- }
- protected ProxyMessage formMessage(InputStream in)
- throws SocksException,
- IOException{
- return new Socks5Message(in);
- }
-
-}
+package net.sourceforge.jsocks;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+/**
+ SOCKS5 Proxy.
+*/
+
+public class Socks5Proxy extends Proxy implements Cloneable{
+
+//Data members
+ private Hashtable<Integer, Authentication> authMethods = new Hashtable<Integer, Authentication>();
+ private int selectedMethod;
+
+ boolean resolveAddrLocally = true;
+ UDPEncapsulation udp_encapsulation=null;
+
+
+//Public Constructors
+//====================
+
+ /**
+ Creates SOCKS5 proxy.
+ @param proxyHost Host on which a Proxy server runs.
+ @param proxyPort Port on which a Proxy server listens for connections.
+ @throws UnknownHostException If proxyHost can't be resolved.
+ */
+ public Socks5Proxy(String proxyHost,int proxyPort)
+ throws UnknownHostException{
+ super(proxyHost,proxyPort);
+ version = 5;
+ setAuthenticationMethod(0,new AuthenticationNone());
+ }
+
+
+ /**
+ Creates SOCKS5 proxy.
+ @param proxyIP Host on which a Proxy server runs.
+ @param proxyPort Port on which a Proxy server listens for connections.
+ */
+ public Socks5Proxy(InetAddress proxyIP,int proxyPort){
+ super(proxyIP,proxyPort);
+ version = 5;
+ setAuthenticationMethod(0,new AuthenticationNone());
+ }
+
+
+//Public instance methods
+//========================
+
+
+ /**
+ * Wether to resolve address locally or to let proxy do so.
+ <p>
+ SOCKS5 protocol allows to send host names rather then IPs in the
+ requests, this option controls wether the hostnames should be send
+ to the proxy server as names, or should they be resolved locally.
+ @param doResolve Wether to perform resolution locally.
+ @return Previous settings.
+ */
+ public boolean resolveAddrLocally(boolean doResolve){
+ boolean old = resolveAddrLocally;
+ resolveAddrLocally = doResolve;
+ return old;
+ }
+ /**
+ Get current setting on how the addresses should be handled.
+ @return Current setting for address resolution.
+ @see Socks5Proxy#resolveAddrLocally(boolean doResolve)
+ */
+ public boolean resolveAddrLocally(){
+ return resolveAddrLocally;
+ }
+
+ /**
+ Adds another authentication method.
+ @param methodId Authentication method id, see rfc1928
+ @param method Implementation of Authentication
+ @see Authentication
+ */
+ public boolean setAuthenticationMethod(int methodId,
+ Authentication method){
+ if(methodId<0 || methodId > 255)
+ return false;
+ if(method == null){
+ //Want to remove a particular method
+ return (authMethods.remove(new Integer(methodId)) != null);
+ }else{//Add the method, or rewrite old one
+ authMethods.put(new Integer(methodId),method);
+ }
+ return true;
+ }
+
+ /**
+ Get authentication method, which corresponds to given method id
+ @param methodId Authentication method id.
+ @return Implementation for given method or null, if one was not set.
+ */
+ public Authentication getAuthenticationMethod(int methodId){
+ Object method = authMethods.get(new Integer(methodId));
+ if(method == null) return null;
+ return (Authentication)method;
+ }
+
+ /**
+ Creates a clone of this Proxy.
+ */
+ @SuppressWarnings("unchecked")
+public Object clone(){
+ Socks5Proxy newProxy = new Socks5Proxy(proxyIP,proxyPort);
+ newProxy.authMethods = (Hashtable<Integer, Authentication>) this.authMethods.clone();
+ newProxy.resolveAddrLocally = resolveAddrLocally;
+ newProxy.chainProxy = chainProxy;
+ return newProxy;
+ }
+
+//Public Static(Class) Methods
+//==============================
+
+
+//Protected Methods
+//=================
+
+ protected Proxy copy(){
+ Socks5Proxy copy = new Socks5Proxy(proxyIP,proxyPort);
+ copy.authMethods = this.authMethods; //same Hash, no copy
+ copy.chainProxy = this.chainProxy;
+ copy.resolveAddrLocally = this.resolveAddrLocally;
+ return copy;
+ }
+ /**
+ *
+ *
+ */
+ protected void startSession()throws SocksException{
+ super.startSession();
+ Authentication auth;
+ Socket ps = proxySocket; //The name is too long
+
+ try{
+
+ byte nMethods = (byte) authMethods.size(); //Number of methods
+
+ byte[] buf = new byte[2+nMethods]; //2 is for VER,NMETHODS
+ buf[0] = (byte) version;
+ buf[1] = nMethods; //Number of methods
+ int i=2;
+
+ Enumeration<Integer> ids = authMethods.keys();
+ while(ids.hasMoreElements())
+ buf[i++] = (byte)((Integer)ids.nextElement()).intValue();
+
+ out.write(buf);
+ out.flush();
+
+ int versionNumber = in.read();
+ selectedMethod = in.read();
+
+ if(versionNumber < 0 || selectedMethod < 0){
+ //EOF condition was reached
+ endSession();
+ throw(new SocksException(SOCKS_PROXY_IO_ERROR,
+ "Connection to proxy lost."));
+ }
+ if(versionNumber < version){
+ //What should we do??
+ }
+ if(selectedMethod == 0xFF){ //No method selected
+ ps.close();
+ throw ( new SocksException(SOCKS_AUTH_NOT_SUPPORTED));
+ }
+
+ auth = getAuthenticationMethod(selectedMethod);
+ if(auth == null){
+ //This shouldn't happen, unless method was removed by other
+ //thread, or the server stuffed up
+ throw(new SocksException(SOCKS_JUST_ERROR,
+ "Speciefied Authentication not found!"));
+ }
+ Object[] in_out = auth.doSocksAuthentication(selectedMethod,ps);
+ if(in_out == null){
+ //Authentication failed by some reason
+ throw(new SocksException(SOCKS_AUTH_FAILURE));
+ }
+ //Most authentication methods are expected to return
+ //simply the input/output streams associated with
+ //the socket. However if the auth. method requires
+ //some kind of encryption/decryption being done on the
+ //connection it should provide classes to handle I/O.
+
+ in = (InputStream) in_out[0];
+ out = (OutputStream) in_out[1];
+ if(in_out.length > 2)
+ udp_encapsulation = (UDPEncapsulation) in_out[2];
+
+ }catch(SocksException s_ex){
+ throw s_ex;
+ }catch(UnknownHostException uh_ex){
+ throw(new SocksException(SOCKS_PROXY_NO_CONNECT));
+ }catch(SocketException so_ex){
+ throw(new SocksException(SOCKS_PROXY_NO_CONNECT));
+ }catch(IOException io_ex){
+ //System.err.println(io_ex);
+ throw(new SocksException(SOCKS_PROXY_IO_ERROR,""+io_ex));
+ }
+ }
+
+ protected ProxyMessage formMessage(int cmd,InetAddress ip,int port){
+ return new Socks5Message(cmd,ip,port);
+ }
+ protected ProxyMessage formMessage(int cmd,String host,int port)
+ throws UnknownHostException{
+ if(resolveAddrLocally)
+ return formMessage(cmd,InetAddress.getByName(host),port);
+ else
+ return new Socks5Message(cmd,host,port);
+ }
+ protected ProxyMessage formMessage(InputStream in)
+ throws SocksException,
+ IOException{
+ return new Socks5Message(in);
+ }
+
+}
diff --git a/src/net/sourceforge/jsocks/SocksException.java b/src/net/sourceforge/jsocks/SocksException.java
index 867eff6..764587f 100644
--- a/src/net/sourceforge/jsocks/SocksException.java
+++ b/src/net/sourceforge/jsocks/SocksException.java
@@ -1,80 +1,80 @@
-package net.sourceforge.jsocks;
-
-/**
- Exception thrown by various socks classes to indicate errors
- with protocol or unsuccessful server responses.
-*/
-public class SocksException extends java.io.IOException{
- private static final long serialVersionUID = 6141184566248512277L;
-
- /**
- Construct a SocksException with given error code.
- <p>
- Tries to look up message which corresponds to this error code.
- @param errCode Error code for this exception.
- */
- public SocksException(int errCode){
- this.errCode = errCode;
- if((errCode >> 16) == 0){
- //Server reply error message
- errString = errCode <= serverReplyMessage.length ?
- serverReplyMessage[errCode] :
- UNASSIGNED_ERROR_MESSAGE;
- }else{
- //Local error
- errCode = (errCode >> 16) -1;
- errString = errCode <= localErrorMessage.length ?
- localErrorMessage[errCode] :
- UNASSIGNED_ERROR_MESSAGE;
- }
- }
- /**
- Constructs a SocksException with given error code and message.
- @param errCode Error code.
- @param errString Error Message.
- */
- public SocksException(int errCode,String errString){
- this.errCode = errCode;
- this.errString = errString;
- }
- /**
- Get the error code associated with this exception.
- @return Error code associated with this exception.
- */
- public int getErrorCode(){
- return errCode;
- }
- /**
- Get human readable representation of this exception.
- @return String represntation of this exception.
- */
- public String toString(){
- return errString;
- }
-
- static final String UNASSIGNED_ERROR_MESSAGE =
- "Unknown error message";
- static final String serverReplyMessage[] = {
- "Succeeded",
- "General SOCKS server failure",
- "Connection not allowed by ruleset",
- "Network unreachable",
- "Host unreachable",
- "Connection refused",
- "TTL expired",
- "Command not supported",
- "Address type not supported" };
-
- static final String localErrorMessage[] ={
- "SOCKS server not specified",
- "Unable to contact SOCKS server",
- "IO error",
- "None of Authentication methods are supported",
- "Authentication failed",
- "General SOCKS fault" };
-
- String errString;
- public int errCode;
-
-}//End of SocksException class
-
+package net.sourceforge.jsocks;
+
+/**
+ Exception thrown by various socks classes to indicate errors
+ with protocol or unsuccessful server responses.
+*/
+public class SocksException extends java.io.IOException{
+ private static final long serialVersionUID = 6141184566248512277L;
+
+ /**
+ Construct a SocksException with given error code.
+ <p>
+ Tries to look up message which corresponds to this error code.
+ @param errCode Error code for this exception.
+ */
+ public SocksException(int errCode){
+ this.errCode = errCode;
+ if((errCode >> 16) == 0){
+ //Server reply error message
+ errString = errCode <= serverReplyMessage.length ?
+ serverReplyMessage[errCode] :
+ UNASSIGNED_ERROR_MESSAGE;
+ }else{
+ //Local error
+ errCode = (errCode >> 16) -1;
+ errString = errCode <= localErrorMessage.length ?
+ localErrorMessage[errCode] :
+ UNASSIGNED_ERROR_MESSAGE;
+ }
+ }
+ /**
+ Constructs a SocksException with given error code and message.
+ @param errCode Error code.
+ @param errString Error Message.
+ */
+ public SocksException(int errCode,String errString){
+ this.errCode = errCode;
+ this.errString = errString;
+ }
+ /**
+ Get the error code associated with this exception.
+ @return Error code associated with this exception.
+ */
+ public int getErrorCode(){
+ return errCode;
+ }
+ /**
+ Get human readable representation of this exception.
+ @return String represntation of this exception.
+ */
+ public String toString(){
+ return errString;
+ }
+
+ static final String UNASSIGNED_ERROR_MESSAGE =
+ "Unknown error message";
+ static final String serverReplyMessage[] = {
+ "Succeeded",
+ "General SOCKS server failure",
+ "Connection not allowed by ruleset",
+ "Network unreachable",
+ "Host unreachable",
+ "Connection refused",
+ "TTL expired",
+ "Command not supported",
+ "Address type not supported" };
+
+ static final String localErrorMessage[] ={
+ "SOCKS server not specified",
+ "Unable to contact SOCKS server",
+ "IO error",
+ "None of Authentication methods are supported",
+ "Authentication failed",
+ "General SOCKS fault" };
+
+ String errString;
+ public int errCode;
+
+}//End of SocksException class
+
diff --git a/src/net/sourceforge/jsocks/SocksServerSocket.java b/src/net/sourceforge/jsocks/SocksServerSocket.java
index 739251d..179e9c4 100644
--- a/src/net/sourceforge/jsocks/SocksServerSocket.java
+++ b/src/net/sourceforge/jsocks/SocksServerSocket.java
@@ -1,164 +1,164 @@
-package net.sourceforge.jsocks;
-
-import java.net.*;
-import java.io.*;
-
-/**
- SocksServerSocket allows to accept connections from one particular
- host through the SOCKS4 or SOCKS5 proxy.
-*/
-public class SocksServerSocket extends ServerSocket{
- //Data members
- protected Proxy proxy;
- protected String localHost;
- protected InetAddress localIP;
- protected int localPort;
-
- boolean doing_direct = false;
- InetAddress remoteAddr;
-
- /**
- *Creates ServerSocket capable of accepting one connection
- *through the firewall, uses given proxy.
- *@param host Host from which the connection should be recieved.
- *@param port Port number of the primary connection.
- */
- public SocksServerSocket(String host, int port) throws SocksException,
- UnknownHostException, IOException {
-
- super(0);
- remoteAddr = InetAddress.getByName(host);
- doDirect();
- }
-
- /**
- * Creates ServerSocket capable of accepting one connection
- * through the firewall, uses default Proxy.
- *@param ip Host from which the connection should be recieved.
- *@param port Port number of the primary connection.
- */
- public SocksServerSocket(InetAddress ip, int port) throws SocksException,
- IOException{
- this(Proxy.defaultProxy,ip,port);
- }
-
- /**
- *Creates ServerSocket capable of accepting one connection
- *through the firewall, uses given proxy.
- *@param ip Host from which the connection should be recieved.
- *@param port Port number of the primary connection.
- */
- public SocksServerSocket(Proxy p, InetAddress ip, int port)
- throws SocksException, IOException {
- super(0);
-
- remoteAddr = ip;
- doDirect();
- }
-
-
- /**
- * Accepts the incoming connection.
- */
- public Socket accept() throws IOException{
- Socket s;
-
- if(!doing_direct){
- if(proxy == null) return null;
-
- ProxyMessage msg = proxy.accept();
- s = msg.ip == null? new SocksSocket(msg.host,msg.port,proxy)
- : new SocksSocket(msg.ip,msg.port,proxy);
- //Set timeout back to 0
- proxy.proxySocket.setSoTimeout(0);
- }else{ //Direct Connection
-
- //Mimic the proxy behaviour,
- //only accept connections from the speciefed host.
- while(true){
- s = super.accept();
- if(s.getInetAddress().equals(remoteAddr)){
- //got the connection from the right host
- //Close listenning socket.
- break;
- }else
- s.close(); //Drop all connections from other hosts
- }
-
- }
- proxy = null;
- //Return accepted socket
- return s;
- }
-
- /**
- * Closes the connection to proxy if socket have not been accepted, if
- * the direct connection is used, closes direct ServerSocket. If the
- * client socket have been allready accepted, does nothing.
- */
- public void close() throws IOException{
- super.close();
- if(proxy != null) proxy.endSession();
- proxy = null;
- }
-
- /**
- Get the name of the host proxy is using to listen for incoming
- connection.
- <P>
- Usefull when address is returned by proxy as the hostname.
- @return the hostname of the address proxy is using to listen
- for incoming connection.
- */
- public String getHost(){
- return localHost;
- }
-
- /**
- * Get address assigned by proxy to listen for incomming
- * connections, or the local machine address if doing direct
- * connection.
- */
- public InetAddress getInetAddress(){
- if(localIP == null){
- try{
- localIP = InetAddress.getByName(localHost);
- }catch(UnknownHostException e){
- return null;
- }
- }
- return localIP;
- }
-
- /**
- * Get port assigned by proxy to listen for incoming connections, or
- the port chosen by local system, if accepting directly.
- */
- public int getLocalPort(){
- return localPort;
- }
-
- /**
- Set Timeout.
-
- @param timeout Amount of time in milliseconds, accept should wait for
- incoming connection before failing with exception.
- Zero timeout implies infinity.
- */
- public void setSoTimeout(int timeout) throws SocketException{
- super.setSoTimeout(timeout);
- if(!doing_direct) proxy.proxySocket.setSoTimeout(timeout);
- }
-
-
-//Private Methods
-//////////////////
-
- private void doDirect(){
- doing_direct = true;
- localPort = super.getLocalPort();
- localIP = super.getInetAddress();
- localHost = localIP.getHostName();
- }
-
-}
+package net.sourceforge.jsocks;
+
+import java.net.*;
+import java.io.*;
+
+/**
+ SocksServerSocket allows to accept connections from one particular
+ host through the SOCKS4 or SOCKS5 proxy.
+*/
+public class SocksServerSocket extends ServerSocket{
+ //Data members
+ protected Proxy proxy;
+ protected String localHost;
+ protected InetAddress localIP;
+ protected int localPort;
+
+ boolean doing_direct = false;
+ InetAddress remoteAddr;
+
+ /**
+ *Creates ServerSocket capable of accepting one connection
+ *through the firewall, uses given proxy.
+ *@param host Host from which the connection should be recieved.
+ *@param port Port number of the primary connection.
+ */
+ public SocksServerSocket(String host, int port) throws SocksException,
+ UnknownHostException, IOException {
+
+ super(0);
+ remoteAddr = InetAddress.getByName(host);
+ doDirect();
+ }
+
+ /**
+ * Creates ServerSocket capable of accepting one connection
+ * through the firewall, uses default Proxy.
+ *@param ip Host from which the connection should be recieved.
+ *@param port Port number of the primary connection.
+ */
+ public SocksServerSocket(InetAddress ip, int port) throws SocksException,
+ IOException{
+ this(Proxy.defaultProxy,ip,port);
+ }
+
+ /**
+ *Creates ServerSocket capable of accepting one connection
+ *through the firewall, uses given proxy.
+ *@param ip Host from which the connection should be recieved.
+ *@param port Port number of the primary connection.
+ */
+ public SocksServerSocket(Proxy p, InetAddress ip, int port)
+ throws SocksException, IOException {
+ super(0);
+
+ remoteAddr = ip;
+ doDirect();
+ }
+
+
+ /**
+ * Accepts the incoming connection.
+ */
+ public Socket accept() throws IOException{
+ Socket s;
+
+ if(!doing_direct){
+ if(proxy == null) return null;
+
+ ProxyMessage msg = proxy.accept();
+ s = msg.ip == null? new SocksSocket(msg.host,msg.port,proxy)
+ : new SocksSocket(msg.ip,msg.port,proxy);
+ //Set timeout back to 0
+ proxy.proxySocket.setSoTimeout(0);
+ }else{ //Direct Connection
+
+ //Mimic the proxy behaviour,
+ //only accept connections from the speciefed host.
+ while(true){
+ s = super.accept();
+ if(s.getInetAddress().equals(remoteAddr)){
+ //got the connection from the right host
+ //Close listenning socket.
+ break;
+ }else
+ s.close(); //Drop all connections from other hosts
+ }
+
+ }
+ proxy = null;
+ //Return accepted socket
+ return s;
+ }
+
+ /**
+ * Closes the connection to proxy if socket have not been accepted, if
+ * the direct connection is used, closes direct ServerSocket. If the
+ * client socket have been allready accepted, does nothing.
+ */
+ public void close() throws IOException{
+ super.close();
+ if(proxy != null) proxy.endSession();
+ proxy = null;
+ }
+
+ /**
+ Get the name of the host proxy is using to listen for incoming
+ connection.
+ <P>
+ Usefull when address is returned by proxy as the hostname.
+ @return the hostname of the address proxy is using to listen
+ for incoming connection.
+ */
+ public String getHost(){
+ return localHost;
+ }
+
+ /**
+ * Get address assigned by proxy to listen for incomming
+ * connections, or the local machine address if doing direct
+ * connection.
+ */
+ public InetAddress getInetAddress(){
+ if(localIP == null){
+ try{
+ localIP = InetAddress.getByName(localHost);
+ }catch(UnknownHostException e){
+ return null;
+ }
+ }
+ return localIP;
+ }
+
+ /**
+ * Get port assigned by proxy to listen for incoming connections, or
+ the port chosen by local system, if accepting directly.
+ */
+ public int getLocalPort(){
+ return localPort;
+ }
+
+ /**
+ Set Timeout.
+
+ @param timeout Amount of time in milliseconds, accept should wait for
+ incoming connection before failing with exception.
+ Zero timeout implies infinity.
+ */
+ public void setSoTimeout(int timeout) throws SocketException{
+ super.setSoTimeout(timeout);
+ if(!doing_direct) proxy.proxySocket.setSoTimeout(timeout);
+ }
+
+
+//Private Methods
+//////////////////
+
+ private void doDirect(){
+ doing_direct = true;
+ localPort = super.getLocalPort();
+ localIP = super.getInetAddress();
+ localHost = localIP.getHostName();
+ }
+
+}
diff --git a/src/net/sourceforge/jsocks/SocksSocket.java b/src/net/sourceforge/jsocks/SocksSocket.java
index 95a7e42..cf9ff65 100644
--- a/src/net/sourceforge/jsocks/SocksSocket.java
+++ b/src/net/sourceforge/jsocks/SocksSocket.java
@@ -1,291 +1,291 @@
-package net.sourceforge.jsocks;
-
-import java.net.*;
-import java.io.*;
-
-/**
- * SocksSocket tryies to look very similar to normal Socket,
- * while allowing connections through the SOCKS4 or 5 proxy.
- * To use this class you will have to identify proxy you need
- * to use, Proxy class allows you to set default proxy, which
- * will be used by all Socks aware sockets. You can also create
- * either Socks4Proxy or Socks5Proxy, and use them by passing to the
- * appropriate constructors.
- * <P>
- * Using Socks package can be as easy as that:
- *
- * <pre><tt>
- *
- * import Socks.*;
- * ....
- *
- * try{
- * //Specify SOCKS5 proxy
- * Proxy.setDefaultProxy("socks-proxy",1080);
- *
- * //OR you still use SOCKS4
- * //Code below uses SOCKS4 proxy
- * //Proxy.setDefaultProxy("socks-proxy",1080,userName);
- *
- * Socket s = SocksSocket("some.host.of.mine",13);
- * readTimeFromSock(s);
- * }catch(SocksException sock_ex){
- * //Usually it will turn in more or less meaningfull message
- * System.err.println("SocksException:"+sock_ex);
- * }
- *
- * </tt></pre>
- *<P>
- * However if the need exist for more control, like resolving addresses
- * remotely, or using some non-trivial authentication schemes, it can be done.
- */
-
-public class SocksSocket extends Socket{
- //Data members
- protected Proxy proxy;
- protected String localHost, remoteHost;
- protected InetAddress localIP, remoteIP;
- protected int localPort,remotePort;
-
- private Socket directSock = null;
-
- /**
- * Tryies to connect to given host and port
- * using default proxy. If no default proxy speciefied
- * it throws SocksException with error code SOCKS_NO_PROXY.
- @param host Machine to connect to.
- @param port Port to which to connect.
- * @see SocksSocket#SocksSocket(Proxy,String,int)
- * @see Socks5Proxy#resolveAddrLocally
- */
- public SocksSocket(String host,int port)
- throws SocksException,UnknownHostException{
- this(Proxy.defaultProxy,host,port);
- }
- /**
- * Connects to host port using given proxy server.
- @param p Proxy to use.
- @param host Machine to connect to.
- @param port Port to which to connect.
- @throws UnknownHostException
- If one of the following happens:
- <ol>
-
- <li> Proxy settings say that address should be resolved locally, but
- this fails.
- <li> Proxy settings say that the host should be contacted directly but
- host name can't be resolved.
- </ol>
- @throws SocksException
- If one of the following happens:
- <ul>
- <li> Proxy is is null.
- <li> Proxy settings say that the host should be contacted directly but
- this fails.
- <li> Socks Server can't be contacted.
- <li> Authentication fails.
- <li> Connection is not allowed by the SOCKS proxy.
- <li> SOCKS proxy can't establish the connection.
- <li> Any IO error occured.
- <li> Any protocol error occured.
- </ul>
- @throws IOexception if anything is wrong with I/O.
- @see Socks5Proxy#resolveAddrLocally
- */
- public SocksSocket(Proxy p, String host, int port) throws SocksException,
- UnknownHostException {
- remoteHost = host;
- remotePort = port;
- remoteIP = InetAddress.getByName(host);
- doDirect();
- }
-
- /**
- Connects to given ip and port using given Proxy server.
- @param p Proxy to use.
- @param ip Machine to connect to.
- @param port Port to which to connect.
-
- */
- public SocksSocket(InetAddress ip, int port) throws SocksException{
- this.remoteIP = ip;
- this.remotePort = port;
- this.remoteHost = ip.getHostName();
- doDirect();
- }
-
-
- /**
- * These 2 constructors are used by the SocksServerSocket.
- * This socket simply overrides remoteHost, remotePort
- */
- protected SocksSocket(String host,int port,Proxy proxy){
- this.remotePort = port;
- this.proxy = proxy;
- this.localIP = proxy.proxySocket.getLocalAddress();
- this.localPort = proxy.proxySocket.getLocalPort();
- this.remoteHost = host;
- }
- protected SocksSocket(InetAddress ip,int port,Proxy proxy){
- remoteIP = ip;
- remotePort = port;
- this.proxy = proxy;
- this.localIP = proxy.proxySocket.getLocalAddress();
- this.localPort = proxy.proxySocket.getLocalPort();
- remoteHost = remoteIP.getHostName();
- }
-
- /**
- * Same as Socket
- */
- public void close() throws IOException{
- if(proxy!= null)proxy.endSession();
- proxy = null;
- }
- /**
- * Same as Socket
- */
- public InputStream getInputStream(){
- return proxy.in;
- }
- /**
- * Same as Socket
- */
- public OutputStream getOutputStream(){
- return proxy.out;
- }
- /**
- * Same as Socket
- */
- public int getPort(){
- return remotePort;
- }
- /**
- * Returns remote host name, it is usefull in cases when addresses
- * are resolved by proxy, and we can't create InetAddress object.
- @return The name of the host this socket is connected to.
- */
- public String getHost(){
- return remoteHost;
- }
- /**
- * Get remote host as InetAddress object, might return null if
- * addresses are resolved by proxy, and it is not possible to resolve
- * it locally
- @return Ip address of the host this socket is connected to, or null
- if address was returned by the proxy as DOMAINNAME and can't be
- resolved locally.
- */
- public InetAddress getInetAddress(){
- if(remoteIP == null){
- try{
- remoteIP = InetAddress.getByName(remoteHost);
- }catch(UnknownHostException e){
- return null;
- }
- }
- return remoteIP;
- }
-
- /**
- * Get the port assigned by the proxy for the socket, not
- * the port on locall machine as in Socket.
- @return Port of the socket used on the proxy server.
- */
- public int getLocalPort(){
- return localPort;
- }
-
- /**
- * Get address assigned by proxy to make a remote connection,
- * it might be different from the host specified for the proxy.
- * Can return null if socks server returned this address as hostname
- * and it can't be resolved locally, use getLocalHost() then.
- @return Address proxy is using to make a connection.
- */
- public InetAddress getLocalAddress(){
- if(localIP == null){
- try{
- localIP = InetAddress.getByName(localHost);
- }catch(UnknownHostException e){
- return null;
- }
- }
- return localIP;
- }
- /**
- Get name of the host, proxy has assigned to make a remote connection
- for this socket. This method is usefull when proxy have returned
- address as hostname, and we can't resolve it on this machine.
- @return The name of the host proxy is using to make a connection.
- */
- public String getLocalHost(){
- return localHost;
- }
-
- /**
- Same as socket.
- */
- public void setSoLinger(boolean on,int val) throws SocketException{
- proxy.proxySocket.setSoLinger(on,val);
- }
- /**
- Same as socket.
- */
- public int getSoLinger(int timeout) throws SocketException{
- return proxy.proxySocket.getSoLinger();
- }
- /**
- Same as socket.
- */
- public void setSoTimeout(int timeout) throws SocketException{
- proxy.proxySocket.setSoTimeout(timeout);
- }
- /**
- Same as socket.
- */
- public int getSoTimeout(int timeout) throws SocketException{
- return proxy.proxySocket.getSoTimeout();
- }
- /**
- Same as socket.
- */
- public void setTcpNoDelay(boolean on) throws SocketException{
- proxy.proxySocket.setTcpNoDelay(on);
- }
- /**
- Same as socket.
- */
- public boolean getTcpNoDelay() throws SocketException{
- return proxy.proxySocket.getTcpNoDelay();
- }
-
- /**
- Get string representation of the socket.
- */
- public String toString(){
- if(directSock!=null) return "Direct connection:"+directSock;
- return ("Proxy:"+proxy+";"+"addr:"+remoteHost+",port:"+remotePort
- +",localport:"+localPort);
-
- }
-
-//Private Methods
-//////////////////
-
- private void doDirect()throws SocksException{
- try{
- //System.out.println("IP:"+remoteIP+":"+remotePort);
- directSock = new Socket(remoteIP,remotePort);
- proxy.out = directSock.getOutputStream();
- proxy.in = directSock.getInputStream();
- proxy.proxySocket = directSock;
- localIP = directSock.getLocalAddress();
- localPort = directSock.getLocalPort();
- }catch(IOException io_ex){
- throw new SocksException(Proxy.SOCKS_DIRECT_FAILED,
- "Direct connect failed:"+io_ex);
- }
- }
-
-}
+package net.sourceforge.jsocks;
+
+import java.net.*;
+import java.io.*;
+
+/**
+ * SocksSocket tryies to look very similar to normal Socket,
+ * while allowing connections through the SOCKS4 or 5 proxy.
+ * To use this class you will have to identify proxy you need
+ * to use, Proxy class allows you to set default proxy, which
+ * will be used by all Socks aware sockets. You can also create
+ * either Socks4Proxy or Socks5Proxy, and use them by passing to the
+ * appropriate constructors.
+ * <P>
+ * Using Socks package can be as easy as that:
+ *
+ * <pre><tt>
+ *
+ * import Socks.*;
+ * ....
+ *
+ * try{
+ * //Specify SOCKS5 proxy
+ * Proxy.setDefaultProxy("socks-proxy",1080);
+ *
+ * //OR you still use SOCKS4
+ * //Code below uses SOCKS4 proxy
+ * //Proxy.setDefaultProxy("socks-proxy",1080,userName);
+ *
+ * Socket s = SocksSocket("some.host.of.mine",13);
+ * readTimeFromSock(s);
+ * }catch(SocksException sock_ex){
+ * //Usually it will turn in more or less meaningfull message
+ * System.err.println("SocksException:"+sock_ex);
+ * }
+ *
+ * </tt></pre>
+ *<P>
+ * However if the need exist for more control, like resolving addresses
+ * remotely, or using some non-trivial authentication schemes, it can be done.
+ */
+
+public class SocksSocket extends Socket{
+ //Data members
+ protected Proxy proxy;
+ protected String localHost, remoteHost;
+ protected InetAddress localIP, remoteIP;
+ protected int localPort,remotePort;
+
+ private Socket directSock = null;
+
+ /**
+ * Tryies to connect to given host and port
+ * using default proxy. If no default proxy speciefied
+ * it throws SocksException with error code SOCKS_NO_PROXY.
+ @param host Machine to connect to.
+ @param port Port to which to connect.
+ * @see SocksSocket#SocksSocket(Proxy,String,int)
+ * @see Socks5Proxy#resolveAddrLocally
+ */
+ public SocksSocket(String host,int port)
+ throws SocksException,UnknownHostException{
+ this(Proxy.defaultProxy,host,port);
+ }
+ /**
+ * Connects to host port using given proxy server.
+ @param p Proxy to use.
+ @param host Machine to connect to.
+ @param port Port to which to connect.
+ @throws UnknownHostException
+ If one of the following happens:
+ <ol>
+
+ <li> Proxy settings say that address should be resolved locally, but
+ this fails.
+ <li> Proxy settings say that the host should be contacted directly but
+ host name can't be resolved.
+ </ol>
+ @throws SocksException
+ If one of the following happens:
+ <ul>
+ <li> Proxy is is null.
+ <li> Proxy settings say that the host should be contacted directly but
+ this fails.
+ <li> Socks Server can't be contacted.
+ <li> Authentication fails.
+ <li> Connection is not allowed by the SOCKS proxy.
+ <li> SOCKS proxy can't establish the connection.
+ <li> Any IO error occured.
+ <li> Any protocol error occured.
+ </ul>
+ @throws IOexception if anything is wrong with I/O.
+ @see Socks5Proxy#resolveAddrLocally
+ */
+ public SocksSocket(Proxy p, String host, int port) throws SocksException,
+ UnknownHostException {
+ remoteHost = host;
+ remotePort = port;
+ remoteIP = InetAddress.getByName(host);
+ doDirect();
+ }
+
+ /**
+ Connects to given ip and port using given Proxy server.
+ @param p Proxy to use.
+ @param ip Machine to connect to.
+ @param port Port to which to connect.
+
+ */
+ public SocksSocket(InetAddress ip, int port) throws SocksException{
+ this.remoteIP = ip;
+ this.remotePort = port;
+ this.remoteHost = ip.getHostName();
+ doDirect();
+ }
+
+
+ /**
+ * These 2 constructors are used by the SocksServerSocket.
+ * This socket simply overrides remoteHost, remotePort
+ */
+ protected SocksSocket(String host,int port,Proxy proxy){
+ this.remotePort = port;
+ this.proxy = proxy;
+ this.localIP = proxy.proxySocket.getLocalAddress();
+ this.localPort = proxy.proxySocket.getLocalPort();
+ this.remoteHost = host;
+ }
+ protected SocksSocket(InetAddress ip,int port,Proxy proxy){
+ remoteIP = ip;
+ remotePort = port;
+ this.proxy = proxy;
+ this.localIP = proxy.proxySocket.getLocalAddress();
+ this.localPort = proxy.proxySocket.getLocalPort();
+ remoteHost = remoteIP.getHostName();
+ }
+
+ /**
+ * Same as Socket
+ */
+ public void close() throws IOException{
+ if(proxy!= null)proxy.endSession();
+ proxy = null;
+ }
+ /**
+ * Same as Socket
+ */
+ public InputStream getInputStream(){
+ return proxy.in;
+ }
+ /**
+ * Same as Socket
+ */
+ public OutputStream getOutputStream(){
+ return proxy.out;
+ }
+ /**
+ * Same as Socket
+ */
+ public int getPort(){
+ return remotePort;
+ }
+ /**
+ * Returns remote host name, it is usefull in cases when addresses
+ * are resolved by proxy, and we can't create InetAddress object.
+ @return The name of the host this socket is connected to.
+ */
+ public String getHost(){
+ return remoteHost;
+ }
+ /**
+ * Get remote host as InetAddress object, might return null if
+ * addresses are resolved by proxy, and it is not possible to resolve
+ * it locally
+ @return Ip address of the host this socket is connected to, or null
+ if address was returned by the proxy as DOMAINNAME and can't be
+ resolved locally.
+ */
+ public InetAddress getInetAddress(){
+ if(remoteIP == null){
+ try{
+ remoteIP = InetAddress.getByName(remoteHost);
+ }catch(UnknownHostException e){
+ return null;
+ }
+ }
+ return remoteIP;
+ }
+
+ /**
+ * Get the port assigned by the proxy for the socket, not
+ * the port on locall machine as in Socket.
+ @return Port of the socket used on the proxy server.
+ */
+ public int getLocalPort(){
+ return localPort;
+ }
+
+ /**
+ * Get address assigned by proxy to make a remote connection,
+ * it might be different from the host specified for the proxy.
+ * Can return null if socks server returned this address as hostname
+ * and it can't be resolved locally, use getLocalHost() then.
+ @return Address proxy is using to make a connection.
+ */
+ public InetAddress getLocalAddress(){
+ if(localIP == null){
+ try{
+ localIP = InetAddress.getByName(localHost);
+ }catch(UnknownHostException e){
+ return null;
+ }
+ }
+ return localIP;
+ }
+ /**
+ Get name of the host, proxy has assigned to make a remote connection
+ for this socket. This method is usefull when proxy have returned
+ address as hostname, and we can't resolve it on this machine.
+ @return The name of the host proxy is using to make a connection.
+ */
+ public String getLocalHost(){
+ return localHost;
+ }
+
+ /**
+ Same as socket.
+ */
+ public void setSoLinger(boolean on,int val) throws SocketException{
+ proxy.proxySocket.setSoLinger(on,val);
+ }
+ /**
+ Same as socket.
+ */
+ public int getSoLinger(int timeout) throws SocketException{
+ return proxy.proxySocket.getSoLinger();
+ }
+ /**
+ Same as socket.
+ */
+ public void setSoTimeout(int timeout) throws SocketException{
+ proxy.proxySocket.setSoTimeout(timeout);
+ }
+ /**
+ Same as socket.
+ */
+ public int getSoTimeout(int timeout) throws SocketException{
+ return proxy.proxySocket.getSoTimeout();
+ }
+ /**
+ Same as socket.
+ */
+ public void setTcpNoDelay(boolean on) throws SocketException{
+ proxy.proxySocket.setTcpNoDelay(on);
+ }
+ /**
+ Same as socket.
+ */
+ public boolean getTcpNoDelay() throws SocketException{
+ return proxy.proxySocket.getTcpNoDelay();
+ }
+
+ /**
+ Get string representation of the socket.
+ */
+ public String toString(){
+ if(directSock!=null) return "Direct connection:"+directSock;
+ return ("Proxy:"+proxy+";"+"addr:"+remoteHost+",port:"+remotePort
+ +",localport:"+localPort);
+
+ }
+
+//Private Methods
+//////////////////
+
+ private void doDirect()throws SocksException{
+ try{
+ //System.out.println("IP:"+remoteIP+":"+remotePort);
+ directSock = new Socket(remoteIP,remotePort);
+ proxy.out = directSock.getOutputStream();
+ proxy.in = directSock.getInputStream();
+ proxy.proxySocket = directSock;
+ localIP = directSock.getLocalAddress();
+ localPort = directSock.getLocalPort();
+ }catch(IOException io_ex){
+ throw new SocksException(Proxy.SOCKS_DIRECT_FAILED,
+ "Direct connect failed:"+io_ex);
+ }
+ }
+
+}
diff --git a/src/net/sourceforge/jsocks/UDPEncapsulation.java b/src/net/sourceforge/jsocks/UDPEncapsulation.java
index efeb0ed..e965942 100644
--- a/src/net/sourceforge/jsocks/UDPEncapsulation.java
+++ b/src/net/sourceforge/jsocks/UDPEncapsulation.java
@@ -1,29 +1,29 @@
-package net.sourceforge.jsocks;
-/**
- This interface provides for datagram encapsulation for SOCKSv5 protocol.
- <p>
- SOCKSv5 allows for datagrams to be encapsulated for purposes of integrity
- and/or authenticity. How it should be done is aggreed during the
- authentication stage, and is authentication dependent. This interface is
- provided to allow this encapsulation.
- @see Authentication
-*/
-public interface UDPEncapsulation{
-
- /**
- This method should provide any authentication depended transformation
- on datagrams being send from/to the client.
-
- @param data Datagram data (including any SOCKS related bytes), to be
- encapsulated/decapsulated.
- @param out Wether the data is being send out. If true method should
- encapsulate/encrypt data, otherwise it should decapsulate/
- decrypt data.
- @throw IOException if for some reason data can be transformed correctly.
- @return Should return byte array containing data after transformation.
- It is possible to return same array as input, if transformation
- only involves bit mangling, and no additional data is being
- added or removed.
- */
- byte[] udpEncapsulate(byte[] data, boolean out) throws java.io.IOException;
-}
+package net.sourceforge.jsocks;
+/**
+ This interface provides for datagram encapsulation for SOCKSv5 protocol.
+ <p>
+ SOCKSv5 allows for datagrams to be encapsulated for purposes of integrity
+ and/or authenticity. How it should be done is aggreed during the
+ authentication stage, and is authentication dependent. This interface is
+ provided to allow this encapsulation.
+ @see Authentication
+*/
+public interface UDPEncapsulation{
+
+ /**
+ This method should provide any authentication depended transformation
+ on datagrams being send from/to the client.
+
+ @param data Datagram data (including any SOCKS related bytes), to be
+ encapsulated/decapsulated.
+ @param out Wether the data is being send out. If true method should
+ encapsulate/encrypt data, otherwise it should decapsulate/
+ decrypt data.
+ @throw IOException if for some reason data can be transformed correctly.
+ @return Should return byte array containing data after transformation.
+ It is possible to return same array as input, if transformation
+ only involves bit mangling, and no additional data is being
+ added or removed.
+ */
+ byte[] udpEncapsulate(byte[] data, boolean out) throws java.io.IOException;
+}
diff --git a/src/net/sourceforge/jsocks/UDPRelayServer.java b/src/net/sourceforge/jsocks/UDPRelayServer.java
index 682ab17..dfa6016 100644
--- a/src/net/sourceforge/jsocks/UDPRelayServer.java
+++ b/src/net/sourceforge/jsocks/UDPRelayServer.java
@@ -1,212 +1,212 @@
-package net.sourceforge.jsocks;
-import net.sourceforge.jsocks.server.*;
-import java.net.*;
-import java.io.*;
-
-/**
- UDP Relay server, used by ProxyServer to perform udp forwarding.
-*/
-class UDPRelayServer implements Runnable{
-
-
- DatagramSocket client_sock;
- DatagramSocket remote_sock;
-
- Socket controlConnection;
-
- int relayPort;
- InetAddress relayIP;
-
- Thread pipe_thread1,pipe_thread2;
- Thread master_thread;
-
- ServerAuthenticator auth;
-
- long lastReadTime;
-
- static PrintStream log = null;
- static Proxy proxy = null;
- static int datagramSize = 0xFFFF;//64K, a bit more than max udp size
- static int iddleTimeout = 180000;//3 minutes
-
-
- /**
- Constructs UDP relay server to communicate with client
- on given ip and port.
- @param clientIP Address of the client from whom datagrams
- will be recieved and to whom they will be forwarded.
- @param clientPort Clients port.
- @param master_thread Thread which will be interrupted, when
- UDP relay server stoppes for some reason.
- @param controlConnection Socket which will be closed, before
- interrupting the master thread, it is introduced due to a bug
- in windows JVM which does not throw InterruptedIOException in
- threads which block in I/O operation.
- */
- public UDPRelayServer(InetAddress clientIP,int clientPort,
- Thread master_thread,
- Socket controlConnection,
- ServerAuthenticator auth)
- throws IOException{
- this.master_thread = master_thread;
- this.controlConnection = controlConnection;
- this.auth = auth;
-
- client_sock = new Socks5DatagramSocket(true,auth.getUdpEncapsulation(),
- clientIP,clientPort);
- relayPort = client_sock.getLocalPort();
- relayIP = client_sock.getLocalAddress();
-
- if(relayIP.getHostAddress().equals("0.0.0.0"))
- relayIP = InetAddress.getLocalHost();
-
- if(proxy == null)
- remote_sock = new DatagramSocket();
- else
- remote_sock = new Socks5DatagramSocket(proxy,0,null);
- }
-
-
-//Public methods
-/////////////////
-
-
- /**
- Sets the timeout for UDPRelay server.<br>
- Zero timeout implies infinity.<br>
- Default timeout is 3 minutes.
- */
-
- static public void setTimeout(int timeout){
- iddleTimeout = timeout;
- }
-
-
- /**
- Sets the size of the datagrams used in the UDPRelayServer.<br>
- Default size is 64K, a bit more than maximum possible size of the
- datagram.
- */
- static public void setDatagramSize(int size){
- datagramSize = size;
- }
-
- /**
- Port to which client should send datagram for association.
- */
- public int getRelayPort(){
- return relayPort;
- }
- /**
- IP address to which client should send datagrams for association.
- */
- public InetAddress getRelayIP(){
- return relayIP;
- }
-
- /**
- Starts udp relay server.
- Spawns two threads of execution and returns.
- */
- public void start() throws IOException{
- remote_sock.setSoTimeout(iddleTimeout);
- client_sock.setSoTimeout(iddleTimeout);
-
- log("Starting UDP relay server on "+relayIP+":"+relayPort);
- log("Remote socket "+remote_sock.getLocalAddress()+":"+
- remote_sock.getLocalPort());
-
- pipe_thread1 = new Thread(this,"pipe1");
- pipe_thread2 = new Thread(this,"pipe2");
-
- lastReadTime = System.currentTimeMillis();
-
- pipe_thread1.start();
- pipe_thread2.start();
- }
-
- /**
- Stops Relay server.
- <p>
- Does not close control connection, does not interrupt master_thread.
- */
- public synchronized void stop(){
- master_thread = null;
- controlConnection = null;
- abort();
- }
-
-//Runnable interface
-////////////////////
- public void run(){
- try{
- if(Thread.currentThread().getName().equals("pipe1"))
- pipe(remote_sock,client_sock,false);
- else
- pipe(client_sock,remote_sock,true);
- }catch(IOException ioe){
- }finally{
- abort();
- log("UDP Pipe thread "+Thread.currentThread().getName()+" stopped.");
- }
-
- }
-
-//Private methods
-/////////////////
- private synchronized void abort(){
- if(pipe_thread1 == null) return;
-
- log("Aborting UDP Relay Server");
-
- remote_sock.close();
- client_sock.close();
-
- if(controlConnection != null)
- try{ controlConnection.close();} catch(IOException ioe){}
-
- if(master_thread!=null) master_thread.interrupt();
-
- pipe_thread1.interrupt();
- pipe_thread2.interrupt();
-
- pipe_thread1 = null;
- }
-
-
- static private void log(String s){
- if(log != null){
- log.println(s);
- log.flush();
- }
- }
-
- private void pipe(DatagramSocket from,DatagramSocket to,boolean out)
- throws IOException{
- byte[] data = new byte[datagramSize];
- DatagramPacket dp = new DatagramPacket(data,data.length);
-
- while(true){
- try{
- from.receive(dp);
- lastReadTime = System.currentTimeMillis();
-
- if(auth.checkRequest(dp,out))
- to.send(dp);
-
- }catch(UnknownHostException uhe){
- log("Dropping datagram for unknown host");
- }catch(InterruptedIOException iioe){
- //log("Interrupted: "+iioe);
- //If we were interrupted by other thread.
- if(iddleTimeout == 0) return;
-
- //If last datagram was received, long time ago, return.
- long timeSinceRead = System.currentTimeMillis() - lastReadTime;
- if(timeSinceRead >= iddleTimeout -100) //-100 for adjustment
- return;
- }
- dp.setLength(data.length);
- }
- }
-}
+package net.sourceforge.jsocks;
+import net.sourceforge.jsocks.server.*;
+import java.net.*;
+import java.io.*;
+
+/**
+ UDP Relay server, used by ProxyServer to perform udp forwarding.
+*/
+class UDPRelayServer implements Runnable{
+
+
+ DatagramSocket client_sock;
+ DatagramSocket remote_sock;
+
+ Socket controlConnection;
+
+ int relayPort;
+ InetAddress relayIP;
+
+ Thread pipe_thread1,pipe_thread2;
+ Thread master_thread;
+
+ ServerAuthenticator auth;
+
+ long lastReadTime;
+
+ static PrintStream log = null;
+ static Proxy proxy = null;
+ static int datagramSize = 0xFFFF;//64K, a bit more than max udp size
+ static int iddleTimeout = 180000;//3 minutes
+
+
+ /**
+ Constructs UDP relay server to communicate with client
+ on given ip and port.
+ @param clientIP Address of the client from whom datagrams
+ will be recieved and to whom they will be forwarded.
+ @param clientPort Clients port.
+ @param master_thread Thread which will be interrupted, when
+ UDP relay server stoppes for some reason.
+ @param controlConnection Socket which will be closed, before
+ interrupting the master thread, it is introduced due to a bug
+ in windows JVM which does not throw InterruptedIOException in
+ threads which block in I/O operation.
+ */
+ public UDPRelayServer(InetAddress clientIP,int clientPort,
+ Thread master_thread,
+ Socket controlConnection,
+ ServerAuthenticator auth)
+ throws IOException{
+ this.master_thread = master_thread;
+ this.controlConnection = controlConnection;
+ this.auth = auth;
+
+ client_sock = new Socks5DatagramSocket(true,auth.getUdpEncapsulation(),
+ clientIP,clientPort);
+ relayPort = client_sock.getLocalPort();
+ relayIP = client_sock.getLocalAddress();
+
+ if(relayIP.getHostAddress().equals("0.0.0.0"))
+ relayIP = InetAddress.getLocalHost();
+
+ if(proxy == null)
+ remote_sock = new DatagramSocket();
+ else
+ remote_sock = new Socks5DatagramSocket(proxy,0,null);
+ }
+
+
+//Public methods
+/////////////////
+
+
+ /**
+ Sets the timeout for UDPRelay server.<br>
+ Zero timeout implies infinity.<br>
+ Default timeout is 3 minutes.
+ */
+
+ static public void setTimeout(int timeout){
+ iddleTimeout = timeout;
+ }
+
+
+ /**
+ Sets the size of the datagrams used in the UDPRelayServer.<br>
+ Default size is 64K, a bit more than maximum possible size of the
+ datagram.
+ */
+ static public void setDatagramSize(int size){
+ datagramSize = size;
+ }
+
+ /**
+ Port to which client should send datagram for association.
+ */
+ public int getRelayPort(){
+ return relayPort;
+ }
+ /**
+ IP address to which client should send datagrams for association.
+ */
+ public InetAddress getRelayIP(){
+ return relayIP;
+ }
+
+ /**
+ Starts udp relay server.
+ Spawns two threads of execution and returns.
+ */
+ public void start() throws IOException{
+ remote_sock.setSoTimeout(iddleTimeout);
+ client_sock.setSoTimeout(iddleTimeout);
+
+ log("Starting UDP relay server on "+relayIP+":"+relayPort);
+ log("Remote socket "+remote_sock.getLocalAddress()+":"+
+ remote_sock.getLocalPort());
+
+ pipe_thread1 = new Thread(this,"pipe1");
+ pipe_thread2 = new Thread(this,"pipe2");
+
+ lastReadTime = System.currentTimeMillis();
+
+ pipe_thread1.start();
+ pipe_thread2.start();
+ }
+
+ /**
+ Stops Relay server.
+ <p>
+ Does not close control connection, does not interrupt master_thread.
+ */
+ public synchronized void stop(){
+ master_thread = null;
+ controlConnection = null;
+ abort();
+ }
+
+//Runnable interface
+////////////////////
+ public void run(){
+ try{
+ if(Thread.currentThread().getName().equals("pipe1"))
+ pipe(remote_sock,client_sock,false);
+ else
+ pipe(client_sock,remote_sock,true);
+ }catch(IOException ioe){
+ }finally{
+ abort();
+ log("UDP Pipe thread "+Thread.currentThread().getName()+" stopped.");
+ }
+
+ }
+
+//Private methods
+/////////////////
+ private synchronized void abort(){
+ if(pipe_thread1 == null) return;
+
+ log("Aborting UDP Relay Server");
+
+ remote_sock.close();
+ client_sock.close();
+
+ if(controlConnection != null)
+ try{ controlConnection.close();} catch(IOException ioe){}
+
+ if(master_thread!=null) master_thread.interrupt();
+
+ pipe_thread1.interrupt();
+ pipe_thread2.interrupt();
+
+ pipe_thread1 = null;
+ }
+
+
+ static private void log(String s){
+ if(log != null){
+ log.println(s);
+ log.flush();
+ }
+ }
+
+ private void pipe(DatagramSocket from,DatagramSocket to,boolean out)
+ throws IOException{
+ byte[] data = new byte[datagramSize];
+ DatagramPacket dp = new DatagramPacket(data,data.length);
+
+ while(true){
+ try{
+ from.receive(dp);
+ lastReadTime = System.currentTimeMillis();
+
+ if(auth.checkRequest(dp,out))
+ to.send(dp);
+
+ }catch(UnknownHostException uhe){
+ log("Dropping datagram for unknown host");
+ }catch(InterruptedIOException iioe){
+ //log("Interrupted: "+iioe);
+ //If we were interrupted by other thread.
+ if(iddleTimeout == 0) return;
+
+ //If last datagram was received, long time ago, return.
+ long timeSinceRead = System.currentTimeMillis() - lastReadTime;
+ if(timeSinceRead >= iddleTimeout -100) //-100 for adjustment
+ return;
+ }
+ dp.setLength(data.length);
+ }
+ }
+}
diff --git a/src/net/sourceforge/jsocks/server/ServerAuthenticator.java b/src/net/sourceforge/jsocks/server/ServerAuthenticator.java
index 6fb99e0..cb7f0af 100644
--- a/src/net/sourceforge/jsocks/server/ServerAuthenticator.java
+++ b/src/net/sourceforge/jsocks/server/ServerAuthenticator.java
@@ -1,120 +1,120 @@
-package net.sourceforge.jsocks.server;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.DatagramPacket;
-import java.net.Socket;
-
-import net.sourceforge.jsocks.ProxyMessage;
-import net.sourceforge.jsocks.UDPEncapsulation;
-
-/**
- Classes implementing this interface should provide socks server with
- authentication and authorization of users.
-**/
-public interface ServerAuthenticator{
-
- /**
- This method is called when a new connection accepted by the server.
- <p>
- At this point no data have been extracted from the connection. It is
- responsibility of this method to ensure that the next byte in the
- stream after this method have been called is the first byte of the
- socks request message. For SOCKSv4 there is no authentication data and
- the first byte in the stream is part of the request. With SOCKSv5 however
- there is an authentication data first. It is expected that implementaions
- will process this authentication data.
- <p>
- If authentication was successful an instance of ServerAuthentication
- should be returned, it later will be used by the server to perform
- authorization and some other things. If authentication fails null should
- be returned, or an exception may be thrown.
-
- @param s Accepted Socket.
- @return An instance of ServerAuthenticator to be used for this connection
- or null
- */
- ServerAuthenticator startSession(Socket s) throws IOException;
-
- /**
- This method should return input stream which should be used on the
- accepted socket.
- <p>
- SOCKSv5 allows to have multiple authentication methods, and these methods
- might require some kind of transformations being made on the data.
- <p>
- This method is called on the object returned from the startSession
- function.
- */
- InputStream getInputStream();
- /**
- This method should return output stream to use to write to the accepted
- socket.
- <p>
- SOCKSv5 allows to have multiple authentication methods, and these methods
- might require some kind of transformations being made on the data.
- <p>
- This method is called on the object returned from the startSession
- function.
- */
- OutputStream getOutputStream();
-
- /**
- This method should return UDPEncapsulation, which should be used
- on the datagrams being send in/out.
- <p>
- If no transformation should be done on the datagrams, this method
- should return null.
- <p>
- This method is called on the object returned from the startSession
- function.
- */
-
- UDPEncapsulation getUdpEncapsulation();
-
- /**
- This method is called when a request have been read.
- <p>
- Implementation should decide wether to grant request or not. Returning
- true implies granting the request, false means request should be rejected.
- <p>
- This method is called on the object returned from the startSession
- function.
- @param msg Request message.
- @return true to grant request, false to reject it.
- */
- boolean checkRequest(ProxyMessage msg);
-
- /**
- This method is called when datagram is received by the server.
- <p>
- Implementaions should decide wether it should be forwarded or dropped.
- It is expecteed that implementation will use datagram address and port
- information to make a decision, as well as anything else. Address and
- port of the datagram are always correspond to remote machine. It is
- either destination or source address. If out is true address is destination
- address, else it is a source address, address of the machine from which
- datagram have been received for the client.
- <p>
- Implementaions should return true if the datagram is to be forwarded, and
- false if the datagram should be dropped.
- <p>
- This method is called on the object returned from the startSession
- function.
-
- @param out If true the datagram is being send out(from the client),
- otherwise it is an incoming datagram.
- @return True to forward datagram false drop it silently.
- */
- boolean checkRequest(DatagramPacket dp, boolean out);
-
- /**
- This method is called when session is completed. Either due to normal
- termination or due to any error condition.
- <p>
- This method is called on the object returned from the startSession
- function.
- */
- void endSession();
-}
+package net.sourceforge.jsocks.server;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.DatagramPacket;
+import java.net.Socket;
+
+import net.sourceforge.jsocks.ProxyMessage;
+import net.sourceforge.jsocks.UDPEncapsulation;
+
+/**
+ Classes implementing this interface should provide socks server with
+ authentication and authorization of users.
+**/
+public interface ServerAuthenticator{
+
+ /**
+ This method is called when a new connection accepted by the server.
+ <p>
+ At this point no data have been extracted from the connection. It is
+ responsibility of this method to ensure that the next byte in the
+ stream after this method have been called is the first byte of the
+ socks request message. For SOCKSv4 there is no authentication data and
+ the first byte in the stream is part of the request. With SOCKSv5 however
+ there is an authentication data first. It is expected that implementaions
+ will process this authentication data.
+ <p>
+ If authentication was successful an instance of ServerAuthentication
+ should be returned, it later will be used by the server to perform
+ authorization and some other things. If authentication fails null should
+ be returned, or an exception may be thrown.
+
+ @param s Accepted Socket.
+ @return An instance of ServerAuthenticator to be used for this connection
+ or null
+ */
+ ServerAuthenticator startSession(Socket s) throws IOException;
+
+ /**
+ This method should return input stream which should be used on the
+ accepted socket.
+ <p>
+ SOCKSv5 allows to have multiple authentication methods, and these methods
+ might require some kind of transformations being made on the data.
+ <p>
+ This method is called on the object returned from the startSession
+ function.
+ */
+ InputStream getInputStream();
+ /**
+ This method should return output stream to use to write to the accepted
+ socket.
+ <p>
+ SOCKSv5 allows to have multiple authentication methods, and these methods
+ might require some kind of transformations being made on the data.
+ <p>
+ This method is called on the object returned from the startSession
+ function.
+ */
+ OutputStream getOutputStream();
+
+ /**
+ This method should return UDPEncapsulation, which should be used
+ on the datagrams being send in/out.
+ <p>
+ If no transformation should be done on the datagrams, this method
+ should return null.
+ <p>
+ This method is called on the object returned from the startSession
+ function.
+ */
+
+ UDPEncapsulation getUdpEncapsulation();
+
+ /**
+ This method is called when a request have been read.
+ <p>
+ Implementation should decide wether to grant request or not. Returning
+ true implies granting the request, false means request should be rejected.
+ <p>
+ This method is called on the object returned from the startSession
+ function.
+ @param msg Request message.
+ @return true to grant request, false to reject it.
+ */
+ boolean checkRequest(ProxyMessage msg);
+
+ /**
+ This method is called when datagram is received by the server.
+ <p>
+ Implementaions should decide wether it should be forwarded or dropped.
+ It is expecteed that implementation will use datagram address and port
+ information to make a decision, as well as anything else. Address and
+ port of the datagram are always correspond to remote machine. It is
+ either destination or source address. If out is true address is destination
+ address, else it is a source address, address of the machine from which
+ datagram have been received for the client.
+ <p>
+ Implementaions should return true if the datagram is to be forwarded, and
+ false if the datagram should be dropped.
+ <p>
+ This method is called on the object returned from the startSession
+ function.
+
+ @param out If true the datagram is being send out(from the client),
+ otherwise it is an incoming datagram.
+ @return True to forward datagram false drop it silently.
+ */
+ boolean checkRequest(DatagramPacket dp, boolean out);
+
+ /**
+ This method is called when session is completed. Either due to normal
+ termination or due to any error condition.
+ <p>
+ This method is called on the object returned from the startSession
+ function.
+ */
+ void endSession();
+}
diff --git a/src/net/sourceforge/jsocks/server/ServerAuthenticatorNone.java b/src/net/sourceforge/jsocks/server/ServerAuthenticatorNone.java
index 30eb265..e4edbe7 100644
--- a/src/net/sourceforge/jsocks/server/ServerAuthenticatorNone.java
+++ b/src/net/sourceforge/jsocks/server/ServerAuthenticatorNone.java
@@ -1,169 +1,169 @@
-package net.sourceforge.jsocks.server;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PushbackInputStream;
-import java.net.Socket;
-
-import net.sourceforge.jsocks.ProxyMessage;
-import net.sourceforge.jsocks.UDPEncapsulation;
-
-/**
- An implementation of ServerAuthenticator, which does <b>not</b> do
- any authentication.
-<P>
-<FONT size="+3" color ="FF0000"> Warning!!</font><br> Should not be
-used on machines which are not behind the firewall.
-<p>
-It is only provided to make implementing other authentication schemes
-easier.<br>
-For Example: <tt><pre>
- class MyAuth extends socks.server.ServerAuthenticator{
- ...
- public ServerAuthenticator startSession(java.net.Socket s){
- if(!checkHost(s.getInetAddress()) return null;
- return super.startSession(s);
- }
-
- boolean checkHost(java.net.Inetaddress addr){
- boolean allow;
- //Do it somehow
- return allow;
- }
- }
-</pre></tt>
-*/
-public class ServerAuthenticatorNone implements ServerAuthenticator{
-
- static final byte[] socks5response = {5,0};
-
- InputStream in;
- OutputStream out;
-
- /**
- Creates new instance of the ServerAuthenticatorNone.
- */
- public ServerAuthenticatorNone(){
- this.in = null;
- this.out = null;
- }
- /**
- Constructs new ServerAuthenticatorNone object suitable for returning
- from the startSession function.
- @param in Input stream to return from getInputStream method.
- @param out Output stream to return from getOutputStream method.
- */
- public ServerAuthenticatorNone(InputStream in, OutputStream out){
- this.in = in;
- this.out = out;
- }
- /**
- Grants access to everyone.Removes authentication related bytes from
- the stream, when a SOCKS5 connection is being made, selects an
- authentication NONE.
- */
- public ServerAuthenticator startSession(Socket s)
- throws IOException{
-
- PushbackInputStream in = new PushbackInputStream(s.getInputStream());
- OutputStream out = s.getOutputStream();
-
- int version = in.read();
- if(version == 5){
- if(!selectSocks5Authentication(in,out,0))
- return null;
- }else if(version == 4){
- //Else it is the request message allready, version 4
- in.unread(version);
- }else
- return null;
-
-
- return new ServerAuthenticatorNone(in,out);
- }
-
- /**
- Get input stream.
- @return Input stream speciefied in the constructor.
- */
- public InputStream getInputStream(){
- return in;
- }
- /**
- Get output stream.
- @return Output stream speciefied in the constructor.
- */
- public OutputStream getOutputStream(){
- return out;
- }
- /**
- Allways returns null.
- @return null
- */
- public UDPEncapsulation getUdpEncapsulation(){
- return null;
- }
-
- /**
- Allways returns true.
- */
- public boolean checkRequest(ProxyMessage msg){
- return true;
- }
-
- /**
- Allways returns true.
- */
- public boolean checkRequest(java.net.DatagramPacket dp, boolean out){
- return true;
- }
-
- /**
- Does nothing.
- */
- public void endSession(){
- }
-
- /**
- Convinience routine for selecting SOCKSv5 authentication.
- <p>
- This method reads in authentication methods that client supports,
- checks wether it supports given method. If it does, the notification
- method is written back to client, that this method have been chosen
- for authentication. If given method was not found, authentication
- failure message is send to client ([5,FF]).
- @param in Input stream, version byte should be removed from the stream
- before calling this method.
- @param out Output stream.
- @param methodId Method which should be selected.
- @return true if methodId was found, false otherwise.
- */
- static public boolean selectSocks5Authentication(InputStream in,
- OutputStream out,
- int methodId)
- throws IOException{
-
- int num_methods = in.read();
- if (num_methods <= 0) return false;
- byte method_ids[] = new byte[num_methods];
- byte response[] = new byte[2];
- boolean found = false;
-
- response[0] = (byte) 5; //SOCKS version
- response[1] = (byte) 0xFF; //Not found, we are pessimistic
-
- int bread = 0; //bytes read so far
- while(bread < num_methods)
- bread += in.read(method_ids,bread,num_methods-bread);
-
- for(int i=0;i<num_methods;++i)
- if(method_ids[i] == methodId){
- found = true;
- response[1] = (byte) methodId;
- break;
- }
-
- out.write(response);
- return found;
- }
-}
+package net.sourceforge.jsocks.server;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PushbackInputStream;
+import java.net.Socket;
+
+import net.sourceforge.jsocks.ProxyMessage;
+import net.sourceforge.jsocks.UDPEncapsulation;
+
+/**
+ An implementation of ServerAuthenticator, which does <b>not</b> do
+ any authentication.
+<P>
+<FONT size="+3" color ="FF0000"> Warning!!</font><br> Should not be
+used on machines which are not behind the firewall.
+<p>
+It is only provided to make implementing other authentication schemes
+easier.<br>
+For Example: <tt><pre>
+ class MyAuth extends socks.server.ServerAuthenticator{
+ ...
+ public ServerAuthenticator startSession(java.net.Socket s){
+ if(!checkHost(s.getInetAddress()) return null;
+ return super.startSession(s);
+ }
+
+ boolean checkHost(java.net.Inetaddress addr){
+ boolean allow;
+ //Do it somehow
+ return allow;
+ }
+ }
+</pre></tt>
+*/
+public class ServerAuthenticatorNone implements ServerAuthenticator{
+
+ static final byte[] socks5response = {5,0};
+
+ InputStream in;
+ OutputStream out;
+
+ /**
+ Creates new instance of the ServerAuthenticatorNone.
+ */
+ public ServerAuthenticatorNone(){
+ this.in = null;
+ this.out = null;
+ }
+ /**
+ Constructs new ServerAuthenticatorNone object suitable for returning
+ from the startSession function.
+ @param in Input stream to return from getInputStream method.
+ @param out Output stream to return from getOutputStream method.
+ */
+ public ServerAuthenticatorNone(InputStream in, OutputStream out){
+ this.in = in;
+ this.out = out;
+ }
+ /**
+ Grants access to everyone.Removes authentication related bytes from
+ the stream, when a SOCKS5 connection is being made, selects an
+ authentication NONE.
+ */
+ public ServerAuthenticator startSession(Socket s)
+ throws IOException{
+
+ PushbackInputStream in = new PushbackInputStream(s.getInputStream());
+ OutputStream out = s.getOutputStream();
+
+ int version = in.read();
+ if(version == 5){
+ if(!selectSocks5Authentication(in,out,0))
+ return null;
+ }else if(version == 4){
+ //Else it is the request message allready, version 4
+ in.unread(version);
+ }else
+ return null;
+
+
+ return new ServerAuthenticatorNone(in,out);
+ }
+
+ /**
+ Get input stream.
+ @return Input stream speciefied in the constructor.
+ */
+ public InputStream getInputStream(){
+ return in;
+ }
+ /**
+ Get output stream.
+ @return Output stream speciefied in the constructor.
+ */
+ public OutputStream getOutputStream(){
+ return out;
+ }
+ /**
+ Allways returns null.
+ @return null
+ */
+ public UDPEncapsulation getUdpEncapsulation(){
+ return null;
+ }
+
+ /**
+ Allways returns true.
+ */
+ public boolean checkRequest(ProxyMessage msg){
+ return true;
+ }
+
+ /**
+ Allways returns true.
+ */
+ public boolean checkRequest(java.net.DatagramPacket dp, boolean out){
+ return true;
+ }
+
+ /**
+ Does nothing.
+ */
+ public void endSession(){
+ }
+
+ /**
+ Convinience routine for selecting SOCKSv5 authentication.
+ <p>
+ This method reads in authentication methods that client supports,
+ checks wether it supports given method. If it does, the notification
+ method is written back to client, that this method have been chosen
+ for authentication. If given method was not found, authentication
+ failure message is send to client ([5,FF]).
+ @param in Input stream, version byte should be removed from the stream
+ before calling this method.
+ @param out Output stream.
+ @param methodId Method which should be selected.
+ @return true if methodId was found, false otherwise.
+ */
+ static public boolean selectSocks5Authentication(InputStream in,
+ OutputStream out,
+ int methodId)
+ throws IOException{
+
+ int num_methods = in.read();
+ if (num_methods <= 0) return false;
+ byte method_ids[] = new byte[num_methods];
+ byte response[] = new byte[2];
+ boolean found = false;
+
+ response[0] = (byte) 5; //SOCKS version
+ response[1] = (byte) 0xFF; //Not found, we are pessimistic
+
+ int bread = 0; //bytes read so far
+ while(bread < num_methods)
+ bread += in.read(method_ids,bread,num_methods-bread);
+
+ for(int i=0;i<num_methods;++i)
+ if(method_ids[i] == methodId){
+ found = true;
+ response[1] = (byte) methodId;
+ break;
+ }
+
+ out.write(response);
+ return found;
+ }
+}
diff --git a/src/org/connectbot/ActionBarWrapper.java b/src/org/connectbot/ActionBarWrapper.java
new file mode 100644
index 0000000..0c7b65d
--- /dev/null
+++ b/src/org/connectbot/ActionBarWrapper.java
@@ -0,0 +1,83 @@
+/*
+ * ConnectBot: simple, powerful, open-source SSH client for Android
+ * Copyright 2007 Kenny Root, Jeffrey Sharkey
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.connectbot;
+
+import org.connectbot.util.PreferenceConstants;
+
+import android.app.Activity;
+import android.app.ActionBar;
+
+public abstract class ActionBarWrapper {
+ public interface OnMenuVisibilityListener {
+ public void onMenuVisibilityChanged(boolean isVisible);
+ }
+
+ public static ActionBarWrapper getActionBar(Activity activity) {
+ if (PreferenceConstants.PRE_HONEYCOMB)
+ return new DummyActionBar();
+ else
+ return new RealActionBar(activity);
+ }
+
+ public void hide() {
+ }
+
+ public void show() {
+ }
+
+ public void addOnMenuVisibilityListener(OnMenuVisibilityListener listener) {
+ }
+
+ public void setDisplayHomeAsUpEnabled(boolean showHomeAsUp) {
+ }
+
+ private static class DummyActionBar extends ActionBarWrapper {
+ }
+
+ private static class RealActionBar extends ActionBarWrapper {
+ private final ActionBar actionBar;
+
+ public RealActionBar(Activity activity) {
+ actionBar = activity.getActionBar();
+ }
+
+ @Override
+ public void hide() {
+ actionBar.hide();
+ }
+
+ @Override
+ public void show() {
+ actionBar.show();
+ }
+
+ @Override
+ public void addOnMenuVisibilityListener(final OnMenuVisibilityListener listener) {
+ actionBar.addOnMenuVisibilityListener(new ActionBar.OnMenuVisibilityListener() {
+ public void onMenuVisibilityChanged(boolean isVisible) {
+ listener.onMenuVisibilityChanged(isVisible);
+ }
+ });
+ }
+
+ @Override
+ public void setDisplayHomeAsUpEnabled(boolean showHomeAsUp) {
+ actionBar.setDisplayHomeAsUpEnabled(showHomeAsUp);
+ }
+ }
+}
diff --git a/src/org/connectbot/ColorsActivity.java b/src/org/connectbot/ColorsActivity.java
index 38336f7..be5dfb1 100644
--- a/src/org/connectbot/ColorsActivity.java
+++ b/src/org/connectbot/ColorsActivity.java
@@ -17,6 +17,7 @@
package org.connectbot;
+import java.text.NumberFormat;
import java.util.Arrays;
import java.util.List;
@@ -30,6 +31,7 @@ import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.os.Bundle;
+import android.util.DisplayMetrics;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@@ -54,11 +56,12 @@ public class ColorsActivity extends Activity implements OnItemClickListener, OnC
private int mColorScheme;
private List<Integer> mColorList;
- private HostDatabase hostdb;
+ private HostDatabase mHostDb;
private int mCurrentColor = 0;
private int[] mDefaultColors;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -71,10 +74,10 @@ public class ColorsActivity extends Activity implements OnItemClickListener, OnC
mColorScheme = HostDatabase.DEFAULT_COLOR_SCHEME;
- hostdb = new HostDatabase(this);
+ mHostDb = new HostDatabase(this);
- mColorList = Arrays.asList(hostdb.getColorsForScheme(mColorScheme));
- mDefaultColors = hostdb.getDefaultColorsForScheme(mColorScheme);
+ mColorList = Arrays.asList(mHostDb.getColorsForScheme(mColorScheme));
+ mDefaultColors = mHostDb.getDefaultColorsForScheme(mColorScheme);
mColorGrid = (GridView) findViewById(R.id.color_grid);
mColorGrid.setAdapter(new ColorsAdapter(true));
@@ -82,12 +85,12 @@ public class ColorsActivity extends Activity implements OnItemClickListener, OnC
mColorGrid.setSelection(0);
mFgSpinner = (Spinner) findViewById(R.id.fg);
- mFgSpinner.setAdapter(new ColorsAdapter(false));
+ mFgSpinner.setAdapter(new ColorsAdapter(false, R.string.colors_fg_label));
mFgSpinner.setSelection(mDefaultColors[0]);
mFgSpinner.setOnItemSelectedListener(this);
mBgSpinner = (Spinner) findViewById(R.id.bg);
- mBgSpinner.setAdapter(new ColorsAdapter(false));
+ mBgSpinner.setAdapter(new ColorsAdapter(false, R.string.color_bg_label));
mBgSpinner.setSelection(mDefaultColors[1]);
mBgSpinner.setOnItemSelectedListener(this);
}
@@ -96,9 +99,9 @@ public class ColorsActivity extends Activity implements OnItemClickListener, OnC
protected void onDestroy() {
super.onDestroy();
- if (hostdb != null) {
- hostdb.close();
- hostdb = null;
+ if (mHostDb != null) {
+ mHostDb.close();
+ mHostDb = null;
}
}
@@ -106,22 +109,28 @@ public class ColorsActivity extends Activity implements OnItemClickListener, OnC
protected void onResume() {
super.onResume();
- if (hostdb == null)
- hostdb = new HostDatabase(this);
+ if (mHostDb == null)
+ mHostDb = new HostDatabase(this);
}
private class ColorsAdapter extends BaseAdapter {
- private boolean mSquareViews;
+ private final boolean mSquareViews;
+ private final int mResourceLabel;
public ColorsAdapter(boolean squareViews) {
+ this(squareViews, -1);
+ }
+
+ public ColorsAdapter(boolean squareViews, int resourceLabel) {
mSquareViews = squareViews;
+ mResourceLabel = resourceLabel;
}
public View getView(int position, View convertView, ViewGroup parent) {
ColorView c;
if (convertView == null) {
- c = new ColorView(ColorsActivity.this, mSquareViews);
+ c = new ColorView(ColorsActivity.this, mResourceLabel, mSquareViews);
} else {
c = (ColorView) convertView;
}
@@ -146,7 +155,16 @@ public class ColorsActivity extends Activity implements OnItemClickListener, OnC
}
private class ColorView extends View {
- private boolean mSquare;
+ /** The font size displayed in the GridView entries. */
+ private static final float FONT_SIZE_DP = 20f;
+
+ /** Margin around the GridView entries. */
+ private static final float MARGIN_DP = 10f;
+
+ private final boolean mSquare;
+
+ private final int mResourceLabel;
+ private final NumberFormat mNumberFormatter;
private Paint mTextPaint;
private Paint mShadowPaint;
@@ -159,14 +177,19 @@ public class ColorsActivity extends Activity implements OnItemClickListener, OnC
private int mWidthCenter;
private int mHeightCenter;
- public ColorView(Context context, boolean square) {
+ public ColorView(Context context, int resourceLabel, boolean square) {
super(context);
mSquare = square;
+ mResourceLabel = resourceLabel;
+
+ mNumberFormatter = NumberFormat.getIntegerInstance(getContext().getResources().getConfiguration().locale);
+
+ DisplayMetrics metrics = context.getResources().getDisplayMetrics();
mTextPaint = new Paint();
mTextPaint.setAntiAlias(true);
- mTextPaint.setTextSize(16);
+ mTextPaint.setTextSize((int) (metrics.density * FONT_SIZE_DP + 0.5f));
mTextPaint.setColor(0xFFFFFFFF);
mTextPaint.setTextAlign(Paint.Align.CENTER);
@@ -177,7 +200,8 @@ public class ColorsActivity extends Activity implements OnItemClickListener, OnC
mShadowPaint.setStrokeWidth(4f);
mShadowPaint.setColor(0xFF000000);
- setPadding(10, 10, 10, 10);
+ int marginPx = (int) (MARGIN_DP * metrics.density + 0.5f);
+ setPadding(marginPx, marginPx, marginPx, marginPx);
}
public void setColor(int color) {
@@ -185,7 +209,11 @@ public class ColorsActivity extends Activity implements OnItemClickListener, OnC
}
public void setNumber(int number) {
- mText = Integer.toString(number);
+ if (mResourceLabel != -1) {
+ mText = getContext().getResources().getString(mResourceLabel, number);
+ } else {
+ mText = mNumberFormatter.format(number);
+ }
}
@Override
@@ -193,10 +221,12 @@ public class ColorsActivity extends Activity implements OnItemClickListener, OnC
int width = measureWidth(widthMeasureSpec);
int height;
- if (mSquare)
+ if (mSquare) {
+ //noinspection SuspiciousNameCombination
height = width;
- else
+ } else {
height = measureHeight(heightMeasureSpec);
+ }
mAscent = (int) mTextPaint.ascent();
mWidthCenter = width / 2;
@@ -206,7 +236,8 @@ public class ColorsActivity extends Activity implements OnItemClickListener, OnC
}
private int measureWidth(int measureSpec) {
- int result = 0;
+ int result;
+
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
@@ -228,7 +259,8 @@ public class ColorsActivity extends Activity implements OnItemClickListener, OnC
}
private int measureHeight(int measureSpec) {
- int result = 0;
+ int result;
+
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
@@ -249,7 +281,6 @@ public class ColorsActivity extends Activity implements OnItemClickListener, OnC
return result;
}
-
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
@@ -273,7 +304,7 @@ public class ColorsActivity extends Activity implements OnItemClickListener, OnC
public void onNothingSelected(AdapterView<?> arg0) { }
public void colorChanged(int value) {
- hostdb.setGlobalColor(mCurrentColor, value);
+ mHostDb.setGlobalColor(mCurrentColor, value);
mColorList.set(mCurrentColor, value);
mColorGrid.invalidateViews();
}
@@ -294,7 +325,7 @@ public class ColorsActivity extends Activity implements OnItemClickListener, OnC
}
if (needUpdate)
- hostdb.setDefaultColorsForScheme(mColorScheme, mDefaultColors[0], mDefaultColors[1]);
+ mHostDb.setDefaultColorsForScheme(mColorScheme, mDefaultColors[0], mDefaultColors[1]);
}
@Override
@@ -309,8 +340,8 @@ public class ColorsActivity extends Activity implements OnItemClickListener, OnC
public boolean onMenuItemClick(MenuItem arg0) {
// Reset each individual color to defaults.
for (int i = 0; i < Colors.defaults.length; i++) {
- if (mColorList.get(i) != Colors.defaults[i]) {
- hostdb.setGlobalColor(i, Colors.defaults[i]);
+ if (!mColorList.get(i).equals(Colors.defaults[i])) {
+ mHostDb.setGlobalColor(i, Colors.defaults[i]);
mColorList.set(i, Colors.defaults[i]);
}
}
@@ -319,7 +350,7 @@ public class ColorsActivity extends Activity implements OnItemClickListener, OnC
// Reset the default FG/BG colors as well.
mFgSpinner.setSelection(HostDatabase.DEFAULT_FG_COLOR);
mBgSpinner.setSelection(HostDatabase.DEFAULT_BG_COLOR);
- hostdb.setDefaultColorsForScheme(HostDatabase.DEFAULT_COLOR_SCHEME,
+ mHostDb.setDefaultColorsForScheme(HostDatabase.DEFAULT_COLOR_SCHEME,
HostDatabase.DEFAULT_FG_COLOR, HostDatabase.DEFAULT_BG_COLOR);
return true;
diff --git a/src/org/connectbot/ConsoleActivity.java b/src/org/connectbot/ConsoleActivity.java
index f0d930f..aeb0e67 100644
--- a/src/org/connectbot/ConsoleActivity.java
+++ b/src/org/connectbot/ConsoleActivity.java
@@ -55,6 +55,7 @@ import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
+import android.view.Window;
import android.view.WindowManager;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.View.OnClickListener;
@@ -75,8 +76,6 @@ import android.widget.Toast;
import android.widget.ViewFlipper;
import android.widget.AdapterView.OnItemClickListener;
-import com.nullwire.trace.ExceptionHandler;
-
import de.mud.terminal.vt320;
public class ConsoleActivity extends Activity {
@@ -98,6 +97,10 @@ public class ConsoleActivity extends Activity {
private SharedPreferences prefs = null;
+ // determines whether or not menuitem accelerators are bound
+ // otherwise they collide with an external keyboard's CTRL-char
+ private boolean hardKeyboard = false;
+
protected Uri requested;
protected ClipboardManager clipboard;
@@ -109,6 +112,9 @@ public class ConsoleActivity extends Activity {
private TextView booleanPrompt;
private Button booleanYes, booleanNo;
+ private RelativeLayout keyboardGroup;
+ private Runnable keyboardGroupHider;
+
private TextView empty;
private Animation slide_left_in, slide_left_out, slide_right_in, slide_right_out, fade_stay_hidden, fade_out_delayed;
@@ -129,6 +135,10 @@ public class ConsoleActivity extends Activity {
private ImageView mKeyboardButton;
+ private ActionBarWrapper actionBar;
+ private boolean inActionBarMenu = false;
+ private boolean titleBarHide;
+
private ServiceConnection connection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
bound = ((TerminalManager.TerminalBinder) service).getService();
@@ -255,17 +265,63 @@ public class ConsoleActivity extends Activity {
booleanPromptGroup.setVisibility(View.GONE);
}
+ private void showEmulatedKeys() {
+ keyboardGroup.startAnimation(keyboard_fade_in);
+ keyboardGroup.setVisibility(View.VISIBLE);
+ actionBar.show();
+
+ if (keyboardGroupHider != null)
+ handler.removeCallbacks(keyboardGroupHider);
+ keyboardGroupHider = new Runnable() {
+ public void run() {
+ if (keyboardGroup.getVisibility() == View.GONE || inActionBarMenu)
+ return;
+
+ keyboardGroup.startAnimation(keyboard_fade_out);
+ keyboardGroup.setVisibility(View.GONE);
+ if (titleBarHide) {
+ actionBar.hide();
+ }
+ keyboardGroupHider = null;
+ }
+ };
+ handler.postDelayed(keyboardGroupHider, KEYBOARD_DISPLAY_TIME);
+ }
+
+ private void hideEmulatedKeys() {
+ if (keyboardGroupHider != null)
+ handler.removeCallbacks(keyboardGroupHider);
+ keyboardGroup.setVisibility(View.GONE);
+ if (titleBarHide) {
+ actionBar.hide();
+ }
+ }
+
+ // more like configureLaxMode -- enable network IO on UI thread
+ private void configureStrictMode() {
+ try {
+ Class.forName("android.os.StrictMode");
+ StrictModeSetup.run();
+ } catch (ClassNotFoundException e) {
+ }
+ }
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
-
- this.setContentView(R.layout.act_console);
-
- ExceptionHandler.register(this);
+ configureStrictMode();
+ hardKeyboard = getResources().getConfiguration().keyboard ==
+ Configuration.KEYBOARD_QWERTY;
clipboard = (ClipboardManager)getSystemService(CLIPBOARD_SERVICE);
prefs = PreferenceManager.getDefaultSharedPreferences(this);
+ titleBarHide = prefs.getBoolean(PreferenceConstants.TITLEBARHIDE, false);
+ if (titleBarHide) {
+ getWindow().requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
+ }
+
+ this.setContentView(R.layout.act_console);
+
// hide status bar if requested by user
if (prefs.getBoolean(PreferenceConstants.FULLSCREEN, false)) {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
@@ -344,7 +400,7 @@ public class ConsoleActivity extends Activity {
inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
- final RelativeLayout keyboardGroup = (RelativeLayout) findViewById(R.id.keyboard_group);
+ keyboardGroup = (RelativeLayout) findViewById(R.id.keyboard_group);
mKeyboardButton = (ImageView) findViewById(R.id.button_keyboard);
mKeyboardButton.setOnClickListener(new OnClickListener() {
@@ -354,7 +410,7 @@ public class ConsoleActivity extends Activity {
return;
inputManager.showSoftInput(flip, InputMethodManager.SHOW_FORCED);
- keyboardGroup.setVisibility(View.GONE);
+ hideEmulatedKeys();
}
});
@@ -366,9 +422,8 @@ public class ConsoleActivity extends Activity {
TerminalView terminal = (TerminalView)flip;
TerminalKeyListener handler = terminal.bridge.getKeyHandler();
- handler.metaPress(TerminalKeyListener.META_CTRL_ON);
-
- keyboardGroup.setVisibility(View.GONE);
+ handler.metaPress(TerminalKeyListener.OUR_CTRL_ON, true);
+ hideEmulatedKeys();
}
});
@@ -381,8 +436,21 @@ public class ConsoleActivity extends Activity {
TerminalKeyListener handler = terminal.bridge.getKeyHandler();
handler.sendEscape();
+ hideEmulatedKeys();
+ }
+ });
- keyboardGroup.setVisibility(View.GONE);
+ actionBar = ActionBarWrapper.getActionBar(this);
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ if (titleBarHide) {
+ actionBar.hide();
+ }
+ actionBar.addOnMenuVisibilityListener(new ActionBarWrapper.OnMenuVisibilityListener() {
+ public void onMenuVisibilityChanged(boolean isVisible) {
+ inActionBarMenu = isVisible;
+ if (isVisible == false) {
+ hideEmulatedKeys();
+ }
}
});
@@ -550,18 +618,7 @@ public class ConsoleActivity extends Activity {
&& event.getEventTime() - event.getDownTime() < CLICK_TIME
&& Math.abs(event.getX() - lastX) < MAX_CLICK_DISTANCE
&& Math.abs(event.getY() - lastY) < MAX_CLICK_DISTANCE) {
- keyboardGroup.startAnimation(keyboard_fade_in);
- keyboardGroup.setVisibility(View.VISIBLE);
-
- handler.postDelayed(new Runnable() {
- public void run() {
- if (keyboardGroup.getVisibility() == View.GONE)
- return;
-
- keyboardGroup.startAnimation(keyboard_fade_out);
- keyboardGroup.setVisibility(View.GONE);
- }
- }, KEYBOARD_DISPLAY_TIME);
+ showEmulatedKeys();
}
// pass any touch events back to detector
@@ -620,7 +677,8 @@ public class ConsoleActivity extends Activity {
menu.setQwertyMode(true);
disconnect = menu.add(R.string.list_host_disconnect);
- disconnect.setAlphabeticShortcut('w');
+ if (hardKeyboard)
+ disconnect.setAlphabeticShortcut('w');
if (!sessionOpen && disconnected)
disconnect.setTitle(R.string.console_menu_close);
disconnect.setEnabled(activeTerminal);
@@ -637,7 +695,8 @@ public class ConsoleActivity extends Activity {
});
copy = menu.add(R.string.console_menu_copy);
- copy.setAlphabeticShortcut('c');
+ if (hardKeyboard)
+ copy.setAlphabeticShortcut('c');
copy.setIcon(android.R.drawable.ic_menu_set_as);
copy.setEnabled(activeTerminal);
copy.setOnMenuItemClickListener(new OnMenuItemClickListener() {
@@ -661,7 +720,8 @@ public class ConsoleActivity extends Activity {
});
paste = menu.add(R.string.console_menu_paste);
- paste.setAlphabeticShortcut('v');
+ if (hardKeyboard)
+ paste.setAlphabeticShortcut('v');
paste.setIcon(android.R.drawable.ic_menu_edit);
paste.setEnabled(clipboard.hasText() && sessionOpen);
paste.setOnMenuItemClickListener(new OnMenuItemClickListener() {
@@ -679,7 +739,8 @@ public class ConsoleActivity extends Activity {
});
portForward = menu.add(R.string.console_menu_portforwards);
- portForward.setAlphabeticShortcut('f');
+ if (hardKeyboard)
+ portForward.setAlphabeticShortcut('f');
portForward.setIcon(android.R.drawable.ic_menu_manage);
portForward.setEnabled(sessionOpen && canForwardPorts);
portForward.setOnMenuItemClickListener(new OnMenuItemClickListener() {
@@ -695,7 +756,8 @@ public class ConsoleActivity extends Activity {
});
urlscan = menu.add(R.string.console_menu_urlscan);
- urlscan.setAlphabeticShortcut('u');
+ if (hardKeyboard)
+ urlscan.setAlphabeticShortcut('u');
urlscan.setIcon(android.R.drawable.ic_menu_search);
urlscan.setEnabled(activeTerminal);
urlscan.setOnMenuItemClickListener(new OnMenuItemClickListener() {
@@ -720,7 +782,8 @@ public class ConsoleActivity extends Activity {
});
resize = menu.add(R.string.console_menu_resize);
- resize.setAlphabeticShortcut('s');
+ if (hardKeyboard)
+ resize.setAlphabeticShortcut('s');
resize.setIcon(android.R.drawable.ic_menu_crop);
resize.setEnabled(sessionOpen);
resize.setOnMenuItemClickListener(new OnMenuItemClickListener() {
@@ -791,6 +854,19 @@ public class ConsoleActivity extends Activity {
}
@Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ Intent intent = new Intent(this, HostListActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intent);
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @Override
public void onOptionsMenuClosed(Menu menu) {
super.onOptionsMenuClosed(menu);
@@ -868,6 +944,8 @@ public class ConsoleActivity extends Activity {
requestedBridge = bound.openConnection(requested);
} catch(Exception e) {
Log.e(TAG, "Problem while trying to create new requested bridge from URI", e);
+ // TODO: We should display an error dialog here.
+ return;
}
requestedIndex = addNewTerminalView(requestedBridge);
diff --git a/src/org/connectbot/GeneratePubkeyActivity.java b/src/org/connectbot/GeneratePubkeyActivity.java
index 089e485..1b8995f 100644
--- a/src/org/connectbot/GeneratePubkeyActivity.java
+++ b/src/org/connectbot/GeneratePubkeyActivity.java
@@ -22,6 +22,7 @@ import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
+import java.security.Security;
import org.connectbot.bean.PubkeyBean;
import org.connectbot.util.EntropyDialog;
@@ -33,10 +34,7 @@ import org.connectbot.util.PubkeyUtils;
import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
-import android.content.Context;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
@@ -47,16 +45,28 @@ import android.view.View.OnFocusChangeListener;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
+import android.widget.RadioButton;
import android.widget.RadioGroup;
-import android.widget.SeekBar;
import android.widget.RadioGroup.OnCheckedChangeListener;
+import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
+import com.trilead.ssh2.signature.ECDSASHA2Verify;
+
public class GeneratePubkeyActivity extends Activity implements OnEntropyGatheredListener {
- public final static String TAG = "ConnectBot.GeneratePubkeyActivity";
+ /**
+ *
+ */
+ private static final int RSA_MINIMUM_BITS = 768;
+
+ public final static String TAG = "ConnectBot.GeneratePubkeyActivity";
final static int DEFAULT_BITS = 1024;
+ final static int[] ECDSA_SIZES = ECDSASHA2Verify.getCurveSizes();
+
+ final static int ECDSA_DEFAULT_BITS = ECDSA_SIZES[0];
+
private LayoutInflater inflater = null;
private EditText nickname;
@@ -105,11 +115,16 @@ public class GeneratePubkeyActivity extends Activity implements OnEntropyGathere
password1.addTextChangedListener(textChecker);
password2.addTextChangedListener(textChecker);
+ // TODO add BC to provide EC for devices that don't have it.
+ if (Security.getProviders("KeyPairGenerator.EC") == null) {
+ ((RadioButton) findViewById(R.id.ec)).setEnabled(false);
+ }
+
keyTypeGroup.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(RadioGroup group, int checkedId) {
if (checkedId == R.id.rsa) {
- minBits = 768;
+ minBits = RSA_MINIMUM_BITS;
bitsSlider.setEnabled(true);
bitsSlider.setProgress(DEFAULT_BITS - minBits);
@@ -128,6 +143,16 @@ public class GeneratePubkeyActivity extends Activity implements OnEntropyGathere
bitsText.setEnabled(false);
keyType = PubkeyDatabase.KEY_TYPE_DSA;
+ } else if (checkedId == R.id.ec) {
+ minBits = ECDSA_DEFAULT_BITS;
+
+ bitsSlider.setEnabled(true);
+ bitsSlider.setProgress(ECDSA_DEFAULT_BITS - minBits);
+
+ bitsText.setText(String.valueOf(ECDSA_DEFAULT_BITS));
+ bitsText.setEnabled(true);
+
+ keyType = PubkeyDatabase.KEY_TYPE_EC;
}
}
});
@@ -136,16 +161,16 @@ public class GeneratePubkeyActivity extends Activity implements OnEntropyGathere
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromTouch) {
- // Stay evenly divisible by 8 because it looks nicer to have
- // 2048 than 2043 bits.
-
- int leftover = progress % 8;
- int ourProgress = progress;
-
- if (leftover > 0)
- ourProgress += 8 - leftover;
+ if (PubkeyDatabase.KEY_TYPE_EC.equals(keyType)) {
+ bits = getClosestFieldSize(progress + minBits);
+ seekBar.setProgress(bits - minBits);
+ } else {
+ // Stay evenly divisible by 8 because it looks nicer to have
+ // 2048 than 2043 bits.
+ final int ourProgress = progress - (progress % 8);
+ bits = minBits + ourProgress;
+ }
- bits = minBits + ourProgress;
bitsText.setText(String.valueOf(bits));
}
@@ -161,14 +186,18 @@ public class GeneratePubkeyActivity extends Activity implements OnEntropyGathere
bitsText.setOnFocusChangeListener(new OnFocusChangeListener() {
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
+ final boolean isEc = PubkeyDatabase.KEY_TYPE_EC.equals(keyType);
try {
bits = Integer.parseInt(bitsText.getText().toString());
if (bits < minBits) {
bits = minBits;
bitsText.setText(String.valueOf(bits));
}
+ if (isEc) {
+ bits = getClosestFieldSize(bits);
+ }
} catch (NumberFormatException nfe) {
- bits = DEFAULT_BITS;
+ bits = isEc ? ECDSA_DEFAULT_BITS : DEFAULT_BITS;
bitsText.setText(String.valueOf(bits));
}
@@ -237,21 +266,15 @@ public class GeneratePubkeyActivity extends Activity implements OnEntropyGathere
keyGenThread.start();
}
- private Handler handler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- progress.dismiss();
- GeneratePubkeyActivity.this.finish();
- }
- };
-
final private Runnable mKeyGen = new Runnable() {
public void run() {
try {
boolean encrypted = false;
- SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
+ SecureRandom random = new SecureRandom();
+ // Work around JVM bug
+ random.nextInt();
random.setSeed(entropy);
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(keyType);
@@ -273,7 +296,7 @@ public class GeneratePubkeyActivity extends Activity implements OnEntropyGathere
pubkey.setNickname(nickname.getText().toString());
pubkey.setType(keyType);
pubkey.setPrivateKey(PubkeyUtils.getEncodedPrivate(priv, secret));
- pubkey.setPublicKey(PubkeyUtils.getEncodedPublic(pub));
+ pubkey.setPublicKey(pub.getEncoded());
pubkey.setEncrypted(encrypted);
pubkey.setStartup(unlockAtStartup.isChecked());
pubkey.setConfirmUse(confirmUse.isChecked());
@@ -287,7 +310,12 @@ public class GeneratePubkeyActivity extends Activity implements OnEntropyGathere
e.printStackTrace();
}
- handler.sendEmptyMessage(0);
+ GeneratePubkeyActivity.this.runOnUiThread(new Runnable() {
+ public void run() {
+ progress.dismiss();
+ GeneratePubkeyActivity.this.finish();
+ }
+ });
}
};
@@ -315,4 +343,18 @@ public class GeneratePubkeyActivity extends Activity implements OnEntropyGathere
return numSetBits;
}
+
+ private int getClosestFieldSize(int bits) {
+ int outBits = ECDSA_DEFAULT_BITS;
+ int distance = Math.abs(bits - ECDSA_DEFAULT_BITS);
+
+ for (int i = 1; i < ECDSA_SIZES.length; i++) {
+ int thisDistance = Math.abs(bits - ECDSA_SIZES[i]);
+ if (thisDistance < distance) {
+ distance = thisDistance;
+ outBits = ECDSA_SIZES[i];
+ }
+ }
+ return outBits;
+ }
}
diff --git a/src/org/connectbot/HostEditorActivity.java b/src/org/connectbot/HostEditorActivity.java
index 95d04cc..4e8427f 100644
--- a/src/org/connectbot/HostEditorActivity.java
+++ b/src/org/connectbot/HostEditorActivity.java
@@ -23,6 +23,7 @@ import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.Map.Entry;
import org.connectbot.bean.HostBean;
@@ -130,6 +131,11 @@ public class HostEditorActivity extends PreferenceActivity implements OnSharedPr
return true;
}
+ // Gingerbread compatibility
+ public void apply() {
+ commit();
+ }
+
public android.content.SharedPreferences.Editor putBoolean(String key, boolean value) {
return this.putString(key, Boolean.toString(value));
}
@@ -158,6 +164,9 @@ public class HostEditorActivity extends PreferenceActivity implements OnSharedPr
return this;
}
+ public android.content.SharedPreferences.Editor putStringSet(String key, Set<String> value) {
+ throw new UnsupportedOperationException("HostEditor Prefs do not support Set<String>");
+ }
}
@@ -193,6 +202,10 @@ public class HostEditorActivity extends PreferenceActivity implements OnSharedPr
return values.get(key);
}
+ public Set<String> getStringSet(String key, Set<String> defValue) {
+ throw new ClassCastException("HostEditor Prefs do not support Set<String>");
+ }
+
protected List<OnSharedPreferenceChangeListener> listeners = new LinkedList<OnSharedPreferenceChangeListener>();
public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
diff --git a/src/org/connectbot/HostListActivity.java b/src/org/connectbot/HostListActivity.java
index 3b02530..265dd08 100644
--- a/src/org/connectbot/HostListActivity.java
+++ b/src/org/connectbot/HostListActivity.java
@@ -25,7 +25,6 @@ import org.connectbot.service.TerminalManager;
import org.connectbot.transport.TransportFactory;
import org.connectbot.util.HostDatabase;
import org.connectbot.util.PreferenceConstants;
-import org.connectbot.util.UpdateHelper;
import android.app.Activity;
import android.app.AlertDialog;
@@ -40,11 +39,13 @@ import android.content.Intent.ShortcutIconResource;
import android.content.SharedPreferences.Editor;
import android.content.res.ColorStateList;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.preference.PreferenceManager;
+import android.text.format.DateUtils;
import android.util.Log;
import android.view.ContextMenu;
import android.view.KeyEvent;
@@ -63,8 +64,6 @@ import android.widget.Spinner;
import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener;
-import com.nullwire.trace.ExceptionHandler;
-
public class HostListActivity extends ListActivity {
public final static int REQUEST_EDIT = 1;
@@ -135,8 +134,6 @@ public class HostListActivity extends ListActivity {
@Override
public void onResume() {
super.onResume();
-
- ExceptionHandler.checkForTraces(this);
}
@Override
@@ -165,19 +162,37 @@ public class HostListActivity extends ListActivity {
getResources().getText(R.string.app_name),
getResources().getText(R.string.title_hosts_list)));
- ExceptionHandler.register(this);
-
- // check for eula agreement
this.prefs = PreferenceManager.getDefaultSharedPreferences(this);
+ // detect HTC Dream and apply special preferences
+ if (Build.MANUFACTURER.equals("HTC") && Build.DEVICE.equals("dream")) {
+ SharedPreferences.Editor editor = prefs.edit();
+ boolean doCommit = false;
+ if (!prefs.contains(PreferenceConstants.SHIFT_FKEYS) &&
+ !prefs.contains(PreferenceConstants.CTRL_FKEYS)) {
+ editor.putBoolean(PreferenceConstants.SHIFT_FKEYS, true);
+ editor.putBoolean(PreferenceConstants.CTRL_FKEYS, true);
+ doCommit = true;
+ }
+ if (!prefs.contains(PreferenceConstants.STICKY_MODIFIERS)) {
+ editor.putString(PreferenceConstants.STICKY_MODIFIERS, PreferenceConstants.YES);
+ doCommit = true;
+ }
+ if (!prefs.contains(PreferenceConstants.KEYMODE)) {
+ editor.putString(PreferenceConstants.KEYMODE, PreferenceConstants.KEYMODE_RIGHT);
+ doCommit = true;
+ }
+ if (doCommit) {
+ editor.commit();
+ }
+ }
+
+ // check for eula agreement
boolean agreed = prefs.getBoolean(PreferenceConstants.EULA, false);
if(!agreed) {
this.startActivityForResult(new Intent(this, WizardActivity.class), REQUEST_EULA);
}
- // start thread to check for new version
- new UpdateHelper(this);
-
this.makingShortcut = Intent.ACTION_CREATE_SHORTCUT.equals(getIntent().getAction())
|| Intent.ACTION_PICK.equals(getIntent().getAction());
@@ -301,7 +316,7 @@ public class HostListActivity extends ListActivity {
keys.setIcon(android.R.drawable.ic_lock_lock);
keys.setIntent(new Intent(HostListActivity.this, PubkeyListActivity.class));
- MenuItem colors = menu.add("Colors");
+ MenuItem colors = menu.add(R.string.title_colors);
colors.setIcon(android.R.drawable.ic_menu_slideshow);
colors.setIntent(new Intent(HostListActivity.this, ColorsActivity.class));
@@ -331,8 +346,8 @@ public class HostListActivity extends ListActivity {
// edit, disconnect, delete
MenuItem connect = menu.add(R.string.list_host_disconnect);
- final TerminalBridge bridge = bound.getConnectedBridge(host);
- connect.setEnabled((bridge != null));
+ final TerminalBridge bridge = (bound == null) ? null : bound.getConnectedBridge(host);
+ connect.setEnabled(bridge != null);
connect.setOnMenuItemClickListener(new OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
bridge.dispatchDisconnect(true);
@@ -544,20 +559,9 @@ public class HostListActivity extends ListActivity {
holder.caption.setTextAppearance(context, android.R.attr.textAppearanceSmall);
}
- long now = System.currentTimeMillis() / 1000;
-
- String nice = context.getString(R.string.bind_never);
+ CharSequence nice = context.getString(R.string.bind_never);
if (host.getLastConnect() > 0) {
- int minutes = (int)((now - host.getLastConnect()) / 60);
- if (minutes >= 60) {
- int hours = (minutes / 60);
- if (hours >= 24) {
- int days = (hours / 24);
- nice = context.getString(R.string.bind_days, days);
- } else
- nice = context.getString(R.string.bind_hours, hours);
- } else
- nice = context.getString(R.string.bind_minutes, minutes);
+ nice = DateUtils.getRelativeTimeSpanString(host.getLastConnect() * 1000);
}
holder.caption.setText(nice);
diff --git a/src/org/connectbot/PubkeyListActivity.java b/src/org/connectbot/PubkeyListActivity.java
index 3579980..ec9914f 100644
--- a/src/org/connectbot/PubkeyListActivity.java
+++ b/src/org/connectbot/PubkeyListActivity.java
@@ -43,32 +43,30 @@ import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.ServiceConnection;
-import android.content.DialogInterface.OnClickListener;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
-import android.os.Handler;
import android.os.IBinder;
-import android.os.Message;
import android.text.ClipboardManager;
import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
+import android.view.MenuItem.OnMenuItemClickListener;
import android.view.View;
import android.view.ViewGroup;
-import android.view.MenuItem.OnMenuItemClickListener;
import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TableRow;
import android.widget.TextView;
import android.widget.Toast;
-import android.widget.AdapterView.OnItemClickListener;
import com.trilead.ssh2.crypto.Base64;
import com.trilead.ssh2.crypto.PEMDecoder;
@@ -157,12 +155,12 @@ public class PubkeyListActivity extends ListActivity implements EventListener {
getListView().setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> adapter, View view, int position, long id) {
PubkeyBean pubkey = (PubkeyBean) getListView().getItemAtPosition(position);
- boolean loaded = bound.isKeyLoaded(pubkey.getNickname());
+ boolean loaded = bound != null && bound.isKeyLoaded(pubkey.getNickname());
// handle toggling key in-memory on/off
if(loaded) {
bound.removeKey(pubkey.getNickname());
- updateHandler.sendEmptyMessage(-1);
+ updateList();
} else {
handleAddKey(pubkey);
}
@@ -257,45 +255,43 @@ public class PubkeyListActivity extends ListActivity implements EventListener {
}
}
- protected void handleAddKey(PubkeyBean pubkey, String password) {
- Object trileadKey = null;
- if(PubkeyDatabase.KEY_TYPE_IMPORTED.equals(pubkey.getType())) {
+ protected void handleAddKey(PubkeyBean keybean, String password) {
+ KeyPair pair = null;
+ if(PubkeyDatabase.KEY_TYPE_IMPORTED.equals(keybean.getType())) {
// load specific key using pem format
try {
- trileadKey = PEMDecoder.decode(new String(pubkey.getPrivateKey()).toCharArray(), password);
+ pair = PEMDecoder.decode(new String(keybean.getPrivateKey()).toCharArray(), password);
} catch(Exception e) {
- String message = getResources().getString(R.string.pubkey_failed_add, pubkey.getNickname());
+ String message = getResources().getString(R.string.pubkey_failed_add, keybean.getNickname());
Log.e(TAG, message, e);
- Toast.makeText(PubkeyListActivity.this, message, Toast.LENGTH_LONG);
+ Toast.makeText(PubkeyListActivity.this, message, Toast.LENGTH_LONG).show();
}
-
} else {
// load using internal generated format
- PrivateKey privKey = null;
- PublicKey pubKey = null;
try {
- privKey = PubkeyUtils.decodePrivate(pubkey.getPrivateKey(), pubkey.getType(), password);
- pubKey = PubkeyUtils.decodePublic(pubkey.getPublicKey(), pubkey.getType());
+ PrivateKey privKey = PubkeyUtils.decodePrivate(keybean.getPrivateKey(), keybean.getType(), password);
+ PublicKey pubKey = PubkeyUtils.decodePublic(keybean.getPublicKey(), keybean.getType());
+ Log.d(TAG, "Unlocked key " + PubkeyUtils.formatKey(pubKey));
+
+ pair = new KeyPair(pubKey, privKey);
} catch (Exception e) {
- String message = getResources().getString(R.string.pubkey_failed_add, pubkey.getNickname());
+ String message = getResources().getString(R.string.pubkey_failed_add, keybean.getNickname());
Log.e(TAG, message, e);
- Toast.makeText(PubkeyListActivity.this, message, Toast.LENGTH_LONG);
+ Toast.makeText(PubkeyListActivity.this, message, Toast.LENGTH_LONG).show();
return;
}
-
- // convert key to trilead format
- trileadKey = PubkeyUtils.convertToTrilead(privKey, pubKey);
- Log.d(TAG, "Unlocked key " + PubkeyUtils.formatKey(pubKey));
}
- if(trileadKey == null) return;
+ if (pair == null) {
+ return;
+ }
- Log.d(TAG, String.format("Unlocked key '%s'", pubkey.getNickname()));
+ Log.d(TAG, String.format("Unlocked key '%s'", keybean.getNickname()));
// save this key in memory
- bound.addKey(pubkey, trileadKey, true);
+ bound.addKey(keybean, pair, true);
- updateHandler.sendEmptyMessage(-1);
+ updateList();
}
@Override
@@ -311,14 +307,14 @@ public class PubkeyListActivity extends ListActivity implements EventListener {
// cant change password or clipboard imported keys
final boolean imported = PubkeyDatabase.KEY_TYPE_IMPORTED.equals(pubkey.getType());
- final boolean loaded = bound.isKeyLoaded(pubkey.getNickname());
+ final boolean loaded = bound != null && bound.isKeyLoaded(pubkey.getNickname());
MenuItem load = menu.add(loaded ? R.string.pubkey_memory_unload : R.string.pubkey_memory_load);
load.setOnMenuItemClickListener(new OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
if(loaded) {
bound.removeKey(pubkey.getNickname());
- updateHandler.sendEmptyMessage(-1);
+ updateList();
} else {
handleAddKey(pubkey);
//bound.addKey(nickname, trileadKey);
@@ -336,7 +332,7 @@ public class PubkeyListActivity extends ListActivity implements EventListener {
// toggle onstart status
pubkey.setStartup(!pubkey.isStartup());
pubkeydb.savePubkey(pubkey);
- updateHandler.sendEmptyMessage(-1);
+ updateList();
return true;
}
});
@@ -410,7 +406,7 @@ public class PubkeyListActivity extends ListActivity implements EventListener {
.create().show();
else {
pubkeydb.savePubkey(pubkey);
- updateHandler.sendEmptyMessage(-1);
+ updateList();
}
} catch (Exception e) {
Log.e(TAG, "Could not change private key password", e);
@@ -435,7 +431,7 @@ public class PubkeyListActivity extends ListActivity implements EventListener {
// toggle confirm use
pubkey.setConfirmUse(!pubkey.isConfirmUse());
pubkeydb.savePubkey(pubkey);
- updateHandler.sendEmptyMessage(-1);
+ updateList();
return true;
}
});
@@ -455,7 +451,7 @@ public class PubkeyListActivity extends ListActivity implements EventListener {
// delete from backend database and update gui
pubkeydb.deletePubkey(pubkey);
- updateHandler.sendEmptyMessage(-1);
+ updateList();
}
})
.setNegativeButton(R.string.delete_neg, null).create().show();
@@ -466,14 +462,6 @@ public class PubkeyListActivity extends ListActivity implements EventListener {
}
-
- protected Handler updateHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- updateList();
- }
- };
-
protected void updateList() {
if (pubkeydb == null) return;
@@ -561,7 +549,7 @@ public class PubkeyListActivity extends ListActivity implements EventListener {
pubkeydb = new PubkeyDatabase(this);
pubkeydb.savePubkey(pubkey);
- updateHandler.sendEmptyMessage(-1);
+ updateList();
} catch(Exception e) {
Log.e(TAG, "Problem parsing imported private key", e);
Toast.makeText(PubkeyListActivity.this, R.string.pubkey_import_parse_problem, Toast.LENGTH_LONG).show();
@@ -661,8 +649,7 @@ public class PubkeyListActivity extends ListActivity implements EventListener {
}
} else {
try {
- PublicKey pub = PubkeyUtils.decodePublic(pubkey.getPublicKey(), pubkey.getType());
- holder.caption.setText(PubkeyUtils.describeKey(pub, pubkey.isEncrypted()));
+ holder.caption.setText(pubkey.getDescription());
} catch (Exception e) {
Log.e(TAG, "Error decoding public key at " + pubkey.getId(), e);
holder.caption.setText(R.string.pubkey_unknown_format);
diff --git a/src/org/connectbot/StrictModeSetup.java b/src/org/connectbot/StrictModeSetup.java
new file mode 100644
index 0000000..3a2000e
--- /dev/null
+++ b/src/org/connectbot/StrictModeSetup.java
@@ -0,0 +1,23 @@
+/*
+ * ConnectBot: simple, powerful, open-source SSH client for Android
+ * Copyright 2007 Kenny Root, Jeffrey Sharkey
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.connectbot;
+import android.os.StrictMode;
+public class StrictModeSetup {
+ public static void run() {
+ StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.LAX);
+ }
+}
diff --git a/src/org/connectbot/TerminalView.java b/src/org/connectbot/TerminalView.java
index bb58701..02683c2 100644
--- a/src/org/connectbot/TerminalView.java
+++ b/src/org/connectbot/TerminalView.java
@@ -17,22 +17,34 @@
package org.connectbot;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
import org.connectbot.bean.SelectionArea;
import org.connectbot.service.FontSizeChangedListener;
import org.connectbot.service.TerminalBridge;
import org.connectbot.service.TerminalKeyListener;
import android.app.Activity;
+import android.content.ContentResolver;
import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.database.Cursor;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelXorXfermode;
import android.graphics.RectF;
+import android.net.Uri;
+import android.os.AsyncTask;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
@@ -64,6 +76,22 @@ public class TerminalView extends View implements FontSizeChangedListener {
private String lastNotification = null;
private volatile boolean notifications = true;
+ // Related to Accessibility Features
+ private boolean mAccessibilityInitialized = false;
+ private boolean mAccessibilityActive = true;
+ private Object[] mAccessibilityLock = new Object[0];
+ private StringBuffer mAccessibilityBuffer;
+ private Pattern mControlCodes = null;
+ private Matcher mCodeMatcher = null;
+ private AccessibilityEventSender mEventSender = null;
+
+ private static final String BACKSPACE_CODE = "\\x08\\x1b\\[K";
+ private static final String CONTROL_CODE_PATTERN = "\\x1b\\[K[^m]+[m|:]";
+
+ private static final int ACCESSIBILITY_EVENT_THRESHOLD = 1000;
+ private static final String SCREENREADER_INTENT_ACTION = "android.accessibilityservice.AccessibilityService";
+ private static final String SCREENREADER_INTENT_CATEGORY = "android.accessibilityservice.category.FEEDBACK_SPOKEN";
+
public TerminalView(Context context, TerminalBridge bridge) {
super(context);
@@ -113,6 +141,11 @@ public class TerminalView extends View implements FontSizeChangedListener {
// connect our view up to the bridge
setOnKeyListener(bridge.getKeyHandler());
+
+ mAccessibilityBuffer = new StringBuffer();
+
+ // Enable accessibility features if a screen reader is active.
+ new AccessibilityStateTester().execute((Void) null);
}
public void destroy() {
@@ -179,24 +212,29 @@ public class TerminalView extends View implements FontSizeChangedListener {
bridge.charHeight);
canvas.drawPaint(cursorPaint);
+ final int deadKey = bridge.getKeyHandler().getDeadKey();
+ if (deadKey != 0) {
+ canvas.drawText(new char[] { (char)deadKey }, 0, 1, 0, 0, cursorStrokePaint);
+ }
+
// Make sure we scale our decorations to the correct size.
canvas.concat(scaleMatrix);
int metaState = bridge.getKeyHandler().getMetaState();
- if ((metaState & TerminalKeyListener.META_SHIFT_ON) != 0)
+ if ((metaState & TerminalKeyListener.OUR_SHIFT_ON) != 0)
canvas.drawPath(shiftCursor, cursorStrokePaint);
- else if ((metaState & TerminalKeyListener.META_SHIFT_LOCK) != 0)
+ else if ((metaState & TerminalKeyListener.OUR_SHIFT_LOCK) != 0)
canvas.drawPath(shiftCursor, cursorPaint);
- if ((metaState & TerminalKeyListener.META_ALT_ON) != 0)
+ if ((metaState & TerminalKeyListener.OUR_ALT_ON) != 0)
canvas.drawPath(altCursor, cursorStrokePaint);
- else if ((metaState & TerminalKeyListener.META_ALT_LOCK) != 0)
+ else if ((metaState & TerminalKeyListener.OUR_ALT_LOCK) != 0)
canvas.drawPath(altCursor, cursorPaint);
- if ((metaState & TerminalKeyListener.META_CTRL_ON) != 0)
+ if ((metaState & TerminalKeyListener.OUR_CTRL_ON) != 0)
canvas.drawPath(ctrlCursor, cursorStrokePaint);
- else if ((metaState & TerminalKeyListener.META_CTRL_LOCK) != 0)
+ else if ((metaState & TerminalKeyListener.OUR_CTRL_LOCK) != 0)
canvas.drawPath(ctrlCursor, cursorPaint);
// Restore previous clip region
@@ -281,4 +319,134 @@ public class TerminalView extends View implements FontSizeChangedListener {
}
};
}
+
+ public void propagateConsoleText(char[] rawText, int length) {
+ if (mAccessibilityActive) {
+ synchronized (mAccessibilityLock) {
+ mAccessibilityBuffer.append(rawText, 0, length);
+ }
+
+ if (mAccessibilityInitialized) {
+ if (mEventSender != null) {
+ removeCallbacks(mEventSender);
+ } else {
+ mEventSender = new AccessibilityEventSender();
+ }
+
+ postDelayed(mEventSender, ACCESSIBILITY_EVENT_THRESHOLD);
+ }
+ }
+ }
+
+ private class AccessibilityEventSender implements Runnable {
+ public void run() {
+ synchronized (mAccessibilityLock) {
+ if (mCodeMatcher == null) {
+ mCodeMatcher = mControlCodes.matcher(mAccessibilityBuffer);
+ } else {
+ mCodeMatcher.reset(mAccessibilityBuffer);
+ }
+
+ // Strip all control codes out.
+ mAccessibilityBuffer = new StringBuffer(mCodeMatcher.replaceAll(" "));
+
+ // Apply Backspaces using backspace character sequence
+ int i = mAccessibilityBuffer.indexOf(BACKSPACE_CODE);
+ while (i != -1) {
+ mAccessibilityBuffer = mAccessibilityBuffer.replace(i == 0 ? 0 : i - 1, i
+ + BACKSPACE_CODE.length(), "");
+ i = mAccessibilityBuffer.indexOf(BACKSPACE_CODE);
+ }
+
+ if (mAccessibilityBuffer.length() > 0) {
+ AccessibilityEvent event = AccessibilityEvent.obtain(
+ AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
+ event.setFromIndex(0);
+ event.setAddedCount(mAccessibilityBuffer.length());
+ event.getText().add(mAccessibilityBuffer);
+
+ sendAccessibilityEventUnchecked(event);
+ mAccessibilityBuffer.setLength(0);
+ }
+ }
+ }
+ }
+
+ private class AccessibilityStateTester extends AsyncTask<Void, Void, Boolean> {
+ @Override
+ protected Boolean doInBackground(Void... params) {
+ /*
+ * Presumably if the accessibility manager is not enabled, we don't
+ * need to send accessibility events.
+ */
+ final AccessibilityManager accessibility = (AccessibilityManager) context
+ .getSystemService(Context.ACCESSIBILITY_SERVICE);
+ if (!accessibility.isEnabled()) {
+ return false;
+ }
+
+ /*
+ * Restrict the set of intents to only accessibility services that
+ * have the category FEEDBACK_SPOKEN (aka, screen readers).
+ */
+ final Intent screenReaderIntent = new Intent(SCREENREADER_INTENT_ACTION);
+ screenReaderIntent.addCategory(SCREENREADER_INTENT_CATEGORY);
+
+ final ContentResolver cr = context.getContentResolver();
+
+ final List<ResolveInfo> screenReaders = context.getPackageManager().queryIntentServices(
+ screenReaderIntent, 0);
+
+ boolean foundScreenReader = false;
+
+ final int N = screenReaders.size();
+ for (int i = 0; i < N; i++) {
+ final ResolveInfo screenReader = screenReaders.get(i);
+
+ /*
+ * All screen readers are expected to implement a content
+ * provider that responds to:
+ * content://<nameofpackage>.providers.StatusProvider
+ */
+ final Cursor cursor = cr.query(
+ Uri.parse("content://" + screenReader.serviceInfo.packageName
+ + ".providers.StatusProvider"), null, null, null, null);
+ if (cursor != null && cursor.moveToFirst()) {
+ /*
+ * These content providers use a special cursor that only has
+ * one element, an integer that is 1 if the screen reader is
+ * running.
+ */
+ final int status = cursor.getInt(0);
+
+ cursor.close();
+
+ if (status == 1) {
+ foundScreenReader = true;
+ break;
+ }
+ }
+ }
+
+ if (foundScreenReader) {
+ mControlCodes = Pattern.compile(CONTROL_CODE_PATTERN);
+ }
+
+ return foundScreenReader;
+ }
+
+ @Override
+ protected void onPostExecute(Boolean result) {
+ mAccessibilityActive = result;
+
+ mAccessibilityInitialized = true;
+
+ if (result) {
+ mEventSender = new AccessibilityEventSender();
+ postDelayed(mEventSender, ACCESSIBILITY_EVENT_THRESHOLD);
+ } else {
+ mAccessibilityBuffer = null;
+ }
+ }
+ }
}
diff --git a/src/org/connectbot/bean/HostBean.java b/src/org/connectbot/bean/HostBean.java
index 2fd7bfb..183d047 100644
--- a/src/org/connectbot/bean/HostBean.java
+++ b/src/org/connectbot/bean/HostBean.java
@@ -50,6 +50,7 @@ public class HostBean extends AbstractBean {
private boolean compression = false;
private String encoding = HostDatabase.ENCODING_DEFAULT;
private boolean stayConnected = false;
+ private boolean quickDisconnect = false;
public HostBean() {
@@ -202,6 +203,14 @@ public class HostBean extends AbstractBean {
return stayConnected;
}
+ public void setQuickDisconnect(boolean quickDisconnect) {
+ this.quickDisconnect = quickDisconnect;
+ }
+
+ public boolean getQuickDisconnect() {
+ return quickDisconnect;
+ }
+
public String getDescription() {
String description = String.format("%s@%s", username, hostname);
@@ -234,6 +243,7 @@ public class HostBean extends AbstractBean {
values.put(HostDatabase.FIELD_HOST_COMPRESSION, Boolean.toString(compression));
values.put(HostDatabase.FIELD_HOST_ENCODING, encoding);
values.put(HostDatabase.FIELD_HOST_STAYCONNECTED, stayConnected);
+ values.put(HostDatabase.FIELD_HOST_QUICKDISCONNECT, quickDisconnect);
return values;
}
diff --git a/src/org/connectbot/bean/PubkeyBean.java b/src/org/connectbot/bean/PubkeyBean.java
index 0354f37..656c6af 100644
--- a/src/org/connectbot/bean/PubkeyBean.java
+++ b/src/org/connectbot/bean/PubkeyBean.java
@@ -17,7 +17,12 @@
package org.connectbot.bean;
+import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
import org.connectbot.util.PubkeyDatabase;
import org.connectbot.util.PubkeyUtils;
@@ -31,6 +36,12 @@ import android.content.ContentValues;
public class PubkeyBean extends AbstractBean {
public static final String BEAN_NAME = "pubkey";
+ private static final String KEY_TYPE_RSA = "RSA";
+
+ private static final String KEY_TYPE_DSA = "DSA";
+
+ private static final String KEY_TYPE_EC = "EC";
+
/* Database fields */
private long id;
private String nickname;
@@ -43,8 +54,9 @@ public class PubkeyBean extends AbstractBean {
private int lifetime = 0;
/* Transient values */
- private boolean unlocked = false;
- private Object unlockedPrivate = null;
+ private transient boolean unlocked = false;
+ private transient Object unlockedPrivate = null;
+ private transient String description;
@Override
public String getBeanName() {
@@ -89,11 +101,11 @@ public class PubkeyBean extends AbstractBean {
return privateKey.clone();
}
- public void setPublicKey(byte[] publicKey) {
- if (publicKey == null)
- this.publicKey = null;
+ public void setPublicKey(byte[] encoded) {
+ if (encoded == null)
+ publicKey = null;
else
- this.publicKey = publicKey.clone();
+ publicKey = encoded.clone();
}
public byte[] getPublicKey() {
@@ -151,6 +163,41 @@ public class PubkeyBean extends AbstractBean {
return unlockedPrivate;
}
+ public String getDescription() {
+ if (description == null) {
+ final StringBuilder sb = new StringBuilder();
+ try {
+ final PublicKey pubKey = PubkeyUtils.decodePublic(privateKey, type);
+ if (PubkeyDatabase.KEY_TYPE_RSA.equals(type)) {
+ int bits = ((RSAPublicKey) pubKey).getModulus().bitLength();
+ sb.append("RSA ");
+ sb.append(bits);
+ sb.append("-bit");
+ } else if (PubkeyDatabase.KEY_TYPE_DSA.equals(type)) {
+ sb.append("DSA 1024-bit");
+ } else if (PubkeyDatabase.KEY_TYPE_EC.equals(type)) {
+ int bits = ((ECPublicKey) pubKey).getParams().getCurve().getField()
+ .getFieldSize();
+ sb.append("EC ");
+ sb.append(bits);
+ sb.append("-bit");
+ } else {
+ sb.append("Unknown Key Type");
+ }
+ } catch (NoSuchAlgorithmException e) {
+ sb.append("Unknown Key Type");
+ } catch (InvalidKeySpecException e) {
+ sb.append("Unknown Key Type");
+ }
+
+ if (encrypted)
+ sb.append(" (encrypted)");
+
+ description = sb.toString();
+ }
+ return description;
+ }
+
/* (non-Javadoc)
* @see org.connectbot.bean.AbstractBean#getValues()
*/
diff --git a/src/org/connectbot/service/ConnectionNotifier.java b/src/org/connectbot/service/ConnectionNotifier.java
index a9c054d..d276761 100644
--- a/src/org/connectbot/service/ConnectionNotifier.java
+++ b/src/org/connectbot/service/ConnectionNotifier.java
@@ -17,6 +17,9 @@
package org.connectbot.service;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
import org.connectbot.ConsoleActivity;
import org.connectbot.R;
import org.connectbot.bean.HostBean;
@@ -128,20 +131,46 @@ public abstract class ConnectionNotifier {
public abstract void hideRunningNotification(Service context);
private static class PreEclair extends ConnectionNotifier {
+ private static final Class<?>[] setForegroundSignature = new Class[] {boolean.class};
+ private Method setForeground = null;
+
private static class Holder {
private static final PreEclair sInstance = new PreEclair();
}
+ public PreEclair() {
+ try {
+ setForeground = Service.class.getMethod("setForeground", setForegroundSignature);
+ } catch (Exception e) {
+ }
+ }
+
@Override
public void showRunningNotification(Service context) {
- context.setForeground(true);
- getNotificationManager(context).notify(ONLINE_NOTIFICATION, newRunningNotification(context));
+ if (setForeground != null) {
+ Object[] setForegroundArgs = new Object[1];
+ setForegroundArgs[0] = Boolean.TRUE;
+ try {
+ setForeground.invoke(context, setForegroundArgs);
+ } catch (InvocationTargetException e) {
+ } catch (IllegalAccessException e) {
+ }
+ getNotificationManager(context).notify(ONLINE_NOTIFICATION, newRunningNotification(context));
+ }
}
@Override
public void hideRunningNotification(Service context) {
- context.setForeground(false);
- getNotificationManager(context).cancel(ONLINE_NOTIFICATION);
+ if (setForeground != null) {
+ Object[] setForegroundArgs = new Object[1];
+ setForegroundArgs[0] = Boolean.FALSE;
+ try {
+ setForeground.invoke(context, setForegroundArgs);
+ } catch (InvocationTargetException e) {
+ } catch (IllegalAccessException e) {
+ }
+ getNotificationManager(context).cancel(ONLINE_NOTIFICATION);
+ }
}
}
diff --git a/src/org/connectbot/service/ConnectivityReceiver.java b/src/org/connectbot/service/ConnectivityReceiver.java
index 908298f..3248a2a 100644
--- a/src/org/connectbot/service/ConnectivityReceiver.java
+++ b/src/org/connectbot/service/ConnectivityReceiver.java
@@ -100,9 +100,9 @@ public class ConnectivityReceiver extends BroadcastReceiver {
*/
public void incRef() {
synchronized (mLock) {
- acquireWifiLockIfNecessaryLocked();
-
mNetworkRef += 1;
+
+ acquireWifiLockIfNecessaryLocked();
}
}
diff --git a/src/org/connectbot/service/FontSizeChangedListener.java b/src/org/connectbot/service/FontSizeChangedListener.java
index eb1c33d..56b9971 100644
--- a/src/org/connectbot/service/FontSizeChangedListener.java
+++ b/src/org/connectbot/service/FontSizeChangedListener.java
@@ -24,8 +24,8 @@ package org.connectbot.service;
public interface FontSizeChangedListener {
/**
- * @param size
- * new font size
+ * @param sizeDp
+ * new font size in dp
*/
- void onFontSizeChanged(float size);
+ void onFontSizeChanged(float sizeDp);
}
diff --git a/src/org/connectbot/service/KeyEventUtil.java b/src/org/connectbot/service/KeyEventUtil.java
new file mode 100644
index 0000000..1362afa
--- /dev/null
+++ b/src/org/connectbot/service/KeyEventUtil.java
@@ -0,0 +1,149 @@
+/*
+ * ConnectBot: simple, powerful, open-source SSH client for Android
+ * Copyright 2014 Torne Wuff
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.connectbot.service;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.view.KeyEvent;
+
+public class KeyEventUtil {
+ static final char CONTROL_LIMIT = ' ';
+ static final char PRINTABLE_LIMIT = '\u007e';
+ static final char[] HEX_DIGITS = new char[] {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+ };
+
+ static String printableRepresentation(String source) {
+ if (source == null)
+ return null;
+
+ final StringBuilder sb = new StringBuilder();
+ final int limit = source.length();
+ char[] hexbuf = null;
+ int pointer = 0;
+
+ sb.append('"');
+ while (pointer < limit) {
+ int ch = source.charAt(pointer++);
+ switch (ch) {
+ case '\0':
+ sb.append("\\0");
+ break;
+ case '\t':
+ sb.append("\\t");
+ break;
+ case '\n':
+ sb.append("\\n");
+ break;
+ case '\r':
+ sb.append("\\r");
+ break;
+ case '\"':
+ sb.append("\\\"");
+ break;
+ case '\\':
+ sb.append("\\\\");
+ break;
+ default:
+ if (CONTROL_LIMIT <= ch && ch <= PRINTABLE_LIMIT) {
+ sb.append((char) ch);
+ } else {
+ sb.append("\\u");
+ if (hexbuf == null)
+ hexbuf = new char[4];
+ for (int offs = 4; offs > 0; ) {
+ hexbuf[--offs] = HEX_DIGITS[ch & 0xf];
+ ch >>>= 4;
+ }
+ sb.append(hexbuf, 0, 4);
+ }
+ }
+ }
+ return sb.append('"').toString();
+ }
+
+ private static class ClassCompat {
+ private static final ClassCompat INSTANCE;
+ static {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
+ INSTANCE = new HCMR2AndNewer();
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
+ INSTANCE = new HCMR1AndNewer();
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
+ INSTANCE = new GingerbreadAndNewer();
+ } else {
+ INSTANCE = new ClassCompat();
+ }
+ }
+
+ private ClassCompat() {
+ }
+
+ public static void appendExtras(StringBuilder d, int keyCode, KeyEvent event) {
+ INSTANCE.appendForApi(d, keyCode, event);
+ }
+
+ protected void appendForApi(StringBuilder d, int keyCode, KeyEvent event) {
+ }
+
+ @TargetApi(9)
+ private static class GingerbreadAndNewer extends ClassCompat {
+ @Override
+ protected void appendForApi(StringBuilder d, int keyCode, KeyEvent event) {
+ super.appendForApi(d, keyCode, event);
+ d.append(", source=").append(event.getSource());
+
+ }
+ }
+ @TargetApi(12)
+ private static class HCMR1AndNewer extends GingerbreadAndNewer {
+ protected void appendForApi(StringBuilder d, int keyCode, KeyEvent event) {
+ super.appendForApi(d, keyCode, event);
+ d.append(", keyCodeToString=").append(KeyEvent.keyCodeToString(keyCode));
+ }
+ }
+
+ @TargetApi(13)
+ private static class HCMR2AndNewer extends HCMR1AndNewer {
+ @Override
+ protected void appendForApi(StringBuilder d, int keyCode, KeyEvent event) {
+ super.appendForApi(d, keyCode, event);
+ d.append(", modifiers=").append(Integer.toHexString(event.getModifiers()));
+ }
+ }
+ }
+
+ public static String describeKeyEvent(int keyCode, KeyEvent event) {
+ StringBuilder d = new StringBuilder();
+ d.append("keyCode=").append(keyCode);
+ d.append(", event.toString=").append(event.toString());
+ d.append(", action=").append(event.getAction());
+ d.append(", characters=").append(printableRepresentation(event.getCharacters()));
+ d.append(", deviceId=").append(event.getDeviceId());
+ d.append(", displayLabel=").append((int) event.getDisplayLabel());
+ d.append(", flags=0x").append(Integer.toHexString(event.getFlags()));
+ d.append(", printingKey=").append(event.isPrintingKey());
+ d.append(", keyCode=").append(event.getKeyCode());
+ d.append(", metaState=0x").append(Integer.toHexString(event.getMetaState()));
+ d.append(", number=").append((int) event.getNumber());
+ d.append(", scanCode=").append(event.getScanCode());
+ d.append(", unicodeChar=").append(event.getUnicodeChar());
+ ClassCompat.appendExtras(d, keyCode, event);
+ return d.toString();
+ }
+}
diff --git a/src/org/connectbot/service/Relay.java b/src/org/connectbot/service/Relay.java
index d91cce8..36672ec 100644
--- a/src/org/connectbot/service/Relay.java
+++ b/src/org/connectbot/service/Relay.java
@@ -17,8 +17,6 @@
package org.connectbot.service;
-import org.apache.harmony.niochar.charset.additional.IBM437;
-
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
@@ -27,6 +25,7 @@ import java.nio.charset.CharsetDecoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
+import org.apache.harmony.niochar.charset.additional.IBM437;
import org.connectbot.transport.AbsTransport;
import org.connectbot.util.EastAsianWidth;
@@ -45,7 +44,6 @@ public class Relay implements Runnable {
private Charset currentCharset;
private CharsetDecoder decoder;
- private boolean isLegacyEastAsian = false;
private AbsTransport transport;
@@ -135,6 +133,7 @@ public class Relay implements Runnable {
measurer.measure(charArray, 0, offset, wideAttribute, bridge.defaultPaint, charWidth);
buffer.putString(charArray, wideAttribute, 0, charBuffer.position());
+ bridge.propagateConsoleText(charArray, charBuffer.position());
charBuffer.clear();
bridge.redraw();
}
diff --git a/src/org/connectbot/service/TerminalBridge.java b/src/org/connectbot/service/TerminalBridge.java
index 26cf41b..2065a97 100644
--- a/src/org/connectbot/service/TerminalBridge.java
+++ b/src/org/connectbot/service/TerminalBridge.java
@@ -35,12 +35,12 @@ import org.connectbot.util.HostDatabase;
import android.content.Context;
import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
-import android.graphics.Typeface;
-import android.graphics.Bitmap.Config;
import android.graphics.Paint.FontMetrics;
+import android.graphics.Typeface;
import android.text.ClipboardManager;
import android.util.Log;
import de.mud.terminal.VDUBuffer;
@@ -57,11 +57,13 @@ import de.mud.terminal.vt320;
* This class also provides SSH hostkey verification prompting, and password
* prompting.
*/
+@SuppressWarnings("deprecation") // for ClipboardManager
public class TerminalBridge implements VDUDisplay {
public final static String TAG = "ConnectBot.TerminalBridge";
- public final static int DEFAULT_FONT_SIZE = 10;
+ public final static int DEFAULT_FONT_SIZE_DP = 10;
private final static int FONT_SIZE_STEP = 2;
+ private final float displayDensity;
public Integer[] color;
@@ -98,13 +100,15 @@ public class TerminalBridge implements VDUDisplay {
private boolean selectingForCopy = false;
private final SelectionArea selectionArea;
+
+ // TODO add support for the new clipboard API
private ClipboardManager clipboard;
public int charWidth = -1;
public int charHeight = -1;
private int charTop = -1;
- private float fontSize = -1;
+ private float fontSizeDp = -1;
private final List<FontSizeChangedListener> fontSizeChangedListeners;
@@ -140,6 +144,8 @@ public class TerminalBridge implements VDUDisplay {
emulation = null;
manager = null;
+ displayDensity = 1f;
+
defaultPaint = new Paint();
selectionArea = new SelectionArea();
@@ -166,6 +172,8 @@ public class TerminalBridge implements VDUDisplay {
emulation = manager.getEmulation();
scrollback = manager.getScrollback();
+ this.displayDensity = manager.getResources().getDisplayMetrics().density;
+
// create prompt helper to relay password and hostkey requests up to gui
promptHelper = new PromptHelper(this);
@@ -179,10 +187,11 @@ public class TerminalBridge implements VDUDisplay {
fontSizeChangedListeners = new LinkedList<FontSizeChangedListener>();
- int hostFontSize = host.getFontSize();
- if (hostFontSize <= 0)
- hostFontSize = DEFAULT_FONT_SIZE;
- setFontSize(hostFontSize);
+ int hostFontSizeDp = host.getFontSize();
+ if (hostFontSizeDp <= 0) {
+ hostFontSizeDp = DEFAULT_FONT_SIZE_DP;
+ }
+ setFontSize(hostFontSizeDp);
// create terminal buffer and handle outgoing data
// this is probably status reply information
@@ -324,6 +333,10 @@ public class TerminalBridge implements VDUDisplay {
localOutput.add(s);
((vt320) buffer).putString(s);
+
+ // For accessibility
+ final char[] charArray = s.toCharArray();
+ propagateConsoleText(charArray, charArray.length);
}
}
@@ -377,7 +390,7 @@ public class TerminalBridge implements VDUDisplay {
relayThread.start();
// force font-size to make sure we resizePTY as needed
- setFontSize(fontSize);
+ setFontSize(fontSizeDp);
// finally send any post-login string, if requested
injectString(host.getPostLogin());
@@ -422,7 +435,7 @@ public class TerminalBridge implements VDUDisplay {
disconnectThread.setName("Disconnect");
disconnectThread.start();
- if (immediate) {
+ if (immediate || (host.getQuickDisconnect() && !host.getStayConnected())) {
awaitingClose = true;
if (disconnectListener != null)
disconnectListener.onDisconnected(TerminalBridge.this);
@@ -473,13 +486,18 @@ public class TerminalBridge implements VDUDisplay {
/**
* Request a different font size. Will make call to parentChanged() to make
* sure we resize PTY if needed.
+ *
+ * @param sizeDp Size of font in dp
*/
- /* package */ final void setFontSize(float size) {
- if (size <= 0.0)
+ /* package */ final void setFontSize(float sizeDp) {
+ if (sizeDp <= 0.0) {
return;
+ }
+
+ final int fontSizePx = (int) (sizeDp * this.displayDensity + 0.5f);
- defaultPaint.setTextSize(size);
- fontSize = size;
+ defaultPaint.setTextSize(fontSizePx);
+ fontSizeDp = sizeDp;
// read new metrics to get exact pixel dimensions
FontMetrics fm = defaultPaint.getFontMetrics();
@@ -491,13 +509,15 @@ public class TerminalBridge implements VDUDisplay {
charHeight = (int)Math.ceil(fm.descent - fm.top);
// refresh any bitmap with new font size
- if(parent != null)
+ if (parent != null) {
parentChanged(parent);
+ }
- for (FontSizeChangedListener ofscl : fontSizeChangedListeners)
- ofscl.onFontSizeChanged(size);
+ for (FontSizeChangedListener ofscl : fontSizeChangedListeners) {
+ ofscl.onFontSizeChanged(sizeDp);
+ }
- host.setFontSize((int) fontSize);
+ host.setFontSize((int) sizeDp);
manager.hostdb.updateFontSize(host);
forcedSize = false;
@@ -644,6 +664,12 @@ public class TerminalBridge implements VDUDisplay {
return buffer;
}
+ public void propagateConsoleText(char[] rawText, int length) {
+ if (parent != null) {
+ parent.propagateConsoleText(rawText, length);
+ }
+ }
+
public void onDraw() {
int fg, bg;
synchronized (buffer) {
@@ -758,51 +784,52 @@ public class TerminalBridge implements VDUDisplay {
/**
* Resize terminal to fit [rows]x[cols] in screen of size [width]x[height]
- * @param rows
- * @param cols
- * @param width
- * @param height
+ *
+ * @param rows desired number of text rows
+ * @param cols desired numbor of text colums
+ * @param width width of screen in pixels
+ * @param height height of screen in pixels
*/
public synchronized void resizeComputed(int cols, int rows, int width, int height) {
- float size = 8.0f;
+ float sizeDp = 8.0f;
float step = 8.0f;
float limit = 0.125f;
int direction;
- while ((direction = fontSizeCompare(size, cols, rows, width, height)) < 0)
- size += step;
+ while ((direction = fontSizeCompare(sizeDp, cols, rows, width, height)) < 0)
+ sizeDp += step;
if (direction == 0) {
- Log.d("fontsize", String.format("Found match at %f", size));
+ Log.d("fontsize", String.format("Found match at %f", sizeDp));
return;
}
step /= 2.0f;
- size -= step;
+ sizeDp -= step;
- while ((direction = fontSizeCompare(size, cols, rows, width, height)) != 0
+ while ((direction = fontSizeCompare(sizeDp, cols, rows, width, height)) != 0
&& step >= limit) {
step /= 2.0f;
if (direction > 0) {
- size -= step;
+ sizeDp -= step;
} else {
- size += step;
+ sizeDp += step;
}
}
if (direction > 0)
- size -= step;
+ sizeDp -= step;
this.columns = cols;
this.rows = rows;
- setFontSize(size);
+ setFontSize(sizeDp);
forcedSize = true;
}
- private int fontSizeCompare(float size, int cols, int rows, int width, int height) {
+ private int fontSizeCompare(float sizeDp, int cols, int rows, int width, int height) {
// read new metrics to get exact pixel dimensions
- defaultPaint.setTextSize(size);
+ defaultPaint.setTextSize((int) (sizeDp * this.displayDensity + 0.5f));
FontMetrics fm = defaultPaint.getFontMetrics();
float[] widths = new float[1];
@@ -810,7 +837,7 @@ public class TerminalBridge implements VDUDisplay {
int termWidth = (int)widths[0] * cols;
int termHeight = (int)Math.ceil(fm.descent - fm.top) * rows;
- Log.d("fontsize", String.format("font size %f resulted in %d x %d", size, termWidth, termHeight));
+ Log.d("fontsize", String.format("font size %fdp resulted in %d x %d", sizeDp, termWidth, termHeight));
// Check to see if it fits in resolution specified.
if (termWidth > width || termHeight > height)
@@ -941,7 +968,7 @@ public class TerminalBridge implements VDUDisplay {
String host = "(?:" + ipLiteral + "|" + ipv4address + "|" + regName + ")";
String port = "[0-9]*";
String authority = "(?:" + userinfo + "@)?" + host + "(?::" + port + ")?";
- String pchar = "(?:" + unreserved + "|" + pctEncoded + "|" + subDelims + ")";
+ String pchar = "(?:" + unreserved + "|" + pctEncoded + "|" + subDelims + "|@)";
String segment = pchar + "*";
String pathAbempty = "(?:/" + segment + ")*";
String segmentNz = pchar + "+";
@@ -990,16 +1017,16 @@ public class TerminalBridge implements VDUDisplay {
}
/**
- *
+ * Convenience function to increase the font size by a given step.
*/
public void increaseFontSize() {
- setFontSize(fontSize + FONT_SIZE_STEP);
+ setFontSize(fontSizeDp + FONT_SIZE_STEP);
}
/**
- *
+ * Convenience function to decrease the font size by a given step.
*/
public void decreaseFontSize() {
- setFontSize(fontSize - FONT_SIZE_STEP);
+ setFontSize(fontSizeDp - FONT_SIZE_STEP);
}
}
diff --git a/src/org/connectbot/service/TerminalKeyListener.java b/src/org/connectbot/service/TerminalKeyListener.java
index e768134..13e8dd1 100644
--- a/src/org/connectbot/service/TerminalKeyListener.java
+++ b/src/org/connectbot/service/TerminalKeyListener.java
@@ -39,39 +39,65 @@ import de.mud.terminal.vt320;
* @author kenny
*
*/
+@SuppressWarnings("deprecation") // for ClipboardManager
public class TerminalKeyListener implements OnKeyListener, OnSharedPreferenceChangeListener {
private static final String TAG = "ConnectBot.OnKeyListener";
- public final static int META_CTRL_ON = 0x01;
- public final static int META_CTRL_LOCK = 0x02;
- public final static int META_ALT_ON = 0x04;
- public final static int META_ALT_LOCK = 0x08;
- public final static int META_SHIFT_ON = 0x10;
- public final static int META_SHIFT_LOCK = 0x20;
- public final static int META_SLASH = 0x40;
- public final static int META_TAB = 0x80;
-
- // The bit mask of momentary and lock states for each
- public final static int META_CTRL_MASK = META_CTRL_ON | META_CTRL_LOCK;
- public final static int META_ALT_MASK = META_ALT_ON | META_ALT_LOCK;
- public final static int META_SHIFT_MASK = META_SHIFT_ON | META_SHIFT_LOCK;
+ // Constants for our private tracking of modifier state
+ public final static int OUR_CTRL_ON = 0x01;
+ public final static int OUR_CTRL_LOCK = 0x02;
+ public final static int OUR_ALT_ON = 0x04;
+ public final static int OUR_ALT_LOCK = 0x08;
+ public final static int OUR_SHIFT_ON = 0x10;
+ public final static int OUR_SHIFT_LOCK = 0x20;
+ private final static int OUR_SLASH = 0x40;
+ private final static int OUR_TAB = 0x80;
// All the transient key codes
- public final static int META_TRANSIENT = META_CTRL_ON | META_ALT_ON
- | META_SHIFT_ON;
+ private final static int OUR_TRANSIENT = OUR_CTRL_ON | OUR_ALT_ON
+ | OUR_SHIFT_ON | OUR_SLASH | OUR_TAB;
+
+ // The bit mask of momentary and lock states for each
+ private final static int OUR_CTRL_MASK = OUR_CTRL_ON | OUR_CTRL_LOCK;
+ private final static int OUR_ALT_MASK = OUR_ALT_ON | OUR_ALT_LOCK;
+ private final static int OUR_SHIFT_MASK = OUR_SHIFT_ON | OUR_SHIFT_LOCK;
+
+ // backport constants from api level 11
+ private final static int KEYCODE_ESCAPE = 111;
+ private final static int KEYCODE_CTRL_LEFT = 113;
+ private final static int KEYCODE_CTRL_RIGHT = 114;
+ private final static int KEYCODE_INSERT = 124;
+ private final static int KEYCODE_FORWARD_DEL = 112;
+ private final static int KEYCODE_MOVE_HOME = 122;
+ private final static int KEYCODE_MOVE_END = 123;
+ private final static int KEYCODE_PAGE_DOWN = 93;
+ private final static int KEYCODE_PAGE_UP = 92;
+ private final static int HC_META_CTRL_ON = 0x1000;
+ private final static int HC_META_CTRL_LEFT_ON = 0x2000;
+ private final static int HC_META_CTRL_RIGHT_ON = 0x4000;
+ private final static int HC_META_CTRL_MASK = HC_META_CTRL_ON | HC_META_CTRL_RIGHT_ON
+ | HC_META_CTRL_LEFT_ON;
+ private final static int HC_META_ALT_MASK = KeyEvent.META_ALT_ON | KeyEvent.META_ALT_LEFT_ON
+ | KeyEvent.META_ALT_RIGHT_ON;
private final TerminalManager manager;
private final TerminalBridge bridge;
private final VDUBuffer buffer;
- protected KeyCharacterMap keymap = KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD);
-
private String keymode = null;
- private boolean hardKeyboard = false;
+ private final boolean deviceHasHardKeyboard;
+ private boolean shiftedNumbersAreFKeysOnHardKeyboard;
+ private boolean controlNumbersAreFKeysOnSoftKeyboard;
+ private boolean volumeKeysChangeFontSize;
+ private int stickyMetas;
- private int metaState = 0;
+ private int ourMetaState = 0;
+ private int mDeadKey = 0;
+
+ // TODO add support for the new API.
private ClipboardManager clipboard = null;
+
private boolean selectingForCopy = false;
private final SelectionArea selectionArea;
@@ -93,10 +119,10 @@ public class TerminalKeyListener implements OnKeyListener, OnSharedPreferenceCha
prefs = PreferenceManager.getDefaultSharedPreferences(manager);
prefs.registerOnSharedPreferenceChangeListener(this);
- hardKeyboard = (manager.res.getConfiguration().keyboard
+ deviceHasHardKeyboard = (manager.res.getConfiguration().keyboard
== Configuration.KEYBOARD_QWERTY);
- updateKeymode();
+ updatePrefs();
}
/**
@@ -105,39 +131,44 @@ public class TerminalKeyListener implements OnKeyListener, OnSharedPreferenceCha
*/
public boolean onKey(View v, int keyCode, KeyEvent event) {
try {
- final boolean hardKeyboardHidden = manager.hardKeyboardHidden;
+ // skip keys if we aren't connected yet or have been disconnected
+ if (bridge.isDisconnected() || bridge.transport == null)
+ return false;
+
+ final boolean interpretAsHardKeyboard = deviceHasHardKeyboard &&
+ !manager.hardKeyboardHidden;
+ final boolean rightModifiersAreSlashAndTab = interpretAsHardKeyboard &&
+ PreferenceConstants.KEYMODE_RIGHT.equals(keymode);
+ final boolean leftModifiersAreSlashAndTab = interpretAsHardKeyboard &&
+ PreferenceConstants.KEYMODE_LEFT.equals(keymode);
+ final boolean shiftedNumbersAreFKeys = shiftedNumbersAreFKeysOnHardKeyboard &&
+ interpretAsHardKeyboard;
+ final boolean controlNumbersAreFKeys = controlNumbersAreFKeysOnSoftKeyboard &&
+ !interpretAsHardKeyboard;
// Ignore all key-up events except for the special keys
if (event.getAction() == KeyEvent.ACTION_UP) {
- // There's nothing here for virtual keyboard users.
- if (!hardKeyboard || (hardKeyboard && hardKeyboardHidden))
- return false;
-
- // skip keys if we aren't connected yet or have been disconnected
- if (bridge.isDisconnected() || bridge.transport == null)
- return false;
-
- if (PreferenceConstants.KEYMODE_RIGHT.equals(keymode)) {
+ if (rightModifiersAreSlashAndTab) {
if (keyCode == KeyEvent.KEYCODE_ALT_RIGHT
- && (metaState & META_SLASH) != 0) {
- metaState &= ~(META_SLASH | META_TRANSIENT);
+ && (ourMetaState & OUR_SLASH) != 0) {
+ ourMetaState &= ~OUR_TRANSIENT;
bridge.transport.write('/');
return true;
} else if (keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT
- && (metaState & META_TAB) != 0) {
- metaState &= ~(META_TAB | META_TRANSIENT);
+ && (ourMetaState & OUR_TAB) != 0) {
+ ourMetaState &= ~OUR_TRANSIENT;
bridge.transport.write(0x09);
return true;
}
- } else if (PreferenceConstants.KEYMODE_LEFT.equals(keymode)) {
+ } else if (leftModifiersAreSlashAndTab) {
if (keyCode == KeyEvent.KEYCODE_ALT_LEFT
- && (metaState & META_SLASH) != 0) {
- metaState &= ~(META_SLASH | META_TRANSIENT);
+ && (ourMetaState & OUR_SLASH) != 0) {
+ ourMetaState &= ~OUR_TRANSIENT;
bridge.transport.write('/');
return true;
} else if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
- && (metaState & META_TAB) != 0) {
- metaState &= ~(META_TAB | META_TRANSIENT);
+ && (ourMetaState & OUR_TAB) != 0) {
+ ourMetaState &= ~OUR_TRANSIENT;
bridge.transport.write(0x09);
return true;
}
@@ -146,80 +177,21 @@ public class TerminalKeyListener implements OnKeyListener, OnSharedPreferenceCha
return false;
}
- // check for terminal resizing keys
- if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
- bridge.increaseFontSize();
- return true;
- } else if(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
- bridge.decreaseFontSize();
- return true;
- }
+ //Log.i("CBKeyDebug", KeyEventUtil.describeKeyEvent(keyCode, event));
- // skip keys if we aren't connected yet or have been disconnected
- if (bridge.isDisconnected() || bridge.transport == null)
- return false;
-
- bridge.resetScrollPosition();
-
- boolean printing = (keymap.isPrintingKey(keyCode) || keyCode == KeyEvent.KEYCODE_SPACE);
-
- // otherwise pass through to existing session
- // print normal keys
- if (printing) {
- int curMetaState = event.getMetaState();
-
- metaState &= ~(META_SLASH | META_TAB);
-
- if ((metaState & META_SHIFT_MASK) != 0) {
- curMetaState |= KeyEvent.META_SHIFT_ON;
- metaState &= ~META_SHIFT_ON;
- bridge.redraw();
- }
-
- if ((metaState & META_ALT_MASK) != 0) {
- curMetaState |= KeyEvent.META_ALT_ON;
- metaState &= ~META_ALT_ON;
- bridge.redraw();
- }
-
- int key = keymap.get(keyCode, curMetaState);
-
- if ((metaState & META_CTRL_MASK) != 0) {
- metaState &= ~META_CTRL_ON;
- bridge.redraw();
-
- if ((!hardKeyboard || (hardKeyboard && hardKeyboardHidden))
- && sendFunctionKey(keyCode))
- return true;
-
- // Support CTRL-a through CTRL-z
- if (key >= 0x61 && key <= 0x7A)
- key -= 0x60;
- // Support CTRL-A through CTRL-_
- else if (key >= 0x41 && key <= 0x5F)
- key -= 0x40;
- else if (key == 0x20)
- key = 0x00;
- else if (key == 0x3F)
- key = 0x7F;
- }
-
- // handle pressing f-keys
- if ((hardKeyboard && !hardKeyboardHidden)
- && (curMetaState & KeyEvent.META_SHIFT_ON) != 0
- && sendFunctionKey(keyCode))
+ if (volumeKeysChangeFontSize) {
+ if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
+ bridge.increaseFontSize();
return true;
-
- if (key < 0x80)
- bridge.transport.write(key);
- else
- // TODO write encoding routine that doesn't allocate each time
- bridge.transport.write(new String(Character.toChars(key))
- .getBytes(encoding));
-
- return true;
+ } else if(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
+ bridge.decreaseFontSize();
+ return true;
+ }
}
+ bridge.resetScrollPosition();
+
+ // Handle potentially multi-character IME input.
if (keyCode == KeyEvent.KEYCODE_UNKNOWN &&
event.getAction() == KeyEvent.ACTION_MULTIPLE) {
byte[] input = event.getCharacters().getBytes(encoding);
@@ -227,55 +199,158 @@ public class TerminalKeyListener implements OnKeyListener, OnSharedPreferenceCha
return true;
}
- // try handling keymode shortcuts
- if (hardKeyboard && !hardKeyboardHidden &&
- event.getRepeatCount() == 0) {
- if (PreferenceConstants.KEYMODE_RIGHT.equals(keymode)) {
+ /// Handle alt and shift keys if they aren't repeating
+ if (event.getRepeatCount() == 0) {
+ if (rightModifiersAreSlashAndTab) {
switch (keyCode) {
case KeyEvent.KEYCODE_ALT_RIGHT:
- metaState |= META_SLASH;
+ ourMetaState |= OUR_SLASH;
return true;
case KeyEvent.KEYCODE_SHIFT_RIGHT:
- metaState |= META_TAB;
+ ourMetaState |= OUR_TAB;
return true;
case KeyEvent.KEYCODE_SHIFT_LEFT:
- metaPress(META_SHIFT_ON);
+ metaPress(OUR_SHIFT_ON);
return true;
case KeyEvent.KEYCODE_ALT_LEFT:
- metaPress(META_ALT_ON);
+ metaPress(OUR_ALT_ON);
return true;
}
- } else if (PreferenceConstants.KEYMODE_LEFT.equals(keymode)) {
+ } else if (leftModifiersAreSlashAndTab) {
switch (keyCode) {
case KeyEvent.KEYCODE_ALT_LEFT:
- metaState |= META_SLASH;
+ ourMetaState |= OUR_SLASH;
return true;
case KeyEvent.KEYCODE_SHIFT_LEFT:
- metaState |= META_TAB;
+ ourMetaState |= OUR_TAB;
return true;
case KeyEvent.KEYCODE_SHIFT_RIGHT:
- metaPress(META_SHIFT_ON);
+ metaPress(OUR_SHIFT_ON);
return true;
case KeyEvent.KEYCODE_ALT_RIGHT:
- metaPress(META_ALT_ON);
+ metaPress(OUR_ALT_ON);
return true;
}
} else {
switch (keyCode) {
case KeyEvent.KEYCODE_ALT_LEFT:
case KeyEvent.KEYCODE_ALT_RIGHT:
- metaPress(META_ALT_ON);
+ metaPress(OUR_ALT_ON);
return true;
case KeyEvent.KEYCODE_SHIFT_LEFT:
case KeyEvent.KEYCODE_SHIFT_RIGHT:
- metaPress(META_SHIFT_ON);
+ metaPress(OUR_SHIFT_ON);
return true;
}
}
+ if (keyCode == KEYCODE_CTRL_LEFT || keyCode == KEYCODE_CTRL_RIGHT) {
+ metaPress(OUR_CTRL_ON);
+ return true;
+ }
+ }
+
+ if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
+ if (selectingForCopy) {
+ if (selectionArea.isSelectingOrigin())
+ selectionArea.finishSelectingOrigin();
+ else {
+ if (clipboard != null) {
+ // copy selected area to clipboard
+ String copiedText = selectionArea.copyFrom(buffer);
+ clipboard.setText(copiedText);
+ // XXX STOPSHIP
+// manager.notifyUser(manager.getString(
+// R.string.console_copy_done,
+// copiedText.length()));
+ selectingForCopy = false;
+ selectionArea.reset();
+ }
+ }
+ } else {
+ if ((ourMetaState & OUR_CTRL_ON) != 0) {
+ sendEscape();
+ ourMetaState &= ~OUR_CTRL_ON;
+ } else
+ metaPress(OUR_CTRL_ON, true);
+ }
+ bridge.redraw();
+ return true;
+ }
+
+ int derivedMetaState = event.getMetaState();
+ if ((ourMetaState & OUR_SHIFT_MASK) != 0)
+ derivedMetaState |= KeyEvent.META_SHIFT_ON;
+ if ((ourMetaState & OUR_ALT_MASK) != 0)
+ derivedMetaState |= KeyEvent.META_ALT_ON;
+ if ((ourMetaState & OUR_CTRL_MASK) != 0)
+ derivedMetaState |= HC_META_CTRL_ON;
+
+ if ((ourMetaState & OUR_TRANSIENT) != 0) {
+ ourMetaState &= ~OUR_TRANSIENT;
+ bridge.redraw();
+ }
+
+ // Test for modified numbers becoming function keys
+ if (shiftedNumbersAreFKeys && (derivedMetaState & KeyEvent.META_SHIFT_ON) != 0) {
+ if (sendFunctionKey(keyCode))
+ return true;
+ }
+ if (controlNumbersAreFKeys && (derivedMetaState & HC_META_CTRL_ON) != 0) {
+ if (sendFunctionKey(keyCode))
+ return true;
+ }
+
+ // Ask the system to use the keymap to give us the unicode character for this key,
+ // with our derived modifier state applied.
+ int uchar = event.getUnicodeChar(derivedMetaState & ~HC_META_CTRL_MASK);
+ int ucharWithoutAlt = event.getUnicodeChar(
+ derivedMetaState & ~(HC_META_ALT_MASK | HC_META_CTRL_MASK));
+ if (uchar == 0) {
+ // Keymap doesn't know the key with alt on it, so just go with the unmodified version
+ uchar = ucharWithoutAlt;
+ }
+ else if (uchar != ucharWithoutAlt) {
+ // The alt key was used to modify the character returned; therefore, drop the alt
+ // modifier from the state so we don't end up sending alt+key.
+ derivedMetaState &= ~HC_META_ALT_MASK;
+ }
+
+ // Remove shift from the modifier state as it has already been used by getUnicodeChar.
+ derivedMetaState &= ~KeyEvent.META_SHIFT_ON;
+
+ if ((uchar & KeyCharacterMap.COMBINING_ACCENT) != 0) {
+ mDeadKey = uchar & KeyCharacterMap.COMBINING_ACCENT_MASK;
+ return true;
+ }
+
+ if (mDeadKey != 0) {
+ uchar = KeyCharacterMap.getDeadChar(mDeadKey, keyCode);
+ mDeadKey = 0;
+ }
+
+ // If we have a defined non-control character
+ if (uchar >= 0x20) {
+ if ((derivedMetaState & HC_META_CTRL_ON) != 0)
+ uchar = keyAsControl(uchar);
+ if ((derivedMetaState & KeyEvent.META_ALT_ON) != 0)
+ sendEscape();
+ if (uchar < 0x80)
+ bridge.transport.write(uchar);
+ else
+ // TODO write encoding routine that doesn't allocate each time
+ bridge.transport.write(new String(Character.toChars(uchar))
+ .getBytes(encoding));
+ return true;
}
// look for special chars
switch(keyCode) {
+ case KEYCODE_ESCAPE:
+ sendEscape();
+ return true;
+ case KeyEvent.KEYCODE_TAB:
+ bridge.transport.write(0x09);
+ return true;
case KeyEvent.KEYCODE_CAMERA:
// check to see which shortcut the camera button triggers
@@ -299,11 +374,9 @@ public class TerminalKeyListener implements OnKeyListener, OnSharedPreferenceCha
case KeyEvent.KEYCODE_DEL:
((vt320) buffer).keyPressed(vt320.KEY_BACK_SPACE, ' ',
getStateForBuffer());
- metaState &= ~META_TRANSIENT;
return true;
case KeyEvent.KEYCODE_ENTER:
((vt320)buffer).keyTyped(vt320.KEY_ENTER, ' ', 0);
- metaState &= ~META_TRANSIENT;
return true;
case KeyEvent.KEYCODE_DPAD_LEFT:
@@ -313,7 +386,6 @@ public class TerminalKeyListener implements OnKeyListener, OnSharedPreferenceCha
} else {
((vt320) buffer).keyPressed(vt320.KEY_LEFT, ' ',
getStateForBuffer());
- metaState &= ~META_TRANSIENT;
bridge.tryKeyVibrate();
}
return true;
@@ -325,7 +397,6 @@ public class TerminalKeyListener implements OnKeyListener, OnSharedPreferenceCha
} else {
((vt320) buffer).keyPressed(vt320.KEY_UP, ' ',
getStateForBuffer());
- metaState &= ~META_TRANSIENT;
bridge.tryKeyVibrate();
}
return true;
@@ -337,7 +408,6 @@ public class TerminalKeyListener implements OnKeyListener, OnSharedPreferenceCha
} else {
((vt320) buffer).keyPressed(vt320.KEY_DOWN, ' ',
getStateForBuffer());
- metaState &= ~META_TRANSIENT;
bridge.tryKeyVibrate();
}
return true;
@@ -349,40 +419,33 @@ public class TerminalKeyListener implements OnKeyListener, OnSharedPreferenceCha
} else {
((vt320) buffer).keyPressed(vt320.KEY_RIGHT, ' ',
getStateForBuffer());
- metaState &= ~META_TRANSIENT;
bridge.tryKeyVibrate();
}
return true;
- case KeyEvent.KEYCODE_DPAD_CENTER:
- if (selectingForCopy) {
- if (selectionArea.isSelectingOrigin())
- selectionArea.finishSelectingOrigin();
- else {
- if (clipboard != null) {
- // copy selected area to clipboard
- String copiedText = selectionArea.copyFrom(buffer);
-
- clipboard.setText(copiedText);
- // XXX STOPSHIP
-// manager.notifyUser(manager.getString(
-// R.string.console_copy_done,
-// copiedText.length()));
-
- selectingForCopy = false;
- selectionArea.reset();
- }
- }
- } else {
- if ((metaState & META_CTRL_ON) != 0) {
- sendEscape();
- metaState &= ~META_CTRL_ON;
- } else
- metaPress(META_CTRL_ON);
- }
-
- bridge.redraw();
-
+ case KEYCODE_INSERT:
+ ((vt320) buffer).keyPressed(vt320.KEY_INSERT, ' ',
+ getStateForBuffer());
+ return true;
+ case KEYCODE_FORWARD_DEL:
+ ((vt320) buffer).keyPressed(vt320.KEY_DELETE, ' ',
+ getStateForBuffer());
+ return true;
+ case KEYCODE_MOVE_HOME:
+ ((vt320) buffer).keyPressed(vt320.KEY_HOME, ' ',
+ getStateForBuffer());
+ return true;
+ case KEYCODE_MOVE_END:
+ ((vt320) buffer).keyPressed(vt320.KEY_END, ' ',
+ getStateForBuffer());
+ return true;
+ case KEYCODE_PAGE_UP:
+ ((vt320) buffer).keyPressed(vt320.KEY_PAGE_UP, ' ',
+ getStateForBuffer());
+ return true;
+ case KEYCODE_PAGE_DOWN:
+ ((vt320) buffer).keyPressed(vt320.KEY_PAGE_DOWN, ' ',
+ getStateForBuffer());
return true;
}
@@ -402,6 +465,22 @@ public class TerminalKeyListener implements OnKeyListener, OnSharedPreferenceCha
return false;
}
+ public int keyAsControl(int key) {
+ // Support CTRL-a through CTRL-z
+ if (key >= 0x61 && key <= 0x7A)
+ key -= 0x60;
+ // Support CTRL-A through CTRL-_
+ else if (key >= 0x41 && key <= 0x5F)
+ key -= 0x40;
+ // CTRL-space sends NULL
+ else if (key == 0x20)
+ key = 0x00;
+ // CTRL-? sends DEL
+ else if (key == 0x3F)
+ key = 0x7F;
+ return key;
+ }
+
public void sendEscape() {
((vt320)buffer).keyTyped(vt320.KEY_ESCAPE, ' ', 0);
}
@@ -456,17 +535,25 @@ public class TerminalKeyListener implements OnKeyListener, OnSharedPreferenceCha
*
* @param code
*/
- public void metaPress(int code) {
- if ((metaState & (code << 1)) != 0) {
- metaState &= ~(code << 1);
- } else if ((metaState & code) != 0) {
- metaState &= ~code;
- metaState |= code << 1;
- } else
- metaState |= code;
+ public void metaPress(int code, boolean forceSticky) {
+ if ((ourMetaState & (code << 1)) != 0) {
+ ourMetaState &= ~(code << 1);
+ } else if ((ourMetaState & code) != 0) {
+ ourMetaState &= ~code;
+ ourMetaState |= code << 1;
+ } else if (forceSticky || (stickyMetas & code) != 0) {
+ ourMetaState |= code;
+ } else {
+ // skip redraw
+ return;
+ }
bridge.redraw();
}
+ public void metaPress(int code) {
+ metaPress(code, false);
+ }
+
public void setTerminalKeyMode(String keymode) {
this.keymode = keymode;
}
@@ -474,18 +561,22 @@ public class TerminalKeyListener implements OnKeyListener, OnSharedPreferenceCha
private int getStateForBuffer() {
int bufferState = 0;
- if ((metaState & META_CTRL_MASK) != 0)
+ if ((ourMetaState & OUR_CTRL_MASK) != 0)
bufferState |= vt320.KEY_CONTROL;
- if ((metaState & META_SHIFT_MASK) != 0)
+ if ((ourMetaState & OUR_SHIFT_MASK) != 0)
bufferState |= vt320.KEY_SHIFT;
- if ((metaState & META_ALT_MASK) != 0)
+ if ((ourMetaState & OUR_ALT_MASK) != 0)
bufferState |= vt320.KEY_ALT;
return bufferState;
}
public int getMetaState() {
- return metaState;
+ return ourMetaState;
+ }
+
+ public int getDeadKey() {
+ return mDeadKey;
}
public void setClipboardManager(ClipboardManager clipboard) {
@@ -494,13 +585,31 @@ public class TerminalKeyListener implements OnKeyListener, OnSharedPreferenceCha
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
String key) {
- if (PreferenceConstants.KEYMODE.equals(key)) {
- updateKeymode();
+ if (PreferenceConstants.KEYMODE.equals(key) ||
+ PreferenceConstants.SHIFT_FKEYS.equals(key) ||
+ PreferenceConstants.CTRL_FKEYS.equals(key) ||
+ PreferenceConstants.VOLUME_FONT.equals(key) ||
+ PreferenceConstants.STICKY_MODIFIERS.equals(key)) {
+ updatePrefs();
}
}
- private void updateKeymode() {
- keymode = prefs.getString(PreferenceConstants.KEYMODE, PreferenceConstants.KEYMODE_RIGHT);
+ private void updatePrefs() {
+ keymode = prefs.getString(PreferenceConstants.KEYMODE, PreferenceConstants.KEYMODE_NONE);
+ shiftedNumbersAreFKeysOnHardKeyboard =
+ prefs.getBoolean(PreferenceConstants.SHIFT_FKEYS, false);
+ controlNumbersAreFKeysOnSoftKeyboard =
+ prefs.getBoolean(PreferenceConstants.CTRL_FKEYS, false);
+ volumeKeysChangeFontSize = prefs.getBoolean(PreferenceConstants.VOLUME_FONT, true);
+ String stickyModifiers = prefs.getString(PreferenceConstants.STICKY_MODIFIERS,
+ PreferenceConstants.NO);
+ if (PreferenceConstants.ALT.equals(stickyModifiers)) {
+ stickyMetas = OUR_ALT_ON;
+ } else if (PreferenceConstants.YES.equals(stickyModifiers)) {
+ stickyMetas = OUR_SHIFT_ON | OUR_CTRL_ON | OUR_ALT_ON;
+ } else {
+ stickyMetas = 0;
+ }
}
public void setCharset(String encoding) {
diff --git a/src/org/connectbot/service/TerminalManager.java b/src/org/connectbot/service/TerminalManager.java
index e43583b..8978870 100644
--- a/src/org/connectbot/service/TerminalManager.java
+++ b/src/org/connectbot/service/TerminalManager.java
@@ -19,6 +19,7 @@ package org.connectbot.service;
import java.io.IOException;
import java.lang.ref.WeakReference;
+import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Arrays;
@@ -26,9 +27,9 @@ import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Timer;
import java.util.TimerTask;
-import java.util.Map.Entry;
import org.connectbot.R;
import org.connectbot.bean.HostBean;
@@ -59,12 +60,10 @@ import android.os.Vibrator;
import android.preference.PreferenceManager;
import android.util.Log;
-import com.nullwire.trace.ExceptionHandler;
-
/**
- * Manager for SSH connections that runs as a background service. This service
- * holds a list of currently connected SSH bridges that are ready for connection
- * up to a GUI if needed.
+ * Manager for SSH connections that runs as a service. This service holds a list
+ * of currently connected SSH bridges that are ready for connection up to a GUI
+ * if needed.
*
* @author jsharkey
*/
@@ -120,9 +119,7 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen
@Override
public void onCreate() {
- Log.i(TAG, "Starting background service");
-
- ExceptionHandler.register(this);
+ Log.i(TAG, "Starting service");
prefs = PreferenceManager.getDefaultSharedPreferences(this);
prefs.registerOnSharedPreferenceChangeListener(this);
@@ -142,9 +139,9 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen
try {
PrivateKey privKey = PubkeyUtils.decodePrivate(pubkey.getPrivateKey(), pubkey.getType());
PublicKey pubKey = PubkeyUtils.decodePublic(pubkey.getPublicKey(), pubkey.getType());
- Object trileadKey = PubkeyUtils.convertToTrilead(privKey, pubKey);
+ KeyPair pair = new KeyPair(pubKey, privKey);
- addKey(pubkey, trileadKey);
+ addKey(pubkey, pair);
} catch (Exception e) {
Log.d(TAG, String.format("Problem adding key '%s' to in-memory cache", pubkey.getNickname()), e);
}
@@ -171,7 +168,7 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen
@Override
public void onDestroy() {
- Log.i(TAG, "Destroying background service");
+ Log.i(TAG, "Destroying service");
disconnectAll(true, false);
@@ -366,21 +363,21 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen
return loadedKeypairs.containsKey(nickname);
}
- public void addKey(PubkeyBean pubkey, Object trileadKey) {
- addKey(pubkey, trileadKey, false);
+ public void addKey(PubkeyBean pubkey, KeyPair pair) {
+ addKey(pubkey, pair, false);
}
- public void addKey(PubkeyBean pubkey, Object trileadKey, boolean force) {
+ public void addKey(PubkeyBean pubkey, KeyPair pair, boolean force) {
if (!savingKeys && !force)
return;
removeKey(pubkey.getNickname());
- byte[] sshPubKey = PubkeyUtils.extractOpenSSHPublic(trileadKey);
+ byte[] sshPubKey = PubkeyUtils.extractOpenSSHPublic(pair);
KeyHolder keyHolder = new KeyHolder();
keyHolder.bean = pubkey;
- keyHolder.trileadKey = trileadKey;
+ keyHolder.pair = pair;
keyHolder.openSSHPubkey = sshPubKey;
loadedKeypairs.put(pubkey.getNickname(), keyHolder);
@@ -420,18 +417,18 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen
return false;
}
- public Object getKey(String nickname) {
+ public KeyPair getKey(String nickname) {
if (loadedKeypairs.containsKey(nickname)) {
KeyHolder keyHolder = loadedKeypairs.get(nickname);
- return keyHolder.trileadKey;
+ return keyHolder.pair;
} else
return null;
}
- public Object getKey(byte[] publicKey) {
+ public KeyPair getKey(byte[] publicKey) {
for (KeyHolder keyHolder : loadedKeypairs.values()) {
if (Arrays.equals(keyHolder.openSSHPubkey, publicKey))
- return keyHolder.trileadKey;
+ return keyHolder.pair;
}
return null;
}
@@ -456,7 +453,7 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen
idleTimer.schedule(new IdleTask(), IDLE_TIMEOUT);
}
} else {
- Log.d(TAG, "Stopping background service immediately");
+ Log.d(TAG, "Stopping service immediately");
stopSelf();
}
}
@@ -482,16 +479,18 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen
@Override
public IBinder onBind(Intent intent) {
- Log.i(TAG, "Someone bound to TerminalManager");
-
+ Log.i(TAG, "Someone bound to TerminalManager with " + bridges.size() + " bridges active");
+ keepServiceAlive();
setResizeAllowed(true);
+ return binder;
+ }
+ /**
+ * Make sure we stay running to maintain the bridges. Later {@link #stopNow} should be called to stop the service.
+ */
+ private void keepServiceAlive() {
stopIdleTimer();
-
- // Make sure we stay running to maintain the bridges
startService(new Intent(this, TerminalManager.class));
-
- return binder;
}
@Override
@@ -506,17 +505,14 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen
@Override
public void onRebind(Intent intent) {
super.onRebind(intent);
-
+ Log.i(TAG, "Someone rebound to TerminalManager with " + bridges.size() + " bridges active");
+ keepServiceAlive();
setResizeAllowed(true);
-
- Log.i(TAG, "Someone rebound to TerminalManager");
-
- stopIdleTimer();
}
@Override
public boolean onUnbind(Intent intent) {
- Log.i(TAG, "Someone unbound from TerminalManager");
+ Log.i(TAG, "Someone unbound from TerminalManager with " + bridges.size() + " bridges active");
setResizeAllowed(true);
@@ -528,9 +524,6 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen
}
private class IdleTask extends TimerTask {
- /* (non-Javadoc)
- * @see java.util.TimerTask#run()
- */
@Override
public void run() {
Log.d(TAG, String.format("Stopping service after timeout of ~%d seconds", IDLE_TIMEOUT / 1000));
@@ -651,7 +644,7 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen
public static class KeyHolder {
public PubkeyBean bean;
- public Object trileadKey;
+ public KeyPair pair;
public byte[] openSSHPubkey;
}
diff --git a/src/org/connectbot/transport/Local.java b/src/org/connectbot/transport/Local.java
index 5ace1b0..f7bbf11 100644
--- a/src/org/connectbot/transport/Local.java
+++ b/src/org/connectbot/transport/Local.java
@@ -153,6 +153,11 @@ public class Local extends AbsTransport {
@Override
public void setDimensions(int columns, int rows, int width, int height) {
+ // We are not connected yet.
+ if (shellFd == null) {
+ return;
+ }
+
try {
Exec.setPtyWindowSize(shellFd, rows, columns, width, height);
} catch (Exception e) {
diff --git a/src/org/connectbot/transport/SSH.java b/src/org/connectbot/transport/SSH.java
index 8a90f72..b6a5714 100644
--- a/src/org/connectbot/transport/SSH.java
+++ b/src/org/connectbot/transport/SSH.java
@@ -22,14 +22,20 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
+import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
@@ -62,11 +68,7 @@ import com.trilead.ssh2.LocalPortForwarder;
import com.trilead.ssh2.ServerHostKeyVerifier;
import com.trilead.ssh2.Session;
import com.trilead.ssh2.crypto.PEMDecoder;
-import com.trilead.ssh2.signature.DSAPrivateKey;
-import com.trilead.ssh2.signature.DSAPublicKey;
import com.trilead.ssh2.signature.DSASHA1Verify;
-import com.trilead.ssh2.signature.RSAPrivateKey;
-import com.trilead.ssh2.signature.RSAPublicKey;
import com.trilead.ssh2.signature.RSASHA1Verify;
/**
@@ -79,8 +81,9 @@ public class SSH extends AbsTransport implements ConnectionMonitor, InteractiveC
}
/**
+ * @param host
* @param bridge
- * @param db
+ * @param manager
*/
public SSH(HostBean host, TerminalBridge bridge, TerminalManager manager) {
super(host, bridge, manager);
@@ -141,7 +144,7 @@ public class SSH extends AbsTransport implements ConnectionMonitor, InteractiveC
KnownHosts hosts = manager.hostdb.getKnownHosts();
Boolean result;
- String matchName = String.format("%s:%d", hostname, port);
+ String matchName = String.format(Locale.US, "%s:%d", hostname, port);
String fingerprint = KnownHosts.createHexFingerprint(serverHostKeyAlgorithm, serverHostKey);
@@ -150,6 +153,8 @@ public class SSH extends AbsTransport implements ConnectionMonitor, InteractiveC
algorithmName = "RSA";
else if ("ssh-dss".equals(serverHostKeyAlgorithm))
algorithmName = "DSA";
+ else if (serverHostKeyAlgorithm.startsWith("ecdsa-"))
+ algorithmName = "EC";
else
algorithmName = serverHostKeyAlgorithm;
@@ -234,7 +239,7 @@ public class SSH extends AbsTransport implements ConnectionMonitor, InteractiveC
continue;
if (this.tryPublicKey(host.getUsername(), entry.getKey(),
- entry.getValue().trileadKey)) {
+ entry.getValue().pair)) {
finishConnection();
break;
}
@@ -293,7 +298,8 @@ public class SSH extends AbsTransport implements ConnectionMonitor, InteractiveC
* @throws IOException
*/
private boolean tryPublicKey(PubkeyBean pubkey) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException {
- Object trileadKey = null;
+ KeyPair pair = null;
+
if(manager.isKeyLoaded(pubkey.getNickname())) {
// load this key from memory if its already there
Log.d(TAG, String.format("Found unlocked key '%s' already in-memory", pubkey.getNickname()));
@@ -303,7 +309,7 @@ public class SSH extends AbsTransport implements ConnectionMonitor, InteractiveC
return false;
}
- trileadKey = manager.getKey(pubkey.getNickname());
+ pair = manager.getKey(pubkey.getNickname());
} else {
// otherwise load key from database and prompt for password as needed
String password = null;
@@ -318,7 +324,7 @@ public class SSH extends AbsTransport implements ConnectionMonitor, InteractiveC
if(PubkeyDatabase.KEY_TYPE_IMPORTED.equals(pubkey.getType())) {
// load specific key using pem format
- trileadKey = PEMDecoder.decode(new String(pubkey.getPrivateKey()).toCharArray(), password);
+ pair = PEMDecoder.decode(new String(pubkey.getPrivateKey()).toCharArray(), password);
} else {
// load using internal generated format
PrivateKey privKey;
@@ -332,26 +338,25 @@ public class SSH extends AbsTransport implements ConnectionMonitor, InteractiveC
return false;
}
- PublicKey pubKey = PubkeyUtils.decodePublic(pubkey.getPublicKey(),
- pubkey.getType());
+ PublicKey pubKey = PubkeyUtils.decodePublic(pubkey.getPublicKey(), pubkey.getType());
// convert key to trilead format
- trileadKey = PubkeyUtils.convertToTrilead(privKey, pubKey);
+ pair = new KeyPair(pubKey, privKey);
Log.d(TAG, "Unlocked key " + PubkeyUtils.formatKey(pubKey));
}
Log.d(TAG, String.format("Unlocked key '%s'", pubkey.getNickname()));
// save this key in memory
- manager.addKey(pubkey, trileadKey);
+ manager.addKey(pubkey, pair);
}
- return tryPublicKey(host.getUsername(), pubkey.getNickname(), trileadKey);
+ return tryPublicKey(host.getUsername(), pubkey.getNickname(), pair);
}
- private boolean tryPublicKey(String username, String keyNickname, Object trileadKey) throws IOException {
+ private boolean tryPublicKey(String username, String keyNickname, KeyPair pair) throws IOException {
//bridge.outputLine(String.format("Attempting 'publickey' with key '%s' [%s]...", keyNickname, trileadKey.toString()));
- boolean success = connection.authenticateWithPublicKey(username, trileadKey);
+ boolean success = connection.authenticateWithPublicKey(username, pair);
if(!success)
bridge.outputLine(manager.res.getString(R.string.terminal_auth_pubkey_fail, keyNickname));
return success;
@@ -427,6 +432,8 @@ public class SSH extends AbsTransport implements ConnectionMonitor, InteractiveC
connectionInfo = connection.connect(new HostKeyVerifier());
connected = true;
+ bridge.outputLine(manager.res.getString(R.string.terminal_kex_algorithm,
+ connectionInfo.keyExchangeAlgorithm));
if (connectionInfo.clientToServerCryptoAlgorithm
.equals(connectionInfo.serverToClientCryptoAlgorithm)
&& connectionInfo.clientToServerMACAlgorithm
@@ -449,8 +456,13 @@ public class SSH extends AbsTransport implements ConnectionMonitor, InteractiveC
Log.e(TAG, "Problem in SSH connection thread during authentication", e);
// Display the reason in the text.
- bridge.outputLine(e.getCause().getMessage());
+ Throwable t = e.getCause();
+ do {
+ bridge.outputLine(t.getMessage());
+ t = t.getCause();
+ } while (t != null);
+ close();
onDisconnect();
return;
}
@@ -485,8 +497,6 @@ public class SSH extends AbsTransport implements ConnectionMonitor, InteractiveC
}
private void onDisconnect() {
- close();
-
bridge.dispatchDisconnect(false);
}
@@ -517,6 +527,7 @@ public class SSH extends AbsTransport implements ConnectionMonitor, InteractiveC
}
if ((newConditions & ChannelCondition.EOF) != 0) {
+ close();
onDisconnect();
throw new IOException("Remote end closed connection");
}
@@ -740,9 +751,9 @@ public class SSH extends AbsTransport implements ConnectionMonitor, InteractiveC
@Override
public String getDefaultNickname(String username, String hostname, int port) {
if (port == DEFAULT_PORT) {
- return String.format("%s@%s", username, hostname);
+ return String.format(Locale.US, "%s@%s", username, hostname);
} else {
- return String.format("%s@%s:%d", username, hostname, port);
+ return String.format(Locale.US, "%s@%s:%d", username, hostname, port);
}
}
@@ -859,14 +870,15 @@ public class SSH extends AbsTransport implements ConnectionMonitor, InteractiveC
Map<String,byte[]> pubKeys = new HashMap<String,byte[]>(manager.loadedKeypairs.size());
for (Entry<String,KeyHolder> entry : manager.loadedKeypairs.entrySet()) {
- Object trileadKey = entry.getValue().trileadKey;
+ KeyPair pair = entry.getValue().pair;
try {
- if (trileadKey instanceof RSAPrivateKey) {
- RSAPublicKey pubkey = ((RSAPrivateKey) trileadKey).getPublicKey();
+ PrivateKey privKey = pair.getPrivate();
+ if (privKey instanceof RSAPrivateKey) {
+ RSAPublicKey pubkey = (RSAPublicKey) pair.getPublic();
pubKeys.put(entry.getKey(), RSASHA1Verify.encodeSSHRSAPublicKey(pubkey));
- } else if (trileadKey instanceof DSAPrivateKey) {
- DSAPublicKey pubkey = ((DSAPrivateKey) trileadKey).getPublicKey();
+ } else if (privKey instanceof DSAPrivateKey) {
+ DSAPublicKey pubkey = (DSAPublicKey) pair.getPublic();
pubKeys.put(entry.getKey(), DSASHA1Verify.encodeSSHDSAPublicKey(pubkey));
} else
continue;
@@ -878,7 +890,7 @@ public class SSH extends AbsTransport implements ConnectionMonitor, InteractiveC
return pubKeys;
}
- public Object getPrivateKey(byte[] publicKey) {
+ public KeyPair getKeyPair(byte[] publicKey) {
String nickname = manager.getKeyNickname(publicKey);
if (nickname == null)
@@ -902,13 +914,13 @@ public class SSH extends AbsTransport implements ConnectionMonitor, InteractiveC
return result;
}
- public boolean addIdentity(Object key, String comment, boolean confirmUse, int lifetime) {
+ public boolean addIdentity(KeyPair pair, String comment, boolean confirmUse, int lifetime) {
PubkeyBean pubkey = new PubkeyBean();
// pubkey.setType(PubkeyDatabase.KEY_TYPE_IMPORTED);
pubkey.setNickname(comment);
pubkey.setConfirmUse(confirmUse);
pubkey.setLifetime(lifetime);
- manager.addKey(pubkey, key);
+ manager.addKey(pubkey, pair);
return true;
}
diff --git a/src/org/connectbot/transport/Telnet.java b/src/org/connectbot/transport/Telnet.java
index 5fde2f6..16cbd0e 100644
--- a/src/org/connectbot/transport/Telnet.java
+++ b/src/org/connectbot/transport/Telnet.java
@@ -287,7 +287,7 @@ public class Telnet extends AbsTransport {
host.setHostname(uri.getHost());
int port = uri.getPort();
- if (port < 0)
+ if (port < 0 || port > 65535)
port = DEFAULT_PORT;
host.setPort(port);
@@ -309,7 +309,7 @@ public class Telnet extends AbsTransport {
selection.put(HostDatabase.FIELD_HOST_HOSTNAME, uri.getHost());
int port = uri.getPort();
- if (port < 0)
+ if (port < 0 || port > 65535)
port = DEFAULT_PORT;
selection.put(HostDatabase.FIELD_HOST_PORT, Integer.toString(port));
}
diff --git a/src/org/connectbot/util/HostDatabase.java b/src/org/connectbot/util/HostDatabase.java
index d195f72..9991190 100644
--- a/src/org/connectbot/util/HostDatabase.java
+++ b/src/org/connectbot/util/HostDatabase.java
@@ -47,7 +47,7 @@ public class HostDatabase extends RobustSQLiteOpenHelper {
public final static String TAG = "ConnectBot.HostDatabase";
public final static String DB_NAME = "hosts";
- public final static int DB_VERSION = 22;
+ public final static int DB_VERSION = 24;
public final static String TABLE_HOSTS = "hosts";
public final static String FIELD_HOST_NICKNAME = "nickname";
@@ -69,6 +69,7 @@ public class HostDatabase extends RobustSQLiteOpenHelper {
public final static String FIELD_HOST_COMPRESSION = "compression";
public final static String FIELD_HOST_ENCODING = "encoding";
public final static String FIELD_HOST_STAYCONNECTED = "stayconnected";
+ public final static String FIELD_HOST_QUICKDISCONNECT = "quickdisconnect";
public final static String TABLE_PORTFORWARDS = "portforwards";
public final static String FIELD_PORTFORWARD_HOSTID = "hostid";
@@ -124,6 +125,9 @@ public class HostDatabase extends RobustSQLiteOpenHelper {
"CREATE INDEX " + TABLE_COLOR_DEFAULTS + FIELD_COLOR_SCHEME + "index ON "
+ TABLE_COLOR_DEFAULTS + " (" + FIELD_COLOR_SCHEME + ");";
+ private static final String WHERE_SCHEME_AND_COLOR = FIELD_COLOR_SCHEME + " = ? AND "
+ + FIELD_COLOR_NUMBER + " = ?";
+
static {
addTableName(TABLE_HOSTS);
addTableName(TABLE_PORTFORWARDS);
@@ -136,9 +140,14 @@ public class HostDatabase extends RobustSQLiteOpenHelper {
public static final Object[] dbLock = new Object[0];
+ /** Used during upgrades from DB version 23 to 24. */
+ private final float displayDensity;
+
public HostDatabase(Context context) {
super(context, DB_NAME, null, DB_VERSION);
+ this.displayDensity = context.getResources().getDisplayMetrics().density;
+
getWritableDatabase().close();
}
@@ -166,7 +175,8 @@ public class HostDatabase extends RobustSQLiteOpenHelper {
+ FIELD_HOST_WANTSESSION + " TEXT DEFAULT '" + Boolean.toString(true) + "', "
+ FIELD_HOST_COMPRESSION + " TEXT DEFAULT '" + Boolean.toString(false) + "', "
+ FIELD_HOST_ENCODING + " TEXT DEFAULT '" + ENCODING_DEFAULT + "', "
- + FIELD_HOST_STAYCONNECTED + " TEXT)");
+ + FIELD_HOST_STAYCONNECTED + " TEXT, "
+ + FIELD_HOST_QUICKDISCONNECT + " TEXT DEFAULT '" + Boolean.toString(false) + "')");
db.execSQL("CREATE TABLE " + TABLE_PORTFORWARDS
+ " (_id INTEGER PRIMARY KEY, "
@@ -256,12 +266,18 @@ public class HostDatabase extends RobustSQLiteOpenHelper {
db.execSQL("DROP TABLE " + TABLE_COLOR_DEFAULTS);
db.execSQL(CREATE_TABLE_COLOR_DEFAULTS);
db.execSQL(CREATE_TABLE_COLOR_DEFAULTS_INDEX);
+ case 22:
+ db.execSQL("ALTER TABLE " + TABLE_HOSTS
+ + " ADD COLUMN " + FIELD_HOST_QUICKDISCONNECT + " TEXT DEFAULT '" + Boolean.toString(false) + "'");
+ case 23:
+ db.execSQL("UPDATE " + TABLE_HOSTS
+ + " SET " + FIELD_HOST_FONTSIZE + " = " + FIELD_HOST_FONTSIZE + " / " + displayDensity);
}
}
/**
* Touch a specific host to update its "last connected" field.
- * @param nickname Nickname field of host to update
+ * @param host host to update
*/
public void touchHost(HostBean host) {
long now = System.currentTimeMillis() / 1000;
@@ -350,8 +366,7 @@ public class HostDatabase extends RobustSQLiteOpenHelper {
}
/**
- * @param hosts
- * @param c
+ * @param c cursor to read from
*/
private List<HostBean> createHostBeans(Cursor c) {
List<HostBean> hosts = new LinkedList<HostBean>();
@@ -373,8 +388,8 @@ public class HostDatabase extends RobustSQLiteOpenHelper {
COL_FONTSIZE = c.getColumnIndexOrThrow(FIELD_HOST_FONTSIZE),
COL_COMPRESSION = c.getColumnIndexOrThrow(FIELD_HOST_COMPRESSION),
COL_ENCODING = c.getColumnIndexOrThrow(FIELD_HOST_ENCODING),
- COL_STAYCONNECTED = c.getColumnIndexOrThrow(FIELD_HOST_STAYCONNECTED);
-
+ COL_STAYCONNECTED = c.getColumnIndexOrThrow(FIELD_HOST_STAYCONNECTED),
+ COL_QUICKDISCONNECT = c.getColumnIndexOrThrow(FIELD_HOST_QUICKDISCONNECT);
while (c.moveToNext()) {
HostBean host = new HostBean();
@@ -397,6 +412,7 @@ public class HostDatabase extends RobustSQLiteOpenHelper {
host.setCompression(Boolean.valueOf(c.getString(COL_COMPRESSION)));
host.setEncoding(c.getString(COL_ENCODING));
host.setStayConnected(Boolean.valueOf(c.getString(COL_STAYCONNECTED)));
+ host.setQuickDisconnect(Boolean.valueOf(c.getString(COL_QUICKDISCONNECT)));
hosts.add(host);
}
@@ -405,8 +421,8 @@ public class HostDatabase extends RobustSQLiteOpenHelper {
}
/**
- * @param c
- * @return
+ * @param c cursor with zero or more hosts
+ * @return the first host from the cursor or {@code null} if none.
*/
private HostBean getFirstHostBean(Cursor c) {
HostBean host = null;
@@ -421,13 +437,8 @@ public class HostDatabase extends RobustSQLiteOpenHelper {
}
/**
- * @param nickname
- * @param protocol
- * @param username
- * @param hostname
- * @param hostname2
- * @param port
- * @return
+ * @param selection parameters describing the desired host
+ * @return host matching selection or {@code null}.
*/
public HostBean findHost(Map<String, String> selection) {
StringBuilder selectionBuilder = new StringBuilder();
@@ -472,8 +483,8 @@ public class HostDatabase extends RobustSQLiteOpenHelper {
}
/**
- * @param hostId
- * @return
+ * @param hostId host id for the host
+ * @return host matching the hostId or {@code null} if none match
*/
public HostBean findHostById(long hostId) {
HostBean host;
@@ -493,10 +504,10 @@ public class HostDatabase extends RobustSQLiteOpenHelper {
/**
* Record the given hostkey into database under this nickname.
- * @param hostname
- * @param port
- * @param hostkeyalgo
- * @param hostkey
+ * @param hostname hostname to match
+ * @param port port to match
+ * @param hostkeyalgo algorithm for host key
+ * @param hostkey the bytes of the host key itself
*/
public void saveKnownHost(String hostname, int port, String hostkeyalgo, byte[] hostkey) {
ContentValues values = new ContentValues();
@@ -581,10 +592,13 @@ public class HostDatabase extends RobustSQLiteOpenHelper {
/**
* Returns a list of all the port forwards associated with a particular host ID.
* @param host the host for which we want the port forward list
- * @return port forwards associated with host ID
+ * @return port forwards associated with host ID or empty list if no match
*/
public List<PortForwardBean> getPortForwardsForHost(HostBean host) {
List<PortForwardBean> portForwards = new LinkedList<PortForwardBean>();
+ if (host == null) {
+ return portForwards;
+ }
synchronized (dbLock) {
SQLiteDatabase db = this.getReadableDatabase();
@@ -674,39 +688,30 @@ public class HostDatabase extends RobustSQLiteOpenHelper {
}
public void setColorForScheme(int scheme, int number, int value) {
- SQLiteDatabase db;
+ final SQLiteDatabase db;
- String schemeWhere;
- schemeWhere = FIELD_COLOR_SCHEME + " = ?";
+ final String[] whereArgs = new String[] { String.valueOf(scheme), String.valueOf(number) };
if (value == Colors.defaults[number]) {
- String[] whereArgs = new String[1];
-
- whereArgs[0] = String.valueOf(number);
-
synchronized (dbLock) {
db = getWritableDatabase();
db.delete(TABLE_COLORS,
- FIELD_COLOR_NUMBER + " = ? AND "
- + schemeWhere,
- new String[] { String.valueOf(number) });
+ WHERE_SCHEME_AND_COLOR, whereArgs);
}
} else {
- ContentValues values = new ContentValues();
- values.put(FIELD_COLOR_NUMBER, number);
+ final ContentValues values = new ContentValues();
values.put(FIELD_COLOR_VALUE, value);
- String[] whereArgs = null;
-
- whereArgs = new String[] { String.valueOf(scheme) };
-
synchronized (dbLock) {
db = getWritableDatabase();
- int rowsAffected = db.update(TABLE_COLORS, values,
- schemeWhere, whereArgs);
+
+ final int rowsAffected = db.update(TABLE_COLORS, values,
+ WHERE_SCHEME_AND_COLOR, whereArgs);
if (rowsAffected == 0) {
+ values.put(FIELD_COLOR_SCHEME, scheme);
+ values.put(FIELD_COLOR_NUMBER, number);
db.insert(TABLE_COLORS, null, values);
}
}
diff --git a/src/org/connectbot/util/PreferenceConstants.java b/src/org/connectbot/util/PreferenceConstants.java
index 9e37017..d3cd832 100644
--- a/src/org/connectbot/util/PreferenceConstants.java
+++ b/src/org/connectbot/util/PreferenceConstants.java
@@ -24,16 +24,12 @@ import android.os.Build;
*
*/
public class PreferenceConstants {
- public static final boolean PRE_ECLAIR = (Integer.parseInt(Build.VERSION.SDK) <= 4);
- public static final boolean PRE_FROYO = PRE_ECLAIR ? true :
- (Integer.parseInt(Build.VERSION.SDK) <= 7);
+ public static final int SDK_INT = Integer.parseInt(Build.VERSION.SDK);
+ public static final boolean PRE_ECLAIR = SDK_INT < 5;
+ public static final boolean PRE_FROYO = SDK_INT < 8;
+ public static final boolean PRE_HONEYCOMB = SDK_INT < 11;
public static final String MEMKEYS = "memkeys";
- public static final String UPDATE = "update";
-
- public static final String UPDATE_DAILY = "Daily";
- public static final String UPDATE_WEEKLY = "Weekly";
- public static final String UPDATE_NEVER = "Never";
public static final String LAST_CHECKED = "lastchecked";
@@ -49,11 +45,13 @@ public class PreferenceConstants {
public static final String ROTATION_AUTOMATIC = "Automatic";
public static final String FULLSCREEN = "fullscreen";
+ public static final String TITLEBARHIDE = "titlebarhide";
public static final String KEYMODE = "keymode";
public static final String KEYMODE_RIGHT = "Use right-side keys";
public static final String KEYMODE_LEFT = "Use left-side keys";
+ public static final String KEYMODE_NONE = "none";
public static final String CAMERA = "camera";
@@ -80,6 +78,14 @@ public class PreferenceConstants {
public static final String CONNECTION_PERSIST = "connPersist";
+ public static final String SHIFT_FKEYS = "shiftfkeys";
+ public static final String CTRL_FKEYS = "ctrlfkeys";
+ public static final String VOLUME_FONT = "volumefont";
+ public static final String STICKY_MODIFIERS = "stickymodifiers";
+ public static final String YES = "yes";
+ public static final String NO = "no";
+ public static final String ALT = "alt";
+
/* Backup identifiers */
public static final String BACKUP_PREF_KEY = "prefs";
}
diff --git a/src/org/connectbot/util/PubkeyDatabase.java b/src/org/connectbot/util/PubkeyDatabase.java
index 30712ce..a8993cb 100644
--- a/src/org/connectbot/util/PubkeyDatabase.java
+++ b/src/org/connectbot/util/PubkeyDatabase.java
@@ -52,7 +52,8 @@ public class PubkeyDatabase extends RobustSQLiteOpenHelper {
public final static String KEY_TYPE_RSA = "RSA",
KEY_TYPE_DSA = "DSA",
- KEY_TYPE_IMPORTED = "IMPORTED";
+ KEY_TYPE_IMPORTED = "IMPORTED",
+ KEY_TYPE_EC = "EC";
private Context context;
diff --git a/src/org/connectbot/util/PubkeyUtils.java b/src/org/connectbot/util/PubkeyUtils.java
index db884d2..e7922bd 100644
--- a/src/org/connectbot/util/PubkeyUtils.java
+++ b/src/org/connectbot/util/PubkeyUtils.java
@@ -6,7 +6,7 @@
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -33,10 +33,14 @@ import java.security.SecureRandom;
import java.security.interfaces.DSAParams;
import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateCrtKey;
-import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.DSAPublicKeySpec;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPublicKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.KeySpec;
@@ -55,13 +59,19 @@ import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import javax.crypto.spec.SecretKeySpec;
+import org.keyczar.jce.EcCore;
+
import android.util.Log;
import com.trilead.ssh2.crypto.Base64;
+import com.trilead.ssh2.crypto.SimpleDERReader;
import com.trilead.ssh2.signature.DSASHA1Verify;
+import com.trilead.ssh2.signature.ECDSASHA2Verify;
import com.trilead.ssh2.signature.RSASHA1Verify;
public class PubkeyUtils {
+ private static final String TAG = "PubkeyUtils";
+
public static final String PKCS8_START = "-----BEGIN PRIVATE KEY-----";
public static final String PKCS8_END = "-----END PRIVATE KEY-----";
@@ -71,6 +81,10 @@ public class PubkeyUtils {
// Number of iterations for password hashing. PKCS#5 recommends 1000
private static final int ITERATIONS = 1000;
+ // Cannot be instantiated
+ private PubkeyUtils() {
+ }
+
public static String formatKey(Key key){
String algo = key.getAlgorithm();
String fmt = key.getFormat();
@@ -79,23 +93,6 @@ public class PubkeyUtils {
", bytes=" + encoded.length + "]";
}
- public static String describeKey(Key key, boolean encrypted) {
- String desc = null;
- if (key instanceof RSAPublicKey) {
- int bits = ((RSAPublicKey)key).getModulus().bitLength();
- desc = "RSA " + String.valueOf(bits) + "-bit";
- } else if (key instanceof DSAPublicKey) {
- desc = "DSA 1024-bit";
- } else {
- desc = "Unknown Key Type";
- }
-
- if (encrypted)
- desc += " (encrypted)";
-
- return desc;
- }
-
public static byte[] sha256(byte[] data) throws NoSuchAlgorithmException {
return MessageDigest.getInstance("SHA-256").digest(data);
}
@@ -123,35 +120,28 @@ public class PubkeyUtils {
return complete;
}
- public static byte[] decrypt(byte[] complete, String secret) throws Exception {
+ public static byte[] decrypt(byte[] saltAndCiphertext, String secret) throws Exception {
try {
byte[] salt = new byte[SALT_SIZE];
- byte[] ciphertext = new byte[complete.length - salt.length];
+ byte[] ciphertext = new byte[saltAndCiphertext.length - salt.length];
- System.arraycopy(complete, 0, salt, 0, salt.length);
- System.arraycopy(complete, salt.length, ciphertext, 0, ciphertext.length);
+ System.arraycopy(saltAndCiphertext, 0, salt, 0, salt.length);
+ System.arraycopy(saltAndCiphertext, salt.length, ciphertext, 0, ciphertext.length);
return Encryptor.decrypt(salt, ITERATIONS, secret, ciphertext);
} catch (Exception e) {
Log.d("decrypt", "Could not decrypt with new method", e);
// We might be using the old encryption method.
- return cipher(Cipher.DECRYPT_MODE, complete, secret.getBytes());
+ return cipher(Cipher.DECRYPT_MODE, saltAndCiphertext, secret.getBytes());
}
}
- public static byte[] getEncodedPublic(PublicKey pk) {
- return new X509EncodedKeySpec(pk.getEncoded()).getEncoded();
- }
-
- public static byte[] getEncodedPrivate(PrivateKey pk) {
- return new PKCS8EncodedKeySpec(pk.getEncoded()).getEncoded();
- }
-
public static byte[] getEncodedPrivate(PrivateKey pk, String secret) throws Exception {
- if (secret.length() > 0)
- return encrypt(getEncodedPrivate(pk), secret);
- else
- return getEncodedPrivate(pk);
+ final byte[] encoded = pk.getEncoded();
+ if (secret == null || secret.length() == 0) {
+ return encoded;
+ }
+ return encrypt(pk.getEncoded(), secret);
}
public static PrivateKey decodePrivate(byte[] encoded, String keyType) throws NoSuchAlgorithmException, InvalidKeySpecException {
@@ -173,73 +163,76 @@ public class PubkeyUtils {
return kf.generatePublic(pubKeySpec);
}
- public static KeyPair recoverKeyPair(byte[] encoded) throws NoSuchAlgorithmException, InvalidKeySpecException {
- KeySpec privKeySpec = new PKCS8EncodedKeySpec(encoded);
- KeySpec pubKeySpec;
-
- PrivateKey priv;
- PublicKey pub;
- KeyFactory kf;
- try {
- kf = KeyFactory.getInstance(PubkeyDatabase.KEY_TYPE_RSA);
- priv = kf.generatePrivate(privKeySpec);
-
- pubKeySpec = new RSAPublicKeySpec(((RSAPrivateCrtKey) priv)
- .getModulus(), ((RSAPrivateCrtKey) priv)
- .getPublicExponent());
+ static String getAlgorithmForOid(String oid) throws NoSuchAlgorithmException {
+ if ("1.2.840.10045.2.1".equals(oid)) {
+ return "EC";
+ } else if ("1.2.840.113549.1.1.1".equals(oid)) {
+ return "RSA";
+ } else if ("1.2.840.10040.4.1".equals(oid)) {
+ return "DSA";
+ } else {
+ throw new NoSuchAlgorithmException("Unknown algorithm OID " + oid);
+ }
+ }
- pub = kf.generatePublic(pubKeySpec);
- } catch (ClassCastException e) {
- kf = KeyFactory.getInstance(PubkeyDatabase.KEY_TYPE_DSA);
- priv = kf.generatePrivate(privKeySpec);
+ static String getOidFromPkcs8Encoded(byte[] encoded) throws NoSuchAlgorithmException {
+ if (encoded == null) {
+ throw new NoSuchAlgorithmException("encoding is null");
+ }
- DSAParams params = ((DSAPrivateKey) priv).getParams();
+ try {
+ SimpleDERReader reader = new SimpleDERReader(encoded);
+ reader.resetInput(reader.readSequenceAsByteArray());
+ reader.readInt();
+ reader.resetInput(reader.readSequenceAsByteArray());
+ return reader.readOid();
+ } catch (IOException e) {
+ Log.w(TAG, "Could not read OID", e);
+ throw new NoSuchAlgorithmException("Could not read key", e);
+ }
+ }
- // Calculate public key Y
- BigInteger y = params.getG().modPow(((DSAPrivateKey) priv).getX(),
- params.getP());
+ public static KeyPair recoverKeyPair(byte[] encoded) throws NoSuchAlgorithmException,
+ InvalidKeySpecException {
+ final String algo = getAlgorithmForOid(getOidFromPkcs8Encoded(encoded));
- pubKeySpec = new DSAPublicKeySpec(y, params.getP(), params.getQ(),
- params.getG());
+ final KeySpec privKeySpec = new PKCS8EncodedKeySpec(encoded);
- pub = kf.generatePublic(pubKeySpec);
- }
+ final KeyFactory kf = KeyFactory.getInstance(algo);
+ final PrivateKey priv = kf.generatePrivate(privKeySpec);
- return new KeyPair(pub, priv);
+ return new KeyPair(recoverPublicKey(kf, priv), priv);
}
- /*
- * Trilead compatibility methods
- */
+ static PublicKey recoverPublicKey(KeyFactory kf, PrivateKey priv)
+ throws NoSuchAlgorithmException, InvalidKeySpecException {
+ if (priv instanceof RSAPrivateCrtKey) {
+ RSAPrivateCrtKey rsaPriv = (RSAPrivateCrtKey) priv;
+ return kf.generatePublic(new RSAPublicKeySpec(rsaPriv.getModulus(), rsaPriv
+ .getPublicExponent()));
+ } else if (priv instanceof DSAPrivateKey) {
+ DSAPrivateKey dsaPriv = (DSAPrivateKey) priv;
+ DSAParams params = dsaPriv.getParams();
- public static Object convertToTrilead(PublicKey pk) {
- if (pk instanceof RSAPublicKey) {
- return new com.trilead.ssh2.signature.RSAPublicKey(
- ((RSAPublicKey) pk).getPublicExponent(),
- ((RSAPublicKey) pk).getModulus());
- } else if (pk instanceof DSAPublicKey) {
- DSAParams dp = ((DSAPublicKey) pk).getParams();
- return new com.trilead.ssh2.signature.DSAPublicKey(
- dp.getP(), dp.getQ(), dp.getG(), ((DSAPublicKey) pk).getY());
- }
+ // Calculate public key Y
+ BigInteger y = params.getG().modPow(dsaPriv.getX(), params.getP());
- throw new IllegalArgumentException("PublicKey is not RSA or DSA format");
- }
+ return kf.generatePublic(new DSAPublicKeySpec(y, params.getP(), params.getQ(), params
+ .getG()));
+ } else if (priv instanceof ECPrivateKey) {
+ ECPrivateKey ecPriv = (ECPrivateKey) priv;
+ ECParameterSpec params = ecPriv.getParams();
- public static Object convertToTrilead(PrivateKey priv, PublicKey pub) {
- if (priv instanceof RSAPrivateKey) {
- return new com.trilead.ssh2.signature.RSAPrivateKey(
- ((RSAPrivateKey) priv).getPrivateExponent(),
- ((RSAPublicKey) pub).getPublicExponent(),
- ((RSAPrivateKey) priv).getModulus());
- } else if (priv instanceof DSAPrivateKey) {
- DSAParams dp = ((DSAPrivateKey) priv).getParams();
- return new com.trilead.ssh2.signature.DSAPrivateKey(
- dp.getP(), dp.getQ(), dp.getG(), ((DSAPublicKey) pub).getY(),
- ((DSAPrivateKey) priv).getX());
- }
+ // Calculate public key Y
+ ECPoint generator = params.getGenerator();
+ BigInteger[] wCoords = EcCore.multiplyPointA(new BigInteger[] { generator.getAffineX(),
+ generator.getAffineY() }, ecPriv.getS(), params);
+ ECPoint w = new ECPoint(wCoords[0], wCoords[1]);
- throw new IllegalArgumentException("Key is not RSA or DSA format");
+ return kf.generatePublic(new ECPublicKeySpec(w, params));
+ } else {
+ throw new NoSuchAlgorithmException("Key type must be RSA, DSA, or EC");
+ }
}
/*
@@ -253,14 +246,17 @@ public class PubkeyUtils {
if (pk instanceof RSAPublicKey) {
String data = "ssh-rsa ";
- data += String.valueOf(Base64.encode(RSASHA1Verify.encodeSSHRSAPublicKey(
- (com.trilead.ssh2.signature.RSAPublicKey)convertToTrilead(pk))));
+ data += String.valueOf(Base64.encode(RSASHA1Verify.encodeSSHRSAPublicKey((RSAPublicKey) pk)));
return data + " " + nickname;
} else if (pk instanceof DSAPublicKey) {
String data = "ssh-dss ";
- data += String.valueOf(Base64.encode(DSASHA1Verify.encodeSSHDSAPublicKey(
- (com.trilead.ssh2.signature.DSAPublicKey)convertToTrilead(pk))));
+ data += String.valueOf(Base64.encode(DSASHA1Verify.encodeSSHDSAPublicKey((DSAPublicKey) pk)));
return data + " " + nickname;
+ } else if (pk instanceof ECPublicKey) {
+ ECPublicKey ecPub = (ECPublicKey) pk;
+ String keyType = ECDSASHA2Verify.getCurveName(ecPub.getParams().getCurve().getField().getFieldSize());
+ String keyData = String.valueOf(Base64.encode(ECDSASHA2Verify.encodeSSHECDSAPublicKey(ecPub)));
+ return ECDSASHA2Verify.ECDSA_SHA2_PREFIX + keyType + " " + keyData + " " + nickname;
}
throw new InvalidKeyException("Unknown key type");
@@ -274,16 +270,18 @@ public class PubkeyUtils {
* @param trileadKey
* @return OpenSSH-encoded pubkey
*/
- public static byte[] extractOpenSSHPublic(Object trileadKey) {
+ public static byte[] extractOpenSSHPublic(KeyPair pair) {
try {
- if (trileadKey instanceof com.trilead.ssh2.signature.RSAPrivateKey)
- return RSASHA1Verify.encodeSSHRSAPublicKey(
- ((com.trilead.ssh2.signature.RSAPrivateKey) trileadKey).getPublicKey());
- else if (trileadKey instanceof com.trilead.ssh2.signature.DSAPrivateKey)
- return DSASHA1Verify.encodeSSHDSAPublicKey(
- ((com.trilead.ssh2.signature.DSAPrivateKey) trileadKey).getPublicKey());
- else
+ PublicKey pubKey = pair.getPublic();
+ if (pubKey instanceof RSAPublicKey) {
+ return RSASHA1Verify.encodeSSHRSAPublicKey((RSAPublicKey) pair.getPublic());
+ } else if (pubKey instanceof DSAPublicKey) {
+ return DSASHA1Verify.encodeSSHDSAPublicKey((DSAPublicKey) pair.getPublic());
+ } else if (pubKey instanceof ECPublicKey) {
+ return ECDSASHA2Verify.encodeSSHECDSAPublicKey((ECPublicKey) pair.getPublic());
+ } else {
return null;
+ }
} catch (IOException e) {
return null;
}
@@ -338,17 +336,17 @@ public class PubkeyUtils {
return sb.toString();
}
- final static private char hexDigit[] = { '0', '1', '2', '3', '4', '5', '6',
+ private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6',
'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
- private static String encodeHex(byte[] bytes) {
- char[] hex = new char[bytes.length * 2];
+ protected static String encodeHex(byte[] bytes) {
+ final char[] hex = new char[bytes.length * 2];
int i = 0;
for (byte b : bytes) {
- hex[i++] = hexDigit[(b >> 4) & 0x0f];
- hex[i++] = hexDigit[b & 0x0f];
+ hex[i++] = HEX_DIGITS[(b >> 4) & 0x0f];
+ hex[i++] = HEX_DIGITS[b & 0x0f];
}
- return new String(hex);
+ return String.valueOf(hex);
}
}
diff --git a/src/org/connectbot/util/UberColorPickerDialog.java b/src/org/connectbot/util/UberColorPickerDialog.java
index e12307e..dc36bcb 100644
--- a/src/org/connectbot/util/UberColorPickerDialog.java
+++ b/src/org/connectbot/util/UberColorPickerDialog.java
@@ -33,6 +33,8 @@
package org.connectbot.util;
+import org.connectbot.R;
+
import android.app.Dialog;
import android.content.Context;
import android.graphics.Bitmap;
@@ -65,8 +67,8 @@ import android.view.View;
* @author Keith Wiley, kwiley@keithwiley.com, http://keithwiley.com
*/
public class UberColorPickerDialog extends Dialog {
- private OnColorChangedListener mListener;
- private int mInitialColor;
+ private final OnColorChangedListener mListener;
+ private final int mInitialColor;
/**
* Callback to the creator of the dialog, informing the creator of a new color and notifying that the dialog is about to dismiss.
@@ -80,7 +82,6 @@ public class UberColorPickerDialog extends Dialog {
* @param context
* @param listener
* @param initialColor
- * @param showTitle If true, a title is shown across the top of the dialog. If false a toast is shown instead.
*/
public UberColorPickerDialog(Context context,
OnColorChangedListener listener,
@@ -109,7 +110,7 @@ public class UberColorPickerDialog extends Dialog {
int screenWidth = dm.widthPixels;
int screenHeight = dm.heightPixels;
- setTitle("Pick a color (try the trackball)");
+ setTitle(getContext().getResources().getString(R.string.title_color_picker));
try {
setContentView(new ColorPickerView(getContext(), l, screenWidth, screenHeight, mInitialColor));
@@ -132,20 +133,13 @@ public class UberColorPickerDialog extends Dialog {
* I highly recommend adding new methods to the end of the list. If you want to try to reorder the list, you're on your own.
*/
private static class ColorPickerView extends View {
- private static int SWATCH_WIDTH = 95;
- private static final int SWATCH_HEIGHT = 60;
-
- private static int PALETTE_POS_X = 0;
- private static int PALETTE_POS_Y = SWATCH_HEIGHT;
- private static final int PALETTE_DIM = SWATCH_WIDTH * 2;
- private static final int PALETTE_RADIUS = PALETTE_DIM / 2;
- private static final int PALETTE_CENTER_X = PALETTE_RADIUS;
- private static final int PALETTE_CENTER_Y = PALETTE_RADIUS;
+ private static int SWATCH_WIDTH_PORTRAIT_DP = 95;
+ private static int SWATCH_WIDTH_LANDSCAPE_DP = 110;
+ private static final int SWATCH_HEIGHT_DP = 60;
- private static final int SLIDER_THICKNESS = 40;
+ private static final int PALETTE_DIM_DP = SWATCH_WIDTH_PORTRAIT_DP * 2;
- private static int VIEW_DIM_X = PALETTE_DIM;
- private static int VIEW_DIM_Y = SWATCH_HEIGHT;
+ private static final int SLIDER_THICKNESS_DP = 40;
//NEW_METHOD_WORK_NEEDED_HERE
private static final int METHOD_HS_V_PALETTE = 0;
@@ -158,7 +152,11 @@ public class UberColorPickerDialog extends Dialog {
private static final int TRACK_HS_PALETTE = 30;
private static final int TRACK_VER_VALUE_SLIDER = 31;
- private static final int TEXT_SIZE = 12;
+ private static final int TEXT_SIZE_DP = 12;
+ private static final int TEXT_SIZE_LABEL_DP = 12;
+
+ private static final int BUTTON_TEXT_MARGIN_DP = 16;
+
private static int[] TEXT_HSV_POS = new int[2];
private static int[] TEXT_RGB_POS = new int[2];
private static int[] TEXT_YUV_POS = new int[2];
@@ -166,6 +164,19 @@ public class UberColorPickerDialog extends Dialog {
private static final float PI = 3.141592653589793f;
+ private final int mSwatchWidthPx;
+ private final int mTextSizePx;
+ private final int mTextSizeLabelPx;
+ private final int mPalettePosX;
+ private final int mPalettePosY;
+ private final int mPaletteDimPx;
+ private final int mPaletteRadiusPx;
+ private final int mSliderThicknessPx;
+ private final int mViewDimXPx;
+ private final int mViewDimYPx;
+ private final int mPaletteCenterPx;
+ private final int mButtonTextMarginPx;
+
private int mMethod = METHOD_HS_V_PALETTE;
private int mTracking = TRACKED_NONE; //What object on screen is currently being tracked for movement
@@ -224,6 +235,10 @@ public class UberColorPickerDialog extends Dialog {
throws Exception {
super(c);
+ DisplayMetrics metrics = c.getResources().getDisplayMetrics();
+ mTextSizePx = (int) (TEXT_SIZE_DP * metrics.density + 0.5f);
+ mTextSizeLabelPx = (int) (TEXT_SIZE_LABEL_DP * metrics.density + 0.5f);
+
//We need to make the dialog focusable to retrieve trackball events.
setFocusable(true);
@@ -235,56 +250,64 @@ public class UberColorPickerDialog extends Dialog {
updateAllFromHSV();
+ mPaletteDimPx = (int) (PALETTE_DIM_DP * metrics.density + 0.5f);
+ mSliderThicknessPx = (int) (SLIDER_THICKNESS_DP * metrics.density + 0.5f);
+ mButtonTextMarginPx = (int) (BUTTON_TEXT_MARGIN_DP * metrics.density + 0.5f);
+
//Setup the layout based on whether this is a portrait or landscape orientation.
- if (width <= height) { //Portrait layout
- SWATCH_WIDTH = (PALETTE_DIM + SLIDER_THICKNESS) / 2;
+ if (width <= height) { //Portrait layout
+ mSwatchWidthPx = (int) (((PALETTE_DIM_DP + SLIDER_THICKNESS_DP) / 2) * metrics.density + 0.5f);
+ final int swatchHeightPx = (int) (SWATCH_HEIGHT_DP * metrics.density + 0.5f);
- PALETTE_POS_X = 0;
- PALETTE_POS_Y = TEXT_SIZE * 4 + SWATCH_HEIGHT;
+ mPalettePosX = 0;
+ mPalettePosY = mTextSizePx * 4 + swatchHeightPx;
//Set more rects, lots of rects
- mOldSwatchRect.set(0, TEXT_SIZE * 4, SWATCH_WIDTH, TEXT_SIZE * 4 + SWATCH_HEIGHT);
- mNewSwatchRect.set(SWATCH_WIDTH, TEXT_SIZE * 4, SWATCH_WIDTH * 2, TEXT_SIZE * 4 + SWATCH_HEIGHT);
- mPaletteRect.set(0, PALETTE_POS_Y, PALETTE_DIM, PALETTE_POS_Y + PALETTE_DIM);
- mVerSliderRect.set(PALETTE_DIM, PALETTE_POS_Y, PALETTE_DIM + SLIDER_THICKNESS, PALETTE_POS_Y + PALETTE_DIM);
+ mOldSwatchRect.set(0, mTextSizePx * 4, mSwatchWidthPx, mTextSizePx * 4 + swatchHeightPx);
+ mNewSwatchRect.set(mSwatchWidthPx, mTextSizePx * 4, mSwatchWidthPx * 2, mTextSizePx * 4 + swatchHeightPx);
+ mPaletteRect.set(0, mPalettePosY, mPaletteDimPx, mPalettePosY + mPaletteDimPx);
+ mVerSliderRect.set(mPaletteDimPx, mPalettePosY, mPaletteDimPx + mSliderThicknessPx, mPalettePosY + mPaletteDimPx);
- TEXT_HSV_POS[0] = 3;
+ TEXT_HSV_POS[0] = (int) (3 * metrics.density + 0.5f);
TEXT_HSV_POS[1] = 0;
- TEXT_RGB_POS[0] = TEXT_HSV_POS[0] + 50;
+ TEXT_RGB_POS[0] = TEXT_HSV_POS[0] + (int) (50 * metrics.density + 0.5f);
TEXT_RGB_POS[1] = TEXT_HSV_POS[1];
- TEXT_YUV_POS[0] = TEXT_HSV_POS[0] + 100;
+ TEXT_YUV_POS[0] = TEXT_HSV_POS[0] + (int) (100 * metrics.density + 0.5f);
TEXT_YUV_POS[1] = TEXT_HSV_POS[1];
- TEXT_HEX_POS[0] = TEXT_HSV_POS[0] + 150;
+ TEXT_HEX_POS[0] = TEXT_HSV_POS[0] + (int) (150 * metrics.density + 0.5f);
TEXT_HEX_POS[1] = TEXT_HSV_POS[1];
- VIEW_DIM_X = PALETTE_DIM + SLIDER_THICKNESS;
- VIEW_DIM_Y = SWATCH_HEIGHT + PALETTE_DIM + TEXT_SIZE * 4;
- }
- else { //Landscape layout
- SWATCH_WIDTH = 110;
+ mViewDimXPx = mPaletteDimPx + mSliderThicknessPx;
+ mViewDimYPx = swatchHeightPx + mPaletteDimPx + mTextSizePx * 4;
+ } else { //Landscape layout
+ mSwatchWidthPx = (int) (SWATCH_WIDTH_LANDSCAPE_DP * metrics.density + 0.5f);
+ final int swatchHeightPx = (int) (SWATCH_HEIGHT_DP * metrics.density + 0.5f);
- PALETTE_POS_X = SWATCH_WIDTH;
- PALETTE_POS_Y = 0;
+ mPalettePosX = mSwatchWidthPx;
+ mPalettePosY = 0;
//Set more rects, lots of rects
- mOldSwatchRect.set(0, TEXT_SIZE * 7, SWATCH_WIDTH, TEXT_SIZE * 7 + SWATCH_HEIGHT);
- mNewSwatchRect.set(0, TEXT_SIZE * 7 + SWATCH_HEIGHT, SWATCH_WIDTH, TEXT_SIZE * 7 + SWATCH_HEIGHT * 2);
- mPaletteRect.set(SWATCH_WIDTH, PALETTE_POS_Y, SWATCH_WIDTH + PALETTE_DIM, PALETTE_POS_Y + PALETTE_DIM);
- mVerSliderRect.set(SWATCH_WIDTH + PALETTE_DIM, PALETTE_POS_Y, SWATCH_WIDTH + PALETTE_DIM + SLIDER_THICKNESS, PALETTE_POS_Y + PALETTE_DIM);
+ mOldSwatchRect.set(0, mTextSizePx * 7, mSwatchWidthPx, mTextSizePx * 7 + swatchHeightPx);
+ mNewSwatchRect.set(0, mTextSizePx * 7 + mSliderThicknessPx, mSwatchWidthPx, mTextSizePx * 7 + swatchHeightPx * 2);
+ mPaletteRect.set(mSwatchWidthPx, mPalettePosY, mSwatchWidthPx + mPaletteDimPx, mPalettePosY + mPaletteDimPx);
+ mVerSliderRect.set(mSwatchWidthPx + mPaletteDimPx, mPalettePosY, mSwatchWidthPx + mPaletteDimPx + mSliderThicknessPx, mPalettePosY + mPaletteDimPx);
- TEXT_HSV_POS[0] = 3;
+ TEXT_HSV_POS[0] = (int) (3 * metrics.density + 0.5f);
TEXT_HSV_POS[1] = 0;
TEXT_RGB_POS[0] = TEXT_HSV_POS[0];
- TEXT_RGB_POS[1] = (int)(TEXT_HSV_POS[1] + TEXT_SIZE * 3.5);
- TEXT_YUV_POS[0] = TEXT_HSV_POS[0] + 50;
- TEXT_YUV_POS[1] = (int)(TEXT_HSV_POS[1] + TEXT_SIZE * 3.5);
- TEXT_HEX_POS[0] = TEXT_HSV_POS[0] + 50;
+ TEXT_RGB_POS[1] = (int) (TEXT_HSV_POS[1] + mTextSizePx * 3.5);
+ TEXT_YUV_POS[0] = TEXT_HSV_POS[0] + (int) (50 * metrics.density + 0.5f);
+ TEXT_YUV_POS[1] = (int) (TEXT_HSV_POS[1] + mTextSizePx * 3.5);
+ TEXT_HEX_POS[0] = TEXT_HSV_POS[0] + (int) (50 * metrics.density + 0.5f);
TEXT_HEX_POS[1] = TEXT_HSV_POS[1];
- VIEW_DIM_X = PALETTE_POS_X + PALETTE_DIM + SLIDER_THICKNESS;
- VIEW_DIM_Y = Math.max(mNewSwatchRect.bottom, PALETTE_DIM);
+ mViewDimXPx = mPalettePosX + mPaletteDimPx + mSliderThicknessPx;
+ mViewDimYPx = Math.max(mNewSwatchRect.bottom, mPaletteDimPx);
}
+ mPaletteCenterPx = mPaletteDimPx / 2;
+ mPaletteRadiusPx = mPaletteDimPx / 2;
+
//Rainbows make everybody happy!
mSpectrumColorsRev = new int[] {
0xFFFF0000, 0xFFFF00FF, 0xFF0000FF, 0xFF00FFFF,
@@ -305,18 +328,18 @@ public class UberColorPickerDialog extends Dialog {
mSwatchNew.setColor(Color.HSVToColor(mHSV));
Shader shaderA = new SweepGradient(0, 0, mSpectrumColorsRev, null);
- Shader shaderB = new RadialGradient(0, 0, PALETTE_CENTER_X, 0xFFFFFFFF, 0xFF000000, Shader.TileMode.CLAMP);
+ Shader shaderB = new RadialGradient(0, 0, mPaletteCenterPx, 0xFFFFFFFF, 0xFF000000, Shader.TileMode.CLAMP);
Shader shader = new ComposeShader(shaderA, shaderB, PorterDuff.Mode.SCREEN);
mOvalHueSat = new Paint(Paint.ANTI_ALIAS_FLAG);
mOvalHueSat.setShader(shader);
mOvalHueSat.setStyle(Paint.Style.FILL);
mOvalHueSat.setDither(true);
- mVerSliderBM = Bitmap.createBitmap(SLIDER_THICKNESS, PALETTE_DIM, Bitmap.Config.RGB_565);
+ mVerSliderBM = Bitmap.createBitmap(mSliderThicknessPx, mPaletteDimPx, Bitmap.Config.RGB_565);
mVerSliderCv = new Canvas(mVerSliderBM);
for (int i = 0; i < 3; i++) {
- mHorSlidersBM[i] = Bitmap.createBitmap(PALETTE_DIM, SLIDER_THICKNESS, Bitmap.Config.RGB_565);
+ mHorSlidersBM[i] = Bitmap.createBitmap(mPaletteDimPx, mSliderThicknessPx, Bitmap.Config.RGB_565);
mHorSlidersCv[i] = new Canvas(mHorSlidersBM[i]);
}
@@ -332,7 +355,7 @@ public class UberColorPickerDialog extends Dialog {
//Add Paints to represent the icon for the new method
shaderA = new SweepGradient(0, 0, mSpectrumColorsRev, null);
- shaderB = new RadialGradient(0, 0, PALETTE_DIM / 2, 0xFFFFFFFF, 0xFF000000, Shader.TileMode.CLAMP);
+ shaderB = new RadialGradient(0, 0, mPaletteDimPx / 2, 0xFFFFFFFF, 0xFF000000, Shader.TileMode.CLAMP);
shader = new ComposeShader(shaderA, shaderB, PorterDuff.Mode.SCREEN);
mOvalHueSatSmall = new Paint(Paint.ANTI_ALIAS_FLAG);
mOvalHueSatSmall.setShader(shader);
@@ -345,7 +368,7 @@ public class UberColorPickerDialog extends Dialog {
//Make a basic text Paint.
mText = new Paint(Paint.ANTI_ALIAS_FLAG);
- mText.setTextSize(TEXT_SIZE);
+ mText.setTextSize(mTextSizePx);
mText.setColor(Color.WHITE);
//Kickstart
@@ -375,7 +398,7 @@ public class UberColorPickerDialog extends Dialog {
private void drawSwatches(Canvas canvas) {
float[] hsv = new float[3];
- mText.setTextSize(16);
+ mText.setTextSize(mTextSizePx);
//Draw the original swatch
canvas.drawRect(mOldSwatchRect, mSwatchOld);
@@ -384,17 +407,17 @@ public class UberColorPickerDialog extends Dialog {
// hsv[1] = 0;
if (hsv[2] > .5)
mText.setColor(Color.BLACK);
- canvas.drawText("Revert", mOldSwatchRect.left + SWATCH_WIDTH / 2 - mText.measureText("Revert") / 2, mOldSwatchRect.top + 16, mText);
+ canvas.drawText("Revert", mOldSwatchRect.left + mSwatchWidthPx / 2 - mText.measureText("Revert") / 2, mOldSwatchRect.top + mButtonTextMarginPx, mText);
mText.setColor(Color.WHITE);
//Draw the new swatch
canvas.drawRect(mNewSwatchRect, mSwatchNew);
if (mHSV[2] > .5)
mText.setColor(Color.BLACK);
- canvas.drawText("Accept", mNewSwatchRect.left + SWATCH_WIDTH / 2 - mText.measureText("Accept") / 2, mNewSwatchRect.top + 16, mText);
+ canvas.drawText("Accept", mNewSwatchRect.left + mSwatchWidthPx / 2 - mText.measureText("Accept") / 2, mNewSwatchRect.top + mButtonTextMarginPx, mText);
mText.setColor(Color.WHITE);
- mText.setTextSize(TEXT_SIZE);
+ mText.setTextSize(mTextSizePx);
}
/**
@@ -403,25 +426,25 @@ public class UberColorPickerDialog extends Dialog {
*/
private void writeColorParams(Canvas canvas) {
if (mHSVenabled) {
- canvas.drawText("H: " + Integer.toString((int)(mHSV[0] / 360.0f * 255)), TEXT_HSV_POS[0], TEXT_HSV_POS[1] + TEXT_SIZE, mText);
- canvas.drawText("S: " + Integer.toString((int)(mHSV[1] * 255)), TEXT_HSV_POS[0], TEXT_HSV_POS[1] + TEXT_SIZE * 2, mText);
- canvas.drawText("V: " + Integer.toString((int)(mHSV[2] * 255)), TEXT_HSV_POS[0], TEXT_HSV_POS[1] + TEXT_SIZE * 3, mText);
+ canvas.drawText("H: " + Integer.toString((int) (mHSV[0] / 360.0f * 255)), TEXT_HSV_POS[0], TEXT_HSV_POS[1] + mTextSizePx, mText);
+ canvas.drawText("S: " + Integer.toString((int) (mHSV[1] * 255)), TEXT_HSV_POS[0], TEXT_HSV_POS[1] + mTextSizePx * 2, mText);
+ canvas.drawText("V: " + Integer.toString((int) (mHSV[2] * 255)), TEXT_HSV_POS[0], TEXT_HSV_POS[1] + mTextSizePx * 3, mText);
}
if (mRGBenabled) {
- canvas.drawText("R: " + mRGB[0], TEXT_RGB_POS[0], TEXT_RGB_POS[1] + TEXT_SIZE, mText);
- canvas.drawText("G: " + mRGB[1], TEXT_RGB_POS[0], TEXT_RGB_POS[1] + TEXT_SIZE * 2, mText);
- canvas.drawText("B: " + mRGB[2], TEXT_RGB_POS[0], TEXT_RGB_POS[1] + TEXT_SIZE * 3, mText);
+ canvas.drawText("R: " + mRGB[0], TEXT_RGB_POS[0], TEXT_RGB_POS[1] + mTextSizePx, mText);
+ canvas.drawText("G: " + mRGB[1], TEXT_RGB_POS[0], TEXT_RGB_POS[1] + mTextSizePx * 2, mText);
+ canvas.drawText("B: " + mRGB[2], TEXT_RGB_POS[0], TEXT_RGB_POS[1] + mTextSizePx * 3, mText);
}
if (mYUVenabled) {
- canvas.drawText("Y: " + Integer.toString((int)(mYUV[0] * 255)), TEXT_YUV_POS[0], TEXT_YUV_POS[1] + TEXT_SIZE, mText);
- canvas.drawText("U: " + Integer.toString((int)((mYUV[1] + .5f) * 255)), TEXT_YUV_POS[0], TEXT_YUV_POS[1] + TEXT_SIZE * 2, mText);
- canvas.drawText("V: " + Integer.toString((int)((mYUV[2] + .5f) * 255)), TEXT_YUV_POS[0], TEXT_YUV_POS[1] + TEXT_SIZE * 3, mText);
+ canvas.drawText("Y: " + Integer.toString((int) (mYUV[0] * 255)), TEXT_YUV_POS[0], TEXT_YUV_POS[1] + mTextSizePx, mText);
+ canvas.drawText("U: " + Integer.toString((int) ((mYUV[1] + .5f) * 255)), TEXT_YUV_POS[0], TEXT_YUV_POS[1] + mTextSizePx * 2, mText);
+ canvas.drawText("V: " + Integer.toString((int) ((mYUV[2] + .5f) * 255)), TEXT_YUV_POS[0], TEXT_YUV_POS[1] + mTextSizePx * 3, mText);
}
if (mHexenabled)
- canvas.drawText("#" + mHexStr, TEXT_HEX_POS[0], TEXT_HEX_POS[1] + TEXT_SIZE, mText);
+ canvas.drawText("#" + mHexStr, TEXT_HEX_POS[0], TEXT_HEX_POS[1] + mTextSizePx, mText);
}
/**
@@ -444,9 +467,9 @@ public class UberColorPickerDialog extends Dialog {
*/
private void markVerSlider(Canvas canvas, int markerPos) {
mPosMarker.setColor(Color.BLACK);
- canvas.drawRect(new Rect(0, markerPos - 2, SLIDER_THICKNESS, markerPos + 3), mPosMarker);
+ canvas.drawRect(new Rect(0, markerPos - 2, mSliderThicknessPx, markerPos + 3), mPosMarker);
mPosMarker.setColor(Color.WHITE);
- canvas.drawRect(new Rect(0, markerPos, SLIDER_THICKNESS, markerPos + 1), mPosMarker);
+ canvas.drawRect(new Rect(0, markerPos, mSliderThicknessPx, markerPos + 1), mPosMarker);
}
/**
@@ -455,9 +478,9 @@ public class UberColorPickerDialog extends Dialog {
*/
private void hilightFocusedVerSlider(Canvas canvas) {
mPosMarker.setColor(Color.WHITE);
- canvas.drawRect(new Rect(0, 0, SLIDER_THICKNESS, PALETTE_DIM), mPosMarker);
+ canvas.drawRect(new Rect(0, 0, mSliderThicknessPx, mPaletteDimPx), mPosMarker);
mPosMarker.setColor(Color.BLACK);
- canvas.drawRect(new Rect(2, 2, SLIDER_THICKNESS - 2, PALETTE_DIM - 2), mPosMarker);
+ canvas.drawRect(new Rect(2, 2, mSliderThicknessPx - 2, mPaletteDimPx - 2), mPosMarker);
}
/**
@@ -466,9 +489,9 @@ public class UberColorPickerDialog extends Dialog {
*/
private void hilightFocusedOvalPalette(Canvas canvas) {
mPosMarker.setColor(Color.WHITE);
- canvas.drawOval(new RectF(-PALETTE_RADIUS, -PALETTE_RADIUS, PALETTE_RADIUS, PALETTE_RADIUS), mPosMarker);
+ canvas.drawOval(new RectF(-mPaletteRadiusPx, -mPaletteRadiusPx, mPaletteRadiusPx, mPaletteRadiusPx), mPosMarker);
mPosMarker.setColor(Color.BLACK);
- canvas.drawOval(new RectF(-PALETTE_RADIUS + 2, -PALETTE_RADIUS + 2, PALETTE_RADIUS - 2, PALETTE_RADIUS - 2), mPosMarker);
+ canvas.drawOval(new RectF(-mPaletteRadiusPx + 2, -mPaletteRadiusPx + 2, mPaletteRadiusPx - 2, mPaletteRadiusPx - 2), mPosMarker);
}
//NEW_METHOD_WORK_NEEDED_HERE
@@ -480,19 +503,19 @@ public class UberColorPickerDialog extends Dialog {
private void drawHSV1Palette(Canvas canvas) {
canvas.save();
- canvas.translate(PALETTE_POS_X, PALETTE_POS_Y);
+ canvas.translate(mPalettePosX, mPalettePosY);
//Draw the 2D palette
- canvas.translate(PALETTE_CENTER_X, PALETTE_CENTER_Y);
- canvas.drawOval(new RectF(-PALETTE_RADIUS, -PALETTE_RADIUS, PALETTE_RADIUS, PALETTE_RADIUS), mOvalHueSat);
- canvas.drawOval(new RectF(-PALETTE_RADIUS, -PALETTE_RADIUS, PALETTE_RADIUS, PALETTE_RADIUS), mValDimmer);
+ canvas.translate(mPaletteCenterPx, mPaletteCenterPx);
+ canvas.drawOval(new RectF(-mPaletteRadiusPx, -mPaletteRadiusPx, mPaletteRadiusPx, mPaletteRadiusPx), mOvalHueSat);
+ canvas.drawOval(new RectF(-mPaletteRadiusPx, -mPaletteRadiusPx, mPaletteRadiusPx, mPaletteRadiusPx), mValDimmer);
if (mFocusedControl == 0)
hilightFocusedOvalPalette(canvas);
mark2DPalette(canvas, mCoord[0], mCoord[1]);
- canvas.translate(-PALETTE_CENTER_X, -PALETTE_CENTER_Y);
+ canvas.translate(-mPaletteCenterPx, -mPaletteCenterPx);
//Draw the 1D slider
- canvas.translate(PALETTE_DIM, 0);
+ canvas.translate(mPaletteDimPx, 0);
canvas.drawBitmap(mVerSliderBM, 0, 0, null);
if (mFocusedControl == 1)
hilightFocusedVerSlider(canvas);
@@ -520,12 +543,12 @@ public class UberColorPickerDialog extends Dialog {
setOvalValDimmer();
setVerValSlider();
- float angle = 2*PI - mHSV[0] / (180 / 3.1415927f);
- float radius = mHSV[1] * PALETTE_RADIUS;
- mCoord[0] = (int)(Math.cos(angle) * radius);
- mCoord[1] = (int)(Math.sin(angle) * radius);
+ float angle = 2 * PI - mHSV[0] / (180 / 3.1415927f);
+ float radius = mHSV[1] * mPaletteRadiusPx;
+ mCoord[0] = (int) (Math.cos(angle) * radius);
+ mCoord[1] = (int) (Math.sin(angle) * radius);
- mCoord[2] = PALETTE_DIM - (int)(mHSV[2] * PALETTE_DIM);
+ mCoord[2] = mPaletteDimPx - (int) (mHSV[2] * mPaletteDimPx);
}
//NEW_METHOD_WORK_NEEDED_HERE
@@ -558,7 +581,7 @@ public class UberColorPickerDialog extends Dialog {
GradientDrawable gradDraw = new GradientDrawable(Orientation.TOP_BOTTOM, colors);
gradDraw.setDither(true);
gradDraw.setLevel(10000);
- gradDraw.setBounds(0, 0, SLIDER_THICKNESS, PALETTE_DIM);
+ gradDraw.setBounds(0, 0, mSliderThicknessPx, mPaletteDimPx);
gradDraw.draw(mVerSliderCv);
}
@@ -567,7 +590,7 @@ public class UberColorPickerDialog extends Dialog {
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- setMeasuredDimension(VIEW_DIM_X, VIEW_DIM_Y);
+ setMeasuredDimension(mViewDimXPx, mViewDimYPx);
}
/**
@@ -744,18 +767,18 @@ public class UberColorPickerDialog extends Dialog {
mCoord[0] += x2;
mCoord[1] += y2;
- if (mCoord[0] < -PALETTE_RADIUS)
- mCoord[0] = -PALETTE_RADIUS;
- else if (mCoord[0] > PALETTE_RADIUS)
- mCoord[0] = PALETTE_RADIUS;
- if (mCoord[1] < -PALETTE_RADIUS)
- mCoord[1] = -PALETTE_RADIUS;
- else if (mCoord[1] > PALETTE_RADIUS)
- mCoord[1] = PALETTE_RADIUS;
+ if (mCoord[0] < -mPaletteRadiusPx)
+ mCoord[0] = -mPaletteRadiusPx;
+ else if (mCoord[0] > mPaletteRadiusPx)
+ mCoord[0] = mPaletteRadiusPx;
+ if (mCoord[1] < -mPaletteRadiusPx)
+ mCoord[1] = -mPaletteRadiusPx;
+ else if (mCoord[1] > mPaletteRadiusPx)
+ mCoord[1] = mPaletteRadiusPx;
- float radius = (float)java.lang.Math.sqrt(mCoord[0] * mCoord[0] + mCoord[1] * mCoord[1]);
- if (radius > PALETTE_RADIUS)
- radius = PALETTE_RADIUS;
+ float radius = (float) java.lang.Math.sqrt(mCoord[0] * mCoord[0] + mCoord[1] * mCoord[1]);
+ if (radius > mPaletteRadiusPx)
+ radius = mPaletteRadiusPx;
float angle = (float)java.lang.Math.atan2(mCoord[1], mCoord[0]);
// need to turn angle [-PI ... PI] into unit [0....1]
@@ -771,7 +794,7 @@ public class UberColorPickerDialog extends Dialog {
float[] hsv = new float[3];
Color.colorToHSV(c, hsv);
mHSV[0] = hsv[0];
- mHSV[1] = radius / PALETTE_RADIUS;
+ mHSV[1] = radius / mPaletteRadiusPx;
updateAllFromHSV();
mSwatchNew.setColor(Color.HSVToColor(mHSV));
@@ -797,7 +820,7 @@ public class UberColorPickerDialog extends Dialog {
mHSV[2] += (increase ? jump : -jump) / 256.0f;
mHSV[2] = pinToUnit(mHSV[2]);
updateAllFromHSV();
- mCoord[2] = PALETTE_DIM - (int)(mHSV[2] * PALETTE_DIM);
+ mCoord[2] = mPaletteDimPx - (int) (mHSV[2] * mPaletteDimPx);
mSwatchNew.setColor(Color.HSVToColor(mHSV));
@@ -872,11 +895,11 @@ public class UberColorPickerDialog extends Dialog {
float y = event.getY();
//Generate coordinates which are palette=local with the origin at the upper left of the main 2D palette
- int y2 = (int)(pin(round(y - PALETTE_POS_Y), PALETTE_DIM));
+ int y2 = (int) (pin(round(y - mPalettePosY), mPaletteDimPx));
//Generate coordinates which are palette-local with the origin at the center of the main 2D palette
- float circlePinnedX = x - PALETTE_POS_X - PALETTE_CENTER_X;
- float circlePinnedY = y - PALETTE_POS_Y - PALETTE_CENTER_Y;
+ float circlePinnedX = x - mPalettePosX - mPaletteCenterPx;
+ float circlePinnedY = y - mPalettePosY - mPaletteCenterPx;
//Is the event in a swatch?
boolean inSwatchOld = ptInRect(round(x), round(y), mOldSwatchRect);
@@ -886,11 +909,11 @@ public class UberColorPickerDialog extends Dialog {
float radius = (float)java.lang.Math.sqrt(circlePinnedX * circlePinnedX + circlePinnedY * circlePinnedY);
//Is the event in a circle-pinned 2D palette?
- boolean inOvalPalette = radius <= PALETTE_RADIUS;
+ boolean inOvalPalette = radius <= mPaletteRadiusPx;
//Pin the radius
- if (radius > PALETTE_RADIUS)
- radius = PALETTE_RADIUS;
+ if (radius > mPaletteRadiusPx)
+ radius = mPaletteRadiusPx;
//Is the event in a vertical slider to the right of the main 2D palette
boolean inVerSlider = ptInRect(round(x), round(y), mVerSliderRect);
@@ -935,7 +958,7 @@ public class UberColorPickerDialog extends Dialog {
float[] hsv = new float[3];
Color.colorToHSV(c, hsv);
mHSV[0] = hsv[0];
- mHSV[1] = radius / PALETTE_RADIUS;
+ mHSV[1] = radius / mPaletteRadiusPx;
updateAllFromHSV();
mSwatchNew.setColor(Color.HSVToColor(mHSV));
@@ -946,7 +969,7 @@ public class UberColorPickerDialog extends Dialog {
else if (mTracking == TRACK_VER_VALUE_SLIDER) {
if (mCoord[2] != y2) {
mCoord[2] = y2;
- float value = 1.0f - (float)y2 / (float)PALETTE_DIM;
+ float value = 1.0f - (float) y2 / (float) mPaletteDimPx;
mHSV[2] = value;
updateAllFromHSV();
diff --git a/src/org/connectbot/util/UpdateHelper.java b/src/org/connectbot/util/UpdateHelper.java
deleted file mode 100644
index a43ba57..0000000
--- a/src/org/connectbot/util/UpdateHelper.java
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * ConnectBot: simple, powerful, open-source SSH client for Android
- * Copyright 2007 Kenny Root, Jeffrey Sharkey
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.connectbot.util;
-
-import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
-import java.net.URL;
-import java.net.URLConnection;
-import java.util.Locale;
-
-import org.connectbot.R;
-import org.json.JSONObject;
-
-import android.app.AlertDialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.Editor;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Message;
-import android.preference.PreferenceManager;
-import android.util.Log;
-
-/**
- * Helper class that checks for updates to this application. On construction, it
- * spawns a background thread that checks for any app updates. If available,
- * shows a dialog to the user, prompting them to visit Market for the upgrade.
- *
- * <b>Be sure to change the UPDATE_URL field before using this class.</b> Then
- * place a text file at that URL containing JSON data in the format:
- *
- * <code>{"versionCode": 110, "features": "Brand new interface with over
- * 9,000 improvements.", "target": "search?q=searchterms"}</code>
- *
- * Which should contain information about your newest version. The
- * <code>target</code> field is used to build an Intent that launches Market on
- * the device, simply be prefixing it with <code>market://</code>. If you know
- * your exact Market ID, you could use the value
- * <code>details?id=yourexactmarketid</code>
- *
- * If you're looking for an advanced version-checking system that offers more
- * customization, check out Veecheck: http://www.tomgibara.com/android/veecheck/
- *
- * @author jsharkey
- */
-public final class UpdateHelper implements Runnable {
-
- public final static String TAG = "ConnectBot.UpdateHelper";
- public final static String UPDATE_URL = "http://connectbot.org/version";
-
- protected Context context;
-
- private String packageName, versionName;
- protected int versionCode;
-
- private String userAgent;
-
- /**
- * Constructor will automatically spawn thread to check for updates.
- * Recommended usage is <code>new UpdateHelper(this);</code> in the first
- * onCreate() of your app.
- */
- public UpdateHelper(Context context) {
- this.context = context;
-
- try {
- // read current version information about this package
- PackageManager manager = context.getPackageManager();
- PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0);
- this.packageName = info.packageName;
- this.versionCode = info.versionCode;
- this.versionName = info.versionName;
-
- } catch(Exception e) {
- Log.e(TAG, "Couldn't find package information in PackageManager", e);
- return;
- }
-
- // decide if we really need to check for update
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
-
- String frequency;
- try {
- frequency = prefs.getString(PreferenceConstants.UPDATE, PreferenceConstants.UPDATE_DAILY);
- } catch (ClassCastException cce) {
- // Hm, somehow we got a long in there in the previous upgrades.
- frequency = PreferenceConstants.UPDATE_DAILY;
- Editor editor = prefs.edit();
- editor.putString(PreferenceConstants.UPDATE, frequency);
- editor.commit();
- }
- long lastChecked = prefs.getLong(PreferenceConstants.LAST_CHECKED, 0);
- long now = (System.currentTimeMillis() / 1000);
- long passed = now - lastChecked;
-
- boolean shouldCheck = false;
- if (PreferenceConstants.UPDATE_DAILY.equals(frequency)) {
- shouldCheck = (passed > 60 * 60 * 24);
- } else if (PreferenceConstants.UPDATE_WEEKLY.equals(frequency)) {
- shouldCheck = (passed > 60 * 60 * 24 * 7);
- }
-
- // place version information in user-agent string to be used later
- userAgent = String.format("%s/%s (%d, freq=%s, lang=%s)",
- packageName, versionName, versionCode, frequency,
- Locale.getDefault().getLanguage());
-
- if(shouldCheck) {
- // spawn thread to check for update
- // Note that this class should be marked final because a thread is started in the constructor.
- Thread updateThread = new Thread(this);
- updateThread.setName("UpdateHelper");
- updateThread.start();
-
- // update our last-checked time
- Editor editor = prefs.edit();
- editor.putLong(PreferenceConstants.LAST_CHECKED, now);
- editor.commit();
-
- }
-
- }
-
- public void run() {
- try {
- // fetch and parse the version update information as json
- // pass information off to handler to create
- JSONObject json = new JSONObject(UpdateHelper.getUrl(UPDATE_URL, userAgent));
- Message.obtain(versionHandler, -1, json).sendToTarget();
-
- } catch(Exception e) {
- Log.e(TAG, "Problem while fetching/parsing update response", e);
-
- }
- }
-
-
- /**
- * Handler that will parse the JSON response and show dialog to user if an
- * update is available.
- */
- private Handler versionHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
-
- // make sure we are being passed a real json object
- if(!(msg.obj instanceof JSONObject)) return;
- JSONObject json = (JSONObject)msg.obj;
-
- // pull out version and target information from response
- final int versionCode = json.optInt("versionCode");
- final String features = json.optString("features");
- final String target = "market://" + json.optString("target");
-
- // skip if we're already good enough
- if(versionCode <= UpdateHelper.this.versionCode) return;
-
- // build dialog to prompt user about updating
- new AlertDialog.Builder(context)
- .setTitle(R.string.upgrade)
- .setMessage(features)
- .setPositiveButton(R.string.upgrade_pos, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(target));
- context.startActivity(intent);
- }
- })
- .setNegativeButton(R.string.upgrade_neg, null).create().show();
-
- }
-
-
- };
-
- /**
- * Read contents of a URL and return as a String. Handles any server
- * downtime with a 6-second timeout.
- */
- private static String getUrl(String tryUrl, String userAgent) throws Exception {
-
- URL url = new URL(tryUrl);
- URLConnection connection = url.openConnection();
- connection.setConnectTimeout(6000);
- connection.setReadTimeout(6000);
- connection.setRequestProperty("User-Agent", userAgent);
- connection.connect();
-
- InputStream is = connection.getInputStream();
- ByteArrayOutputStream os = new ByteArrayOutputStream();
-
- int bytesRead;
- byte[] buffer = new byte[1024];
- while ((bytesRead = is.read(buffer)) != -1) {
- os.write(buffer, 0, bytesRead);
- }
-
- os.flush();
- os.close();
- is.close();
-
- return new String(os.toByteArray());
-
- }
-
-
-}
diff --git a/src/org/keyczar/jce/EcCore.java b/src/org/keyczar/jce/EcCore.java
new file mode 100644
index 0000000..681d5db
--- /dev/null
+++ b/src/org/keyczar/jce/EcCore.java
@@ -0,0 +1,679 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keyczar.jce;
+
+import java.math.BigInteger;
+import java.security.spec.ECFieldFp;
+import java.security.spec.ECParameterSpec;
+
+/**
+ * This class implements the basic EC operations such as point addition and
+ * doubling and point multiplication. Only NSA Suite B / NIST curves are
+ * supported.
+ *
+ * Todo:
+ * - Add (more) comments - Performance optimizations - Cleanup ASN.1 code,
+ * possibly replace with own impl - ...
+ *
+ * References:
+ *
+ * [1] Software Implementation of the NIST Elliptic Curves Over Prime Fields, M.
+ * Brown et al. [2] Efficient elliptic curve exponentiation using mixed
+ * coordinates, H. Cohen et al. [3] SEC 1: Elliptic Curve Cryptography. [4]
+ * Guide to Elliptic Curve Cryptography, D. Hankerson et al., Springer.
+ *
+ * @author martclau@gmail.com
+ *
+ */
+// BEGIN connectbot-changed
+public final class EcCore {
+// END connectbot-changed
+// BEGIN connectbot-removed
+// private static final long serialVersionUID = -1376116429660095993L;
+//
+// private static final String INFO = "Google Keyczar (EC key/parameter generation; EC signing)";
+//
+// public static final String NAME = "GooKey";
+//
+// @SuppressWarnings("unchecked")
+// public EcCore() {
+// super(NAME, 0.1, INFO);
+// AccessController.doPrivileged(new PrivilegedAction<Object>() {
+// @Override
+// public Object run() {
+// put("Signature.SHA1withECDSA", "org.keyczar.jce.EcSignatureImpl$SHA1");
+// put("Alg.Alias.Signature.ECDSA", "SHA1withDSA");
+// put("Signature.SHA256withECDSA",
+// "org.keyczar.jce.EcSignatureImpl$SHA256");
+// put("Signature.SHA384withECDSA",
+// "org.keyczar.jce.EcSignatureImpl$SHA384");
+// put("Signature.SHA512withECDSA",
+// "org.keyczar.jce.EcSignatureImpl$SHA512");
+// put("KeyPairGenerator.EC", "org.keyczar.jce.EcKeyPairGeneratorImpl");
+// put("KeyFactory.EC", "org.keyczar.jce.EcKeyFactoryImpl");
+// put("Signature.SHA1withECDSA KeySize", "521");
+// put("Signature.SHA1withECDSA ImplementedIn", "Software");
+// put("Signature.SHA256withECDSA KeySize", "521");
+// put("Signature.SHA256withECDSA ImplementedIn", "Software");
+// put("Signature.SHA384withECDSA KeySize", "521");
+// put("Signature.SHA384withECDSA ImplementedIn", "Software");
+// put("Signature.SHA512withECDSA KeySize", "521");
+// put("Signature.SHA512withECDSA ImplementedIn", "Software");
+// put("KeyPairGenerator.EC ImplementedIn", "Software");
+// put("KeyFactory.EC ImplementedIn", "Software");
+// return null;
+// }
+// });
+// }
+//
+// private static final ECParameterSpec P192 = new ECParameterSpec(
+// new EllipticCurve(
+// new ECFieldFp(new BigInteger(
+// "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF", 16)),
+// new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC", 16),
+// new BigInteger("64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1", 16)),
+// new ECPoint(
+// new BigInteger("188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012", 16),
+// new BigInteger("07192B95FFC8DA78631011ED6B24CDD573F977A11E794811", 16)),
+// new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831", 16), 1);
+//
+// private static final ECParameterSpec P224 = new ECParameterSpec(
+// new EllipticCurve(new ECFieldFp(new BigInteger(
+// "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001", 16)),
+// new BigInteger(
+// "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE", 16),
+// new BigInteger(
+// "B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4", 16)),
+// new ECPoint(new BigInteger(
+// "B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21", 16),
+// new BigInteger(
+// "BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34", 16)),
+// new BigInteger(
+// "FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D", 16), 1);
+//
+// private static final ECParameterSpec P256 = new ECParameterSpec(
+// new EllipticCurve(new ECFieldFp(new BigInteger(
+// "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF",
+// 16)), new BigInteger(
+// "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC",
+// 16), new BigInteger(
+// "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B",
+// 16)), new ECPoint(new BigInteger(
+// "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296",
+// 16), new BigInteger(
+// "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5",
+// 16)), new BigInteger(
+// "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551",
+// 16), 1);
+//
+// private static final ECParameterSpec P384 = new ECParameterSpec(
+// new EllipticCurve(
+// new ECFieldFp(
+// new BigInteger(
+// "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF",
+// 16)),
+// new BigInteger(
+// "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC",
+// 16),
+// new BigInteger(
+// "B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF",
+// 16)),
+// new ECPoint(
+// new BigInteger(
+// "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7",
+// 16),
+// new BigInteger(
+// "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F",
+// 16)),
+// new BigInteger(
+// "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973",
+// 16), 1);
+//
+// private static final ECParameterSpec P521 = new ECParameterSpec(
+// new EllipticCurve(
+// new ECFieldFp(
+// new BigInteger(
+// "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
+// 16)),
+// new BigInteger(
+// "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC",
+// 16),
+// new BigInteger(
+// "0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00",
+// 16)),
+// new ECPoint(
+// new BigInteger(
+// "00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66",
+// 16),
+// new BigInteger(
+// "011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650",
+// 16)),
+// new BigInteger(
+// "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409",
+// 16), 1);
+//
+// public static final String EC_PARAMS_P192_OID = "1.2.840.10045.3.1.1";
+// public static final String EC_PARAMS_P224_OID = "1.3.132.0.33";
+// public static final String EC_PARAMS_P256_OID = "1.2.840.10045.3.1.7";
+// public static final String EC_PARAMS_P384_OID = "1.3.132.0.34";
+// public static final String EC_PARAMS_P521_OID = "1.3.132.0.35";
+//
+// private static Map<String, ECParameterSpec> oidMap = new HashMap<String, ECParameterSpec>();
+// private static Map<ECParameterSpec, String> paramsMap = new HashMap<ECParameterSpec, String>();
+// private static Map<ECParameterSpec, String> friendlyNameMap = new HashMap<ECParameterSpec, String>();
+//
+// static {
+// oidMap.put(EC_PARAMS_P192_OID, P192);
+// oidMap.put(EC_PARAMS_P224_OID, P224);
+// oidMap.put(EC_PARAMS_P256_OID, P256);
+// oidMap.put(EC_PARAMS_P384_OID, P384);
+// oidMap.put(EC_PARAMS_P521_OID, P521);
+// paramsMap.put(P192, EC_PARAMS_P192_OID);
+// paramsMap.put(P224, EC_PARAMS_P224_OID);
+// paramsMap.put(P256, EC_PARAMS_P256_OID);
+// paramsMap.put(P384, EC_PARAMS_P384_OID);
+// paramsMap.put(P521, EC_PARAMS_P521_OID);
+// friendlyNameMap.put(P192, "P-192");
+// friendlyNameMap.put(P224, "P-224");
+// friendlyNameMap.put(P256, "P-256");
+// friendlyNameMap.put(P384, "P-384");
+// friendlyNameMap.put(P521, "P-521");
+// }
+//
+// public static ECParameterSpec getParams(String oid) {
+// ECParameterSpec params;
+// if ((params = oidMap.get(oid)) != null) return params;
+// throw new IllegalArgumentException("Unsupported EC parameters: " + oid);
+// }
+//
+// public static String getOID(ECParameterSpec params) {
+// String oid;
+// if ((oid = paramsMap.get(params)) != null) return oid;
+// throw new IllegalArgumentException("Unsupport EC parameters");
+// }
+//
+// public static String getFriendlyName(ECParameterSpec params) {
+// String name;
+// if ((name = friendlyNameMap.get(params)) != null) return name;
+// throw new IllegalArgumentException("Unsupport EC parameters");
+// }
+//
+// private static final BigInteger ZERO = BigInteger.ZERO;
+// private static final BigInteger ONE = BigInteger.ONE;
+// private static final BigInteger TWO = BigInteger.valueOf(2);
+// END connectbot-removed
+ private static final BigInteger THREE = BigInteger.valueOf(3);
+// BEGIN connectbot-removed
+// private static final BigInteger FOUR = BigInteger.valueOf(4);
+// private static final BigInteger EIGHT = BigInteger.valueOf(8);
+// END connectbot-removed
+
+ private static BigInteger[] doublePointA(BigInteger[] P,
+ ECParameterSpec params) {
+ final BigInteger p = ((ECFieldFp) params.getCurve().getField()).getP();
+ final BigInteger a = params.getCurve().getA();
+
+ if (P[0] == null || P[1] == null) return P;
+
+ BigInteger d = (P[0].pow(2).multiply(THREE).add(a)).multiply(P[1]
+ .shiftLeft(1).modInverse(p));
+ BigInteger[] R = new BigInteger[2];
+ R[0] = d.pow(2).subtract(P[0].shiftLeft(1)).mod(p);
+ R[1] = d.multiply(P[0].subtract(R[0])).subtract(P[1]).mod(p);
+
+ return R;
+ }
+
+ private static BigInteger[] addPointsA(BigInteger[] P1, BigInteger[] P2,
+ ECParameterSpec params) {
+ final BigInteger p = ((ECFieldFp) params.getCurve().getField()).getP();
+
+ if (P2[0] == null || P2[1] == null) return P1;
+
+ if (P1[0] == null || P1[1] == null) return P2;
+
+ BigInteger d = (P2[1].subtract(P1[1])).multiply((P2[0].subtract(P1[0]))
+ .modInverse(p));
+ BigInteger[] R = new BigInteger[2];
+ R[0] = d.pow(2).subtract(P1[0]).subtract(P2[0]).mod(p);
+ R[1] = d.multiply(P1[0].subtract(R[0])).subtract(P1[1]).mod(p);
+
+ return R;
+ }
+
+ public static BigInteger[] multiplyPointA(BigInteger[] P, BigInteger k,
+ ECParameterSpec params) {
+ BigInteger[] Q = new BigInteger[] {null, null};
+
+ for (int i = k.bitLength() - 1; i >= 0; i--) {
+ Q = doublePointA(Q, params);
+ if (k.testBit(i)) Q = addPointsA(Q, P, params);
+ }
+
+ return Q;
+ }
+
+// BEGIN connectbot-removed
+// private static BigInteger[] doublePointJ(BigInteger[] P,
+// ECParameterSpec params) {
+// final BigInteger p = ((ECFieldFp) params.getCurve().getField()).getP();
+// BigInteger A, B, C, D;
+//
+// if (P[2].signum() == 0) // point at inf
+// return P;
+//
+// A = FOUR.multiply(P[0]).multiply(P[1].pow(2)).mod(p);
+// B = EIGHT.multiply(P[1].pow(4)).mod(p);
+// C = THREE.multiply(P[0].subtract(P[2].pow(2))).multiply(
+// P[0].add(P[2].pow(2))).mod(p);
+// D = C.pow(2).subtract(A.add(A)).mod(p);
+//
+// return new BigInteger[] {
+// D, C.multiply(A.subtract(D)).subtract(B).mod(p),
+// TWO.multiply(P[1]).multiply(P[2]).mod(p)};
+// }
+//
+// private static BigInteger[] addPointsJA(BigInteger[] P1, BigInteger[] P2,
+// ECParameterSpec params) {
+// final BigInteger p = ((ECFieldFp) params.getCurve().getField()).getP();
+// BigInteger A, B, C, D;
+// BigInteger X3;
+//
+// if (P1[2].signum() == 0) // point at inf
+// return new BigInteger[] {P2[0], P2[1], ONE};
+//
+// A = P2[0].multiply(P1[2].pow(2)).mod(p);
+// B = P2[1].multiply(P1[2].pow(3)).mod(p);
+// C = A.subtract(P1[0]).mod(p);
+// D = B.subtract(P1[1]).mod(p);
+//
+// X3 = D.pow(2)
+// .subtract(C.pow(3).add(TWO.multiply(P1[0]).multiply(C.pow(2)))).mod(p);
+// return new BigInteger[] {
+// X3,
+// D.multiply(P1[0].multiply(C.pow(2)).subtract(X3)).subtract(
+// P1[1].multiply(C.pow(3))).mod(p), P1[2].multiply(C).mod(p)};
+// }
+//
+// // Binary NAF method for point multiplication
+// public static BigInteger[] multiplyPoint(BigInteger[] P, BigInteger k,
+// ECParameterSpec params) {
+// BigInteger h = THREE.multiply(k);
+//
+// BigInteger[] Pneg = new BigInteger[] {P[0], P[1].negate()};
+// BigInteger[] R = new BigInteger[] {P[0], P[1], ONE};
+//
+// int bitLen = h.bitLength();
+// for (int i = bitLen - 2; i > 0; --i) {
+// R = doublePointJ(R, params);
+// if (h.testBit(i)) R = addPointsJA(R, P, params);
+// if (k.testBit(i)) R = addPointsJA(R, Pneg, params);
+// }
+//
+// // // <DEBUG>
+// // BigInteger[] SS = new BigInteger[] { R[0], R[1], R[2] };
+// // toAffine(SS, params);
+// // BigInteger[] RR = multiplyPointA(P, k, params);
+// // if (!SS[0].equals(RR[0]) || !SS[1].equals(RR[1]))
+// // throw new RuntimeException("Internal mult error");
+// // // </DEBUG>
+//
+// return R;
+// }
+
+// // Simultaneous multiple point multiplication, also known as Shamir's trick
+// static BigInteger[] multiplyPoints(BigInteger[] P, BigInteger k,
+// BigInteger[] Q, BigInteger l, ECParameterSpec params) {
+// BigInteger[] PQ = addPointsA(P, Q, params);
+// BigInteger[] R = new BigInteger[] {null, null, ZERO};
+//
+// int max = Math.max(k.bitLength(), l.bitLength());
+// for (int i = max - 1; i >= 0; --i) {
+// R = doublePointJ(R, params);
+// if (k.testBit(i)) {
+// if (l.testBit(i))
+// R = addPointsJA(R, PQ, params);
+// else
+// R = addPointsJA(R, P, params);
+// } else if (l.testBit(i)) R = addPointsJA(R, Q, params);
+// }
+//
+// // // <DEBUG>
+// // BigInteger[] SS = new BigInteger[] { R[0], R[1], R[2] };
+// // toAffine(SS, params);
+// // BigInteger[] AA = multiplyPointA(P, k, params);
+// // BigInteger[] BB = multiplyPointA(Q, l, params);
+// // BigInteger[] AB = addPointsA(AA, BB, params);
+// // if (!SS[0].equals(AB[0]) || !SS[1].equals(AB[1]))
+// // throw new RuntimeException("Internal mult error");
+// // // </DEBUG>
+//
+// return R;
+// }
+//
+// // SEC 1, 2.3.5
+// static byte[] fieldElemToBytes(BigInteger a, ECParameterSpec params) {
+// int len = (((ECFieldFp) params.getCurve().getField()).getP().bitLength() + 7) / 8;
+// byte[] bytes = a.toByteArray();
+// if (len < bytes.length) {
+// byte[] tmp = new byte[len];
+// System.arraycopy(bytes, bytes.length - tmp.length, tmp, 0, tmp.length);
+// return tmp;
+// } else if (len > bytes.length) {
+// byte[] tmp = new byte[len];
+// System.arraycopy(bytes, 0, tmp, tmp.length - bytes.length, bytes.length);
+// return tmp;
+// }
+// return bytes;
+// }
+//
+// static int fieldElemToBytes(BigInteger a, ECParameterSpec params,
+// byte[] data, int off) {
+// int len = (((ECFieldFp) params.getCurve().getField()).getP().bitLength() + 7) / 8;
+// byte[] bytes = a.toByteArray();
+// if (len < bytes.length) {
+// System.arraycopy(bytes, bytes.length - len, data, off, len);
+// return len;
+// } else if (len > bytes.length) {
+// System.arraycopy(bytes, 0, data, len - bytes.length + off, bytes.length);
+// return len;
+// }
+// System.arraycopy(bytes, 0, data, off, bytes.length);
+// return bytes.length;
+// }
+//
+// // SEC 1, 2.3.3
+// static byte[] ecPointToBytes(ECPoint a, ECParameterSpec params) {
+// byte[] fe1 = fieldElemToBytes(a.getAffineX(), params);
+// byte[] fe2 = fieldElemToBytes(a.getAffineY(), params);
+// byte[] bytes = new byte[1 + fe1.length + fe2.length];
+// bytes[0] = 0x04;
+// System.arraycopy(fe1, 0, bytes, 1, fe1.length);
+// System.arraycopy(fe2, 0, bytes, 1 + fe1.length, fe2.length);
+// return bytes;
+// }
+//
+// // SEC 1, 2.3.4
+// static ECPoint bytesToECPoint(byte[] bytes, ECParameterSpec params) {
+// switch (bytes[0]) {
+// case 0x00: // point at inf
+// throw new IllegalArgumentException(
+// "Point at infinity is not a valid argument");
+// case 0x02: // point compression
+// case 0x03:
+// throw new UnsupportedOperationException(
+// "Point compression is not supported");
+// case 0x04:
+// final BigInteger p = ((ECFieldFp) params.getCurve().getField()).getP();
+// byte[] fe = new byte[(p.bitLength() + 7) / 8];
+// System.arraycopy(bytes, 1, fe, 0, fe.length);
+// BigInteger x = new BigInteger(1, fe);
+// System.arraycopy(bytes, 1 + fe.length, fe, 0, fe.length);
+// return new ECPoint(x, new BigInteger(1, fe));
+// default:
+// throw new IllegalArgumentException("Invalid point encoding");
+// }
+// }
+//
+// // Convert Jacobian point to affine
+// static void toAffine(BigInteger[] P, ECParameterSpec params) {
+// final BigInteger p = ((ECFieldFp) params.getCurve().getField()).getP();
+// P[0] = P[0].multiply(P[2].pow(2).modInverse(p)).mod(p);
+// P[1] = P[1].multiply(P[2].pow(3).modInverse(p)).mod(p);
+// }
+//
+// static void toAffineX(BigInteger[] P, ECParameterSpec params) {
+// final BigInteger p = ((ECFieldFp) params.getCurve().getField()).getP();
+// P[0] = P[0].multiply(P[2].pow(2).modInverse(p)).mod(p);
+// }
+//
+// static BigInteger[] internalPoint(ECPoint P) {
+// return new BigInteger[] {P.getAffineX(), P.getAffineY()};
+// }
+//
+// // private static void printPerf(String msg, long start, long stop) {
+// // String unit = "ms";
+// // long diff = stop - start;
+// // if (diff > 1000) {
+// // diff /= 1000;
+// // unit = "s";
+// // }
+// // System.out.printf("%s: %d %s\n", msg, diff, unit);
+// // }
+//
+// public static void main(String[] args) throws Exception {
+//
+// Security.insertProviderAt(new EcCore(), 0);
+//
+// // ----
+// // Test primitives
+// // ----
+//
+// // GooKey EC private key, 256 bit
+// // Private value:
+// // a9231e0d113abdacd3bb5edb24124fbef6f562c5f90b835670f5e48f775019f2
+// // Parameters: P-256 (1.2.840.10045.3.1.7)
+// // GooKey EC public key, 256 bit
+// // Public value (x coordinate):
+// // 86645e0320c0f9dc1a9b8456396cc105754df67a9829c21e13ab6ecf944cf68c
+// // Public value (y coordinate):
+// // ea1721a578043d48f12738359b5eb5f0dac2242ec6128ee0ab6ff40c8fe0cae6
+// // Parameters: P-256 (1.2.840.10045.3.1.7)
+// // GooKey EC private key, 256 bit
+// // Private value:
+// // b84d5cfab214fc3928864abb85f668a85b1006ca0147c78f22deb1dcc7e4a022
+// // Parameters: P-256 (1.2.840.10045.3.1.7)
+// // GooKey EC public key, 256 bit
+// // Public value (x coordinate):
+// // 61f6f7264f0a19f0debcca3efd079667a0112cc0b8be07a815b4c375e96ad3d1
+// // Public value (y coordinate):
+// // 3308c0016d776ed5aa9f021e43348b2e684b3b7a0f25dc9e4c8670b5d87cb705
+// // Parameters: P-256 (1.2.840.10045.3.1.7)
+//
+// // P = kG
+// BigInteger k = new BigInteger(
+// "a9231e0d113abdacd3bb5edb24124fbef6f562c5f90b835670f5e48f775019f2", 16);
+// BigInteger[] P = new BigInteger[] {
+// new BigInteger(
+// "86645e0320c0f9dc1a9b8456396cc105754df67a9829c21e13ab6ecf944cf68c",
+// 16),
+// new BigInteger(
+// "ea1721a578043d48f12738359b5eb5f0dac2242ec6128ee0ab6ff40c8fe0cae6",
+// 16), ONE};
+//
+// // Q = lG
+// BigInteger l = new BigInteger(
+// "b84d5cfab214fc3928864abb85f668a85b1006ca0147c78f22deb1dcc7e4a022", 16);
+// BigInteger[] Q = new BigInteger[] {
+// new BigInteger(
+// "61f6f7264f0a19f0debcca3efd079667a0112cc0b8be07a815b4c375e96ad3d1",
+// 16),
+// new BigInteger(
+// "3308c0016d776ed5aa9f021e43348b2e684b3b7a0f25dc9e4c8670b5d87cb705",
+// 16), ONE};
+//
+// // Known answer for P+Q
+// BigInteger[] kat1 = new BigInteger[] {
+// new BigInteger(
+// "bc7adb05bca2460bbfeb4e0f88b61c384ea88ed3fd56017938ac2582513d4220",
+// 16),
+// new BigInteger(
+// "a640a43df2e9df39eec11445b7e3f7835b743ef1ac4a83cecb570a060b3f1c6c",
+// 16)};
+//
+// BigInteger[] R = addPointsA(P, Q, P256);
+// if (!R[0].equals(kat1[0]) || !R[1].equals(kat1[1]))
+// throw new RuntimeException("kat1 failed");
+//
+// R = addPointsJA(P, Q, P256);
+// toAffine(R, P256);
+// if (!R[0].equals(kat1[0]) || !R[1].equals(kat1[1]))
+// throw new RuntimeException("kat1 failed");
+//
+//
+// // Known answer for Q+Q
+// BigInteger[] kat2 = new BigInteger[] {
+// new BigInteger(
+// "c79d7f9100c14a70f0bb9bdce59654abf99e10d1ac5afc1a0f1b6bc650d6429b",
+// 16),
+// new BigInteger(
+// "6856814e47adce42bc0d7c3bef308c6c737c418ed093effb31e21f53c7735c97",
+// 16)};
+//
+// R = doublePointA(P, P256);
+// if (!R[0].equals(kat2[0]) || !R[1].equals(kat2[1]))
+// throw new RuntimeException("kat2 failed");
+//
+// R = doublePointJ(P, P256);
+// toAffine(R, P256);
+// if (!R[0].equals(kat2[0]) || !R[1].equals(kat2[1]))
+// throw new RuntimeException("kat2 failed");
+//
+// // Known answer for kP
+// BigInteger[] kat3 = new BigInteger[] {
+// new BigInteger(
+// "97a82a834b9e6b50660ae30d43dac9b200276e8bcd2ed6a6593048de09276d1a",
+// 16),
+// new BigInteger(
+// "30a9590a01066d8ef54a910afcc8648dbc7400c01750af423ce95547f2154d56",
+// 16)};
+//
+// R = multiplyPointA(P, k, P256);
+// if (!R[0].equals(kat3[0]) || !R[1].equals(kat3[1]))
+// throw new RuntimeException("kat3 failed");
+//
+// R = multiplyPoint(P, k, P256);
+// toAffine(R, P256);
+// if (!R[0].equals(kat3[0]) || !R[1].equals(kat3[1]))
+// throw new RuntimeException("kat3 failed");
+//
+// // Known answer for kP+lQ
+// BigInteger[] kat4 = new BigInteger[] {
+// new BigInteger(
+// "6fd51be5cf3d6a6bcb62594bbe41ccf549b37d8fefff6e293a5bea0836efcfc6",
+// 16),
+// new BigInteger(
+// "9bc21a930137aa3814908974c431e4545a05dce61321253c337f3883129c42ca",
+// 16)};
+//
+// BigInteger[] RR = multiplyPointA(Q, l, P256);
+// R = addPointsA(R, RR, P256);
+// if (!R[0].equals(kat4[0]) || !R[1].equals(kat4[1]))
+// throw new RuntimeException("kat4 failed");
+//
+// R = multiplyPoints(P, k, Q, l, P256);
+// toAffine(R, P256);
+// if (!R[0].equals(kat4[0]) || !R[1].equals(kat4[1]))
+// throw new RuntimeException("kat4 failed");
+//
+// // ----
+// // Test ECDSA in various combinations
+// // ----
+//
+// Provider gooProv = Security.getProvider("GooKey");
+// Provider nssProv = Security.getProvider("SunPKCS11-NSS");
+//
+// // Number of iterations: trust me, this is a (stress) good test
+// // and does provoke bugs in a fuzzing way.
+// int iter = 50;
+//
+// // Iterate over all key lengths and signature schemes.
+// int[] keyLengths = {192, 224, 256, 384, 521};
+// String[] ecdsas = {
+// "SHA1withECDSA", "SHA256withECDSA", "SHA384withECDSA",
+// "SHA512withECDSA"};
+// for (int s = 0; s < ecdsas.length; s++) {
+// System.out.println("Signature scheme " + ecdsas[s]);
+// for (int i = 0; i < keyLengths.length; i++) {
+// System.out.print("Testing P-" + keyLengths[i] + ": ");
+// for (int n = 0; n < iter; n++) {
+// System.out.print(".");
+//
+// KeyPairGenerator kpGen = KeyPairGenerator.getInstance("EC", gooProv);
+// kpGen.initialize(keyLengths[i]);
+// KeyPair ecKeyPair = kpGen.generateKeyPair();
+//
+// ECPrivateKey ecPrivKey = (ECPrivateKey) ecKeyPair.getPrivate();
+// byte[] tmp = ecPrivKey.getEncoded();
+// KeyFactory keyFab = KeyFactory.getInstance("EC", gooProv);
+// keyFab.generatePrivate(new PKCS8EncodedKeySpec(tmp));
+// ECPrivateKeySpec ecPrivSpec = new ECPrivateKeySpec(ecPrivKey.getS(),
+// ecPrivKey.getParams());
+// keyFab.generatePrivate(ecPrivSpec);
+//
+// ECPublicKey ecPubKey = (ECPublicKey) ecKeyPair.getPublic();
+// tmp = ecPubKey.getEncoded(); // dont modify tmp now - is used below
+// keyFab.generatePublic(new X509EncodedKeySpec(tmp));
+// ECPublicKeySpec ecPubSpec = new ECPublicKeySpec(ecPubKey.getW(),
+// ecPubKey.getParams());
+// keyFab.generatePublic(ecPubSpec);
+//
+// Signature ecdsa = Signature.getInstance(ecdsas[s], gooProv);
+// ecdsa.initSign(ecPrivKey);
+// ecdsa.update(tmp);
+// byte[] sig = ecdsa.sign();
+// ecdsa.initVerify(ecPubKey);
+// ecdsa.update(tmp);
+// if (!ecdsa.verify(sig))
+// throw new RuntimeException("Signature not verified: "
+// + keyLengths[i]);
+//
+// // Cross verify using NSS if present
+// if (nssProv != null) {
+// keyFab = KeyFactory.getInstance("EC", nssProv);
+//
+// // For some reason NSS doesnt seem to work for P-192 and P-224?!
+// if (keyLengths[i] == 192 || keyLengths[i] == 224) continue;
+//
+// ECPrivateKey nssPrivKey = (ECPrivateKey) keyFab
+// .generatePrivate(new PKCS8EncodedKeySpec(ecPrivKey.getEncoded()));
+// ECPublicKey nssPubKey = (ECPublicKey) keyFab
+// .generatePublic(new X509EncodedKeySpec(ecPubKey.getEncoded()));
+//
+// ecdsa = Signature.getInstance(ecdsas[s], nssProv);
+// ecdsa.initVerify(nssPubKey);
+// ecdsa.update(tmp);
+// if (!ecdsa.verify(sig))
+// throw new RuntimeException("Signature not verified 2: "
+// + keyLengths[i]);
+//
+// ecdsa.initSign(nssPrivKey);
+// ecdsa.update(tmp);
+// sig = ecdsa.sign();
+// ecdsa = Signature.getInstance(ecdsas[s], gooProv);
+// ecdsa.initVerify(ecPubKey);
+// ecdsa.update(tmp);
+// if (!ecdsa.verify(sig))
+// throw new RuntimeException("Signature not verified 3: "
+// + keyLengths[i]);
+// }
+// }
+// System.out.println(" done");
+// }
+// }
+//
+// // Test Keyczar integration
+// // Signer ecdsaSigner = new Signer("c:\\temp\\eckeyset");
+// // String tbs = "Sign this";
+// // String sig = ecdsaSigner.sign(tbs);
+// // if (ecdsaSigner.verify(sig, tbs))
+// // System.out.println("Keyczar EC OK");
+// // else
+// // System.out.println("Keyczar EC not OK");
+// }
+//END connectbot-removed
+}
diff --git a/tests/.classpath b/tests/.classpath
index ae03331..1ff00ba 100644
--- a/tests/.classpath
+++ b/tests/.classpath
@@ -4,5 +4,6 @@
<classpathentry kind="src" path="gen"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry combineaccessrules="false" kind="src" path="/connectbot"/>
- <classpathentry kind="output" path="bin"/>
+ <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
+ <classpathentry kind="output" path="bin/classes"/>
</classpath>
diff --git a/tests/.gitignore b/tests/.gitignore
index 8a2a610..fcead0a 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -1,3 +1,4 @@
bin
coverage
gen
+libs
diff --git a/tests/.settings/org.eclipse.jdt.core.prefs b/tests/.settings/org.eclipse.jdt.core.prefs
index b8fb425..e87c6c7 100644
--- a/tests/.settings/org.eclipse.jdt.core.prefs
+++ b/tests/.settings/org.eclipse.jdt.core.prefs
@@ -1,5 +1,291 @@
-#Wed Nov 14 13:33:03 CST 2007
eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
-org.eclipse.jdt.core.compiler.compliance=1.5
-org.eclipse.jdt.core.compiler.source=1.5
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
+org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
+org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
+org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=true
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=100
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=tab
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_on_off_tags=false
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
+org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
diff --git a/tests/.settings/org.eclipse.jdt.ui.prefs b/tests/.settings/org.eclipse.jdt.ui.prefs
index 16019b1..e777683 100644
--- a/tests/.settings/org.eclipse.jdt.ui.prefs
+++ b/tests/.settings/org.eclipse.jdt.ui.prefs
@@ -1,4 +1,3 @@
-#Sat Jan 24 08:09:15 CST 2009
cleanup.add_default_serial_version_id=true
cleanup.add_generated_serial_version_id=false
cleanup.add_missing_annotations=true
@@ -54,7 +53,7 @@ cleanup_settings_version=2
eclipse.preferences.version=1
editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
formatter_profile=_ConnectBot
-formatter_settings_version=11
+formatter_settings_version=12
org.eclipse.jdt.ui.ignorelowercasenames=true
org.eclipse.jdt.ui.importorder=java;javax;org;com;
org.eclipse.jdt.ui.javadoc=true
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 7dc06ee..59a5b6f 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -17,7 +17,5 @@
-->
<instrumentation android:name="android.test.InstrumentationTestRunner"
android:targetPackage="org.connectbot"
- android:label="Tests for HostListActivity" />
-
- <uses-sdk android:targetSdkVersion="6" android:minSdkVersion="3" />
+ android:label="Tests for ConnectBot" />
</manifest>
diff --git a/tests/ant.properties b/tests/ant.properties
new file mode 100644
index 0000000..366f276
--- /dev/null
+++ b/tests/ant.properties
@@ -0,0 +1,21 @@
+# This file is used to override default values used by the Ant build system.
+#
+# This file must be checked in Version Control Systems, as it is
+# integral to the build system of your project.
+
+# This file is only used by the Ant script.
+
+# You can use this to override default values such as
+# 'source.dir' for the location of your java source folder and
+# 'out.dir' for the location of your output folder.
+
+# You can also use it define how the release builds are signed by declaring
+# the following properties:
+# 'key.store' for the location of your keystore and
+# 'key.alias' for the name of the key to use.
+# The password will be asked during the build when you use the 'release' target.
+
+tested.project.dir=..
+source-folder=src
+application-package=org.connectbot
+out-folder=bin
diff --git a/tests/build.properties b/tests/build.properties
deleted file mode 100644
index bd9d530..0000000
--- a/tests/build.properties
+++ /dev/null
@@ -1,15 +0,0 @@
-# This file is used to override default values used by the Ant build system.
-#
-# This file must be checked in Version Control Systems, as it is
-# integral to the build system of your project.
-
-# The name of your application package as defined in the manifest.
-# Used by the 'uninstall' rule.
-application-package=org.connectbot
-
-# The name of the source folder.
-source-folder=src
-
-# The name of the output folder.
-out-folder=bin
-
diff --git a/tests/build.xml b/tests/build.xml
index ca78485..234ab54 100644
--- a/tests/build.xml
+++ b/tests/build.xml
@@ -1,61 +1,138 @@
<?xml version="1.0" encoding="UTF-8"?>
-<project name="ConnectBotTests" default="help">
+<!-- vim: set ts=4 sw=4 et: -->
+<project name="tests" default="help">
<!-- The local.properties file is created and updated by the 'android' tool.
- It contain the path to the SDK. It should *NOT* be checked in in Version
- Control Systems. -->
- <property file="local.properties"/>
+ It contains the path to the SDK. It should *NOT* be checked into
+ Version Control Systems. -->
+ <loadproperties srcFile="local.properties" />
- <!-- The build.properties file can be created by you and is never touched
- by the 'android' tool. This is the place to change some of the default property values
- used by the Ant rules.
+ <!-- The ant.properties file can be created by you. It is only edited by the
+ 'android' tool to add properties to it.
+ This is the place to change some Ant specific build properties.
Here are some properties you may want to change/update:
- application-package
- the name of your application package as defined in the manifest. Used by the
- 'uninstall' rule.
source.dir
- the name of the source folder. Default is 'src'.
+ The name of the source directory. Default is 'src'.
out.dir
- the name of the output folder. Default is 'bin'.
+ The name of the output directory. Default is 'bin'.
- Properties related to the SDK location or the project target should be updated
- using the 'android' tool with the 'update' action.
+ For other overridable properties, look at the beginning of the rules
+ files in the SDK, at tools/ant/build.xml
- This file is an integral part of the build system for your application and
- should be checked in in Version Control Systems.
+ Properties related to the SDK location or the project target should
+ be updated using the 'android' tool with the 'update' action.
+
+ This file is an integral part of the build system for your
+ application and should be checked into Version Control Systems.
-->
- <property file="build.properties"/>
-
- <!-- The default.properties file is created and updated by the 'android' tool, as well
- as ADT.
- This file is an integral part of the build system for your application and
- should be checked in in Version Control Systems. -->
- <property file="default.properties"/>
-
- <!-- Custom Android task to deal with the project target, and import the proper rules.
- This requires ant 1.6.0 or above. -->
- <path id="android.antlibs">
- <pathelement path="${sdk.dir}/tools/lib/anttasks.jar" />
- <pathelement path="${sdk.dir}/tools/lib/sdklib.jar" />
- <pathelement path="${sdk.dir}/tools/lib/androidprefs.jar" />
- <pathelement path="${sdk.dir}/tools/lib/apkbuilder.jar" />
- <pathelement path="${sdk.dir}/tools/lib/jarutils.jar" />
- </path>
-
- <taskdef name="setup"
- classname="com.android.ant.SetupTask"
- classpathref="android.antlibs"/>
-
- <!-- Execute the Android Setup task that will setup some properties specific to the target,
- and import the rules files.
- To customize the rules, copy/paste them below the task, and disable import by setting
- the import attribute to false:
- <setup import="false" />
-
- This will ensure that the properties are setup correctly but that your customized
- targets are used.
+ <property file="ant.properties" />
+
+ <!-- The project.properties file is created and updated by the 'android'
+ tool, as well as ADT.
+
+ This contains project specific properties such as project target, and library
+ dependencies. Lower level build properties are stored in ant.properties
+ (or in .classpath for Eclipse projects).
+
+ This file is an integral part of the build system for your
+ application and should be checked into Version Control Systems. -->
+ <loadproperties srcFile="project.properties" />
+
+ <!-- quick check on sdk.dir -->
+ <fail
+ message="sdk.dir is missing. Make sure to generate local.properties using 'android update project'"
+ unless="sdk.dir"
+ />
+
+
+<!-- extension targets. Uncomment the ones where you want to do custom work
+ in between standard targets -->
+<!--
+ <target name="-pre-build">
+ </target>
+ <target name="-pre-compile">
+ </target>
+
+ /* This is typically used for code obfuscation.
+ Compiled code location: ${out.classes.absolute.dir}
+ If this is not done in place, override ${out.dex.input.absolute.dir} */
+ <target name="-post-compile">
+ </target>
+-->
+
+ <!-- Import the actual build file.
+
+ To customize existing targets, there are two options:
+ - Customize only one target:
+ - copy/paste the target into this file, *before* the
+ <import> task.
+ - customize it to your needs.
+ - Customize the whole content of build.xml
+ - copy/paste the content of the rules files (minus the top node)
+ into this file, replacing the <import> task.
+ - customize to your needs.
+
+ ***********************
+ ****** IMPORTANT ******
+ ***********************
+ In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
+ in order to avoid having your file be overridden by tools such as "android update project"
-->
- <setup />
+ <!-- version-tag: custom -->
+ <import file="${sdk.dir}/tools/ant/build.xml" />
+
+ <target name="coverage-xml" depends="-test-project-check">
+
+ <property name="tested.project.absolute.dir" location="${tested.project.dir}" />
+
+ <property name="test.runner" value="android.test.InstrumentationTestRunner" />
+
+ <!-- Application package of the tested project extracted from its manifest file -->
+ <xpath input="${tested.project.absolute.dir}/AndroidManifest.xml"
+ expression="/manifest/@package" output="tested.manifest.package" />
+
+ <getprojectpaths projectPath="${tested.project.absolute.dir}"
+ binOut="tested.project.out.absolute.dir"
+ srcOut="tested.project.source.absolute.dir" />
+
+ <getlibpath projectPath="${tested.project.absolute.dir}"
+ libraryFolderPathOut="tested.project.lib.source.path"
+ leaf="@{source.dir}" />
+
+ <property name="emma.dump.file"
+ value="/data/data/${tested.manifest.package}/coverage.ec" />
+
+ <run-tests-helper emma.enabled="true">
+ <extra-instrument-args>
+ <arg value="-e" />
+ <arg value="coverageFile" />
+ <arg value="${emma.dump.file}" />
+ </extra-instrument-args>
+ </run-tests-helper>
+ <echo>Downloading coverage file into project directory...</echo>
+ <exec executable="${adb}" failonerror="true">
+ <arg line="${adb.device.arg}" />
+ <arg value="pull" />
+ <arg value="${emma.dump.file}" />
+ <arg value="${out.absolute.dir}/coverage.ec" />
+ </exec>
+ <echo>Extracting XML coverage report...</echo>
+ <emma>
+ <report sourcepath="${tested.project.source.absolute.dir}:${tested.project.lib.source.path.value}"
+ verbosity="${verbosity}">
+ <!-- TODO: report.dir or something like should be introduced if necessary -->
+ <infileset file="${out.absolute.dir}/coverage.ec" />
+ <infileset file="${tested.project.out.absolute.dir}/coverage.em" />
+ <!-- TODO: reports in other, indicated by user formats -->
+ <xml outfile="${out.absolute.dir}/coverage.xml" />
+ </report>
+ </emma>
+ <echo level="info">Cleaning up temporary files...</echo>
+ <delete file="${out.absolute.dir}/coverage.ec" />
+ <delete file="${out.absolute.dir}/coverage.em" />
+ <echo level="info">Saving the report file in ${out.absolute.dir}/coverage.xml</echo>
+ </target>
+
</project>
diff --git a/tests/proguard.cfg b/tests/proguard.cfg
new file mode 100644
index 0000000..b1cdf17
--- /dev/null
+++ b/tests/proguard.cfg
@@ -0,0 +1,40 @@
+-optimizationpasses 5
+-dontusemixedcaseclassnames
+-dontskipnonpubliclibraryclasses
+-dontpreverify
+-verbose
+-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
+
+-keep public class * extends android.app.Activity
+-keep public class * extends android.app.Application
+-keep public class * extends android.app.Service
+-keep public class * extends android.content.BroadcastReceiver
+-keep public class * extends android.content.ContentProvider
+-keep public class * extends android.app.backup.BackupAgentHelper
+-keep public class * extends android.preference.Preference
+-keep public class com.android.vending.licensing.ILicensingService
+
+-keepclasseswithmembernames class * {
+ native <methods>;
+}
+
+-keepclasseswithmembers class * {
+ public <init>(android.content.Context, android.util.AttributeSet);
+}
+
+-keepclasseswithmembers class * {
+ public <init>(android.content.Context, android.util.AttributeSet, int);
+}
+
+-keepclassmembers class * extends android.app.Activity {
+ public void *(android.view.View);
+}
+
+-keepclassmembers enum * {
+ public static **[] values();
+ public static ** valueOf(java.lang.String);
+}
+
+-keep class * implements android.os.Parcelable {
+ public static final android.os.Parcelable$Creator *;
+}
diff --git a/tests/default.properties b/tests/project.properties
index 85c595c..d1f88bc 100644
--- a/tests/default.properties
+++ b/tests/project.properties
@@ -4,10 +4,10 @@
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system use,
-# "build.properties", and override values to adapt the script to your
+# "ant.properties", and override values to adapt the script to your
# project structure.
# Indicates whether an apk should be generated for each density.
split.density=false
# Project target.
-target=android-4
+target=android-11
diff --git a/tests/src/org/connectbot/HostListActivityTest.java b/tests/src/org/connectbot/HostListActivityTest.java
index 6608e6d..3962c9a 100644
--- a/tests/src/org/connectbot/HostListActivityTest.java
+++ b/tests/src/org/connectbot/HostListActivityTest.java
@@ -17,6 +17,7 @@
package org.connectbot;
+import android.app.Activity;
import android.test.ActivityInstrumentationTestCase2;
/**
@@ -24,24 +25,23 @@ import android.test.ActivityInstrumentationTestCase2;
* {@link android.test.ApplicationTestCase ApplicationTestCase} for more
* information on how to write and extend Application tests.
* <p/>
- * To run this test, you can type:
- * adb shell am instrument -w \
- * -e class org.connectbot.HostListActivityTest \
+ * To run this test, you can type: adb shell am instrument -w \ -e class
+ * org.connectbot.HostListActivityTest \
* org.connectbot.tests/android.test.InstrumentationTestRunner
*/
-public class HostListActivityTest extends
- ActivityInstrumentationTestCase2<HostListActivity> {
+public class HostListActivityTest extends ActivityInstrumentationTestCase2<HostListActivity> {
+ private Activity mActivity;
public HostListActivityTest() {
super("org.connectbot", HostListActivity.class);
}
- public void testOpenMenu() {
- HostListActivity a = getActivity();
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
- a.openOptionsMenu();
+ setActivityInitialTouchMode(false);
- a.closeOptionsMenu();
+ mActivity = getActivity();
}
-
}
diff --git a/tests/src/org/connectbot/TerminalBridgeTest.java b/tests/src/org/connectbot/TerminalBridgeTest.java
index ef18022..bfa5e23 100644
--- a/tests/src/org/connectbot/TerminalBridgeTest.java
+++ b/tests/src/org/connectbot/TerminalBridgeTest.java
@@ -17,15 +17,7 @@
package org.connectbot;
-import java.lang.reflect.Field;
-
-import org.connectbot.mock.NullTransport;
-import org.connectbot.service.TerminalBridge;
-import org.connectbot.transport.AbsTransport;
-import org.connectbot.util.PreferenceConstants;
-
import android.test.AndroidTestCase;
-import android.view.KeyEvent;
/**
* @author Kenny Root
@@ -34,80 +26,80 @@ import android.view.KeyEvent;
public class TerminalBridgeTest extends AndroidTestCase {
public void testShiftLock() throws SecurityException, NoSuchFieldException,
IllegalArgumentException, IllegalAccessException {
- TerminalBridge bridge = new TerminalBridge();
- AbsTransport nullTransport = new NullTransport();
-
- // Make sure onKey will work when we call it
- Field disconnected = TerminalBridge.class
- .getDeclaredField("disconnected");
- Field keymode = TerminalBridge.class.getDeclaredField("keymode");
- Field transport = TerminalBridge.class.getDeclaredField("transport");
-
- disconnected.setAccessible(true);
- keymode.setAccessible(true);
- transport.setAccessible(true);
-
- disconnected.setBoolean(bridge, false);
- keymode.set(bridge, PreferenceConstants.KEYMODE_RIGHT);
- transport.set(bridge, nullTransport);
-
- // Begin tests
- assertTrue("Meta state is " + bridge.getMetaState()
- + " when it should be 0", bridge.getMetaState() == 0);
-
- KeyEvent shiftDown = new KeyEvent(KeyEvent.ACTION_DOWN,
- KeyEvent.KEYCODE_SHIFT_LEFT);
- bridge.onKey(null, shiftDown.getKeyCode(), shiftDown);
-
- assertTrue("Shift test: after shift press, meta state is "
- + bridge.getMetaState() + " when it should be "
- + TerminalBridge.META_SHIFT_ON,
- bridge.getMetaState() == TerminalBridge.META_SHIFT_ON);
-
- KeyEvent shiftUp = KeyEvent.changeAction(shiftDown, KeyEvent.ACTION_UP);
- bridge.onKey(null, shiftUp.getKeyCode(), shiftUp);
-
- assertTrue("Shift test: after shift release, meta state is "
- + bridge.getMetaState() + " when it should be "
- + TerminalBridge.META_SHIFT_ON,
- bridge.getMetaState() == TerminalBridge.META_SHIFT_ON);
-
- KeyEvent letterAdown = new KeyEvent(KeyEvent.ACTION_DOWN,
- KeyEvent.KEYCODE_A);
- KeyEvent letterAup = KeyEvent.changeAction(letterAdown,
- KeyEvent.ACTION_UP);
-
- bridge.onKey(null, letterAdown.getKeyCode(), letterAdown);
- bridge.onKey(null, letterAup.getKeyCode(), letterAup);
-
- assertTrue("Shift test: after letter press and release, meta state is "
- + bridge.getMetaState() + " when it should be 0", bridge
- .getMetaState() == 0);
-
- bridge.onKey(null, shiftDown.getKeyCode(), shiftDown);
- bridge.onKey(null, shiftUp.getKeyCode(), shiftUp);
- bridge.onKey(null, shiftDown.getKeyCode(), shiftDown);
- bridge.onKey(null, shiftUp.getKeyCode(), shiftUp);
-
- assertTrue("Shift lock test: after two shift presses, meta state is "
- + bridge.getMetaState() + " when it should be "
- + TerminalBridge.META_SHIFT_LOCK,
- bridge.getMetaState() == TerminalBridge.META_SHIFT_LOCK);
-
- bridge.onKey(null, letterAdown.getKeyCode(), letterAdown);
-
- assertTrue(
- "Shift lock test: after letter press, meta state is "
- + bridge.getMetaState() + " when it should be "
- + TerminalBridge.META_SHIFT_LOCK,
- bridge.getMetaState() == TerminalBridge.META_SHIFT_LOCK);
-
- bridge.onKey(null, letterAup.getKeyCode(), letterAup);
-
- assertTrue(
- "Shift lock test: after letter press and release, meta state is "
- + bridge.getMetaState() + " when it should be "
- + TerminalBridge.META_SHIFT_LOCK,
- bridge.getMetaState() == TerminalBridge.META_SHIFT_LOCK);
+// TerminalBridge bridge = new TerminalBridge();
+// AbsTransport nullTransport = new NullTransport();
+//
+// // Make sure onKey will work when we call it
+// Field disconnected = TerminalBridge.class
+// .getDeclaredField("disconnected");
+// Field keymode = TerminalBridge.class.getDeclaredField("keymode");
+// Field transport = TerminalBridge.class.getDeclaredField("transport");
+//
+// disconnected.setAccessible(true);
+// keymode.setAccessible(true);
+// transport.setAccessible(true);
+//
+// disconnected.setBoolean(bridge, false);
+// keymode.set(bridge, PreferenceConstants.KEYMODE_RIGHT);
+// transport.set(bridge, nullTransport);
+//
+// // Begin tests
+// assertTrue("Meta state is " + bridge.getMetaState()
+// + " when it should be 0", bridge.getMetaState() == 0);
+//
+// KeyEvent shiftDown = new KeyEvent(KeyEvent.ACTION_DOWN,
+// KeyEvent.KEYCODE_SHIFT_LEFT);
+// bridge.onKey(null, shiftDown.getKeyCode(), shiftDown);
+//
+// assertTrue("Shift test: after shift press, meta state is "
+// + bridge.getMetaState() + " when it should be "
+// + TerminalBridge.META_SHIFT_ON,
+// bridge.getMetaState() == TerminalBridge.META_SHIFT_ON);
+//
+// KeyEvent shiftUp = KeyEvent.changeAction(shiftDown, KeyEvent.ACTION_UP);
+// bridge.onKey(null, shiftUp.getKeyCode(), shiftUp);
+//
+// assertTrue("Shift test: after shift release, meta state is "
+// + bridge.getMetaState() + " when it should be "
+// + TerminalBridge.META_SHIFT_ON,
+// bridge.getMetaState() == TerminalBridge.META_SHIFT_ON);
+//
+// KeyEvent letterAdown = new KeyEvent(KeyEvent.ACTION_DOWN,
+// KeyEvent.KEYCODE_A);
+// KeyEvent letterAup = KeyEvent.changeAction(letterAdown,
+// KeyEvent.ACTION_UP);
+//
+// bridge.onKey(null, letterAdown.getKeyCode(), letterAdown);
+// bridge.onKey(null, letterAup.getKeyCode(), letterAup);
+//
+// assertTrue("Shift test: after letter press and release, meta state is "
+// + bridge.getMetaState() + " when it should be 0", bridge
+// .getMetaState() == 0);
+//
+// bridge.onKey(null, shiftDown.getKeyCode(), shiftDown);
+// bridge.onKey(null, shiftUp.getKeyCode(), shiftUp);
+// bridge.onKey(null, shiftDown.getKeyCode(), shiftDown);
+// bridge.onKey(null, shiftUp.getKeyCode(), shiftUp);
+//
+// assertTrue("Shift lock test: after two shift presses, meta state is "
+// + bridge.getMetaState() + " when it should be "
+// + TerminalBridge.META_SHIFT_LOCK,
+// bridge.getMetaState() == TerminalBridge.META_SHIFT_LOCK);
+//
+// bridge.onKey(null, letterAdown.getKeyCode(), letterAdown);
+//
+// assertTrue(
+// "Shift lock test: after letter press, meta state is "
+// + bridge.getMetaState() + " when it should be "
+// + TerminalBridge.META_SHIFT_LOCK,
+// bridge.getMetaState() == TerminalBridge.META_SHIFT_LOCK);
+//
+// bridge.onKey(null, letterAup.getKeyCode(), letterAup);
+//
+// assertTrue(
+// "Shift lock test: after letter press and release, meta state is "
+// + bridge.getMetaState() + " when it should be "
+// + TerminalBridge.META_SHIFT_LOCK,
+// bridge.getMetaState() == TerminalBridge.META_SHIFT_LOCK);
}
}
diff --git a/tests/src/org/connectbot/util/PubkeyUtilsTest.java b/tests/src/org/connectbot/util/PubkeyUtilsTest.java
new file mode 100644
index 0000000..1eb1ee6
--- /dev/null
+++ b/tests/src/org/connectbot/util/PubkeyUtilsTest.java
@@ -0,0 +1,345 @@
+/*
+ * ConnectBot: simple, powerful, open-source SSH client for Android
+ * Copyright 2007 Kenny Root, Jeffrey Sharkey
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.connectbot.util;
+
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.Arrays;
+
+import android.test.AndroidTestCase;
+
+/**
+ * @author Kenny Root
+ *
+ */
+public class PubkeyUtilsTest extends AndroidTestCase {
+ public void testEncodeHex_Null_Failure() throws Exception {
+ try {
+ PubkeyUtils.encodeHex(null);
+ fail("Should throw null pointer exception when argument is null");
+ } catch (NullPointerException e) {
+ // success
+ }
+ }
+ public void testEncodeHex_Success() throws Exception {
+ byte[] input = {(byte) 0xFF, 0x00, (byte) 0xA5, 0x5A, 0x12, 0x23};
+ String expected = "ff00a55a1223";
+
+ assertEquals("Encoded hex should match expected",
+ PubkeyUtils.encodeHex(input), expected);
+ }
+
+ public void testSha256_Empty_Success() throws Exception {
+ byte[] empty_hashed = new byte[] {
+ (byte) 0xe3, (byte) 0xb0, (byte) 0xc4, (byte) 0x42,
+ (byte) 0x98, (byte) 0xfc, (byte) 0x1c, (byte) 0x14,
+ (byte) 0x9a, (byte) 0xfb, (byte) 0xf4, (byte) 0xc8,
+ (byte) 0x99, (byte) 0x6f, (byte) 0xb9, (byte) 0x24,
+ (byte) 0x27, (byte) 0xae, (byte) 0x41, (byte) 0xe4,
+ (byte) 0x64, (byte) 0x9b, (byte) 0x93, (byte) 0x4c,
+ (byte) 0xa4, (byte) 0x95, (byte) 0x99, (byte) 0x1b,
+ (byte) 0x78, (byte) 0x52, (byte) 0xb8, (byte) 0x55,
+ };
+
+ final byte[] empty = new byte[] {};
+
+ assertTrue("Empty string should be equal to known test vector",
+ Arrays.equals(empty_hashed, PubkeyUtils.sha256(empty)));
+ }
+
+ /* openssl ecparam -genkey -name prime256v1 -noout | openssl pkcs8 -topk8 -outform d -nocrypt | recode ../x1 | sed 's/0x/(byte) 0x/g' */
+ private static final byte[] EC_KEY_PKCS8 = new byte[] { (byte) 0x30, (byte) 0x81, (byte) 0x87,
+ (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0x30, (byte) 0x13, (byte) 0x06,
+ (byte) 0x07, (byte) 0x2A, (byte) 0x86, (byte) 0x48, (byte) 0xCE, (byte) 0x3D,
+ (byte) 0x02, (byte) 0x01, (byte) 0x06, (byte) 0x08, (byte) 0x2A, (byte) 0x86,
+ (byte) 0x48, (byte) 0xCE, (byte) 0x3D, (byte) 0x03, (byte) 0x01, (byte) 0x07,
+ (byte) 0x04, (byte) 0x6D, (byte) 0x30, (byte) 0x6B, (byte) 0x02, (byte) 0x01,
+ (byte) 0x01, (byte) 0x04, (byte) 0x20, (byte) 0xC7, (byte) 0x6B, (byte) 0xA5,
+ (byte) 0xB6, (byte) 0xB7, (byte) 0x4E, (byte) 0x0B, (byte) 0x70, (byte) 0x2E,
+ (byte) 0xA0, (byte) 0x5D, (byte) 0x8D, (byte) 0x0A, (byte) 0xF5, (byte) 0x43,
+ (byte) 0xEF, (byte) 0x54, (byte) 0x2F, (byte) 0x05, (byte) 0x5B, (byte) 0x66,
+ (byte) 0x50, (byte) 0xC5, (byte) 0xB4, (byte) 0xA8, (byte) 0x60, (byte) 0x16,
+ (byte) 0x8E, (byte) 0x8D, (byte) 0xCD, (byte) 0x11, (byte) 0xFA, (byte) 0xA1,
+ (byte) 0x44, (byte) 0x03, (byte) 0x42, (byte) 0x00, (byte) 0x04, (byte) 0x12,
+ (byte) 0xE2, (byte) 0x70, (byte) 0x30, (byte) 0x87, (byte) 0x2F, (byte) 0xDE,
+ (byte) 0x10, (byte) 0xD9, (byte) 0xC9, (byte) 0x83, (byte) 0xC7, (byte) 0x8D,
+ (byte) 0xC9, (byte) 0x9B, (byte) 0x94, (byte) 0x24, (byte) 0x50, (byte) 0x5D,
+ (byte) 0xEC, (byte) 0xF1, (byte) 0x4F, (byte) 0x52, (byte) 0xC6, (byte) 0xE7,
+ (byte) 0xA3, (byte) 0xD7, (byte) 0xF4, (byte) 0x7C, (byte) 0x09, (byte) 0xA1,
+ (byte) 0x10, (byte) 0x11, (byte) 0xE4, (byte) 0x9E, (byte) 0x90, (byte) 0xAF,
+ (byte) 0xF9, (byte) 0x4A, (byte) 0x74, (byte) 0x09, (byte) 0x93, (byte) 0xC7,
+ (byte) 0x9A, (byte) 0xB3, (byte) 0xE2, (byte) 0xD8, (byte) 0x61, (byte) 0x5F,
+ (byte) 0x86, (byte) 0x14, (byte) 0x91, (byte) 0x7A, (byte) 0x23, (byte) 0x81,
+ (byte) 0x42, (byte) 0xA9, (byte) 0x02, (byte) 0x1D, (byte) 0x33, (byte) 0x19,
+ (byte) 0xC0, (byte) 0x4B, (byte) 0xCE
+ };
+
+ private static final BigInteger EC_KEY_priv = new BigInteger("c76ba5b6b74e0b702ea05d8d0af543ef542f055b6650c5b4a860168e8dcd11fa", 16);
+ private static final BigInteger EC_KEY_pub_x = new BigInteger("12e27030872fde10d9c983c78dc99b9424505decf14f52c6e7a3d7f47c09a110", 16);
+ private static final BigInteger EC_KEY_pub_y = new BigInteger("11e49e90aff94a740993c79ab3e2d8615f8614917a238142a9021d3319c04bce", 16);
+
+ /* openssl genrsa 512 | openssl pkcs8 -topk8 -outform d -nocrypt | recode ../x1 | sed 's/0x/(byte) 0x/g' */
+ private static final byte[] RSA_KEY_PKCS8 = new byte[] { (byte) 0x30, (byte) 0x82, (byte) 0x01,
+ (byte) 0x55, (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0x30, (byte) 0x0D,
+ (byte) 0x06, (byte) 0x09, (byte) 0x2A, (byte) 0x86, (byte) 0x48, (byte) 0x86,
+ (byte) 0xF7, (byte) 0x0D, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x05,
+ (byte) 0x00, (byte) 0x04, (byte) 0x82, (byte) 0x01, (byte) 0x3F, (byte) 0x30,
+ (byte) 0x82, (byte) 0x01, (byte) 0x3B, (byte) 0x02, (byte) 0x01, (byte) 0x00,
+ (byte) 0x02, (byte) 0x41, (byte) 0x00, (byte) 0xC6, (byte) 0x00, (byte) 0x79,
+ (byte) 0x0C, (byte) 0x46, (byte) 0xF9, (byte) 0x03, (byte) 0x15, (byte) 0xBA,
+ (byte) 0x35, (byte) 0x63, (byte) 0x6C, (byte) 0x97, (byte) 0x3A, (byte) 0x6C,
+ (byte) 0xC8, (byte) 0x15, (byte) 0x32, (byte) 0x2A, (byte) 0x62, (byte) 0x72,
+ (byte) 0xBD, (byte) 0x05, (byte) 0x01, (byte) 0xCF, (byte) 0xE6, (byte) 0x49,
+ (byte) 0xEC, (byte) 0xC9, (byte) 0x8A, (byte) 0x3A, (byte) 0x4E, (byte) 0xB1,
+ (byte) 0xF2, (byte) 0x3E, (byte) 0x86, (byte) 0x3C, (byte) 0x64, (byte) 0x4A,
+ (byte) 0x0A, (byte) 0x29, (byte) 0xD6, (byte) 0xFA, (byte) 0xF9, (byte) 0xAC,
+ (byte) 0xD8, (byte) 0x7B, (byte) 0x9F, (byte) 0x2A, (byte) 0x6B, (byte) 0x13,
+ (byte) 0x06, (byte) 0x06, (byte) 0xEB, (byte) 0x83, (byte) 0x1B, (byte) 0xB8,
+ (byte) 0x97, (byte) 0xA3, (byte) 0x91, (byte) 0x95, (byte) 0x60, (byte) 0x15,
+ (byte) 0xE5, (byte) 0x02, (byte) 0x03, (byte) 0x01, (byte) 0x00, (byte) 0x01,
+ (byte) 0x02, (byte) 0x40, (byte) 0x0F, (byte) 0xDA, (byte) 0x33, (byte) 0xD6,
+ (byte) 0xCE, (byte) 0xCB, (byte) 0xDA, (byte) 0xFA, (byte) 0x5F, (byte) 0x59,
+ (byte) 0x2C, (byte) 0xE7, (byte) 0xA1, (byte) 0xC7, (byte) 0xF4, (byte) 0xB3,
+ (byte) 0xA4, (byte) 0x36, (byte) 0xCA, (byte) 0xFB, (byte) 0xEC, (byte) 0xD1,
+ (byte) 0xC3, (byte) 0x57, (byte) 0xDC, (byte) 0xCC, (byte) 0x44, (byte) 0x38,
+ (byte) 0xE7, (byte) 0xFD, (byte) 0xE0, (byte) 0x23, (byte) 0x0E, (byte) 0x97,
+ (byte) 0x87, (byte) 0x55, (byte) 0x80, (byte) 0x2B, (byte) 0xF2, (byte) 0xF4,
+ (byte) 0x1C, (byte) 0x03, (byte) 0xD2, (byte) 0x3E, (byte) 0x09, (byte) 0x72,
+ (byte) 0x49, (byte) 0xD8, (byte) 0x9C, (byte) 0xAC, (byte) 0xDA, (byte) 0x65,
+ (byte) 0x68, (byte) 0x4D, (byte) 0x38, (byte) 0x19, (byte) 0xD8, (byte) 0xB1,
+ (byte) 0x5B, (byte) 0xB7, (byte) 0x38, (byte) 0xC8, (byte) 0x94, (byte) 0xB5,
+ (byte) 0x02, (byte) 0x21, (byte) 0x00, (byte) 0xF7, (byte) 0x8E, (byte) 0x20,
+ (byte) 0xDC, (byte) 0x26, (byte) 0x12, (byte) 0x3A, (byte) 0x85, (byte) 0x91,
+ (byte) 0x5F, (byte) 0x45, (byte) 0xA6, (byte) 0x95, (byte) 0xE5, (byte) 0x22,
+ (byte) 0xD0, (byte) 0xC4, (byte) 0xD7, (byte) 0x6A, (byte) 0xF1, (byte) 0x43,
+ (byte) 0x38, (byte) 0x88, (byte) 0x20, (byte) 0x7D, (byte) 0x80, (byte) 0x73,
+ (byte) 0x7B, (byte) 0xDC, (byte) 0x73, (byte) 0x51, (byte) 0x3B, (byte) 0x02,
+ (byte) 0x21, (byte) 0x00, (byte) 0xCC, (byte) 0xC1, (byte) 0x99, (byte) 0xC8,
+ (byte) 0xC0, (byte) 0x54, (byte) 0xBC, (byte) 0xE9, (byte) 0xFB, (byte) 0x77,
+ (byte) 0x28, (byte) 0xB8, (byte) 0x26, (byte) 0x02, (byte) 0xC0, (byte) 0x0C,
+ (byte) 0xDE, (byte) 0xFD, (byte) 0xEA, (byte) 0xD0, (byte) 0x15, (byte) 0x4B,
+ (byte) 0x3B, (byte) 0xD1, (byte) 0xDD, (byte) 0xFD, (byte) 0x5B, (byte) 0xAC,
+ (byte) 0xB3, (byte) 0xCF, (byte) 0xC3, (byte) 0x5F, (byte) 0x02, (byte) 0x21,
+ (byte) 0x00, (byte) 0xCD, (byte) 0x8C, (byte) 0x25, (byte) 0x9C, (byte) 0xA5,
+ (byte) 0xBF, (byte) 0xDC, (byte) 0xF7, (byte) 0xAA, (byte) 0x8D, (byte) 0x00,
+ (byte) 0xB8, (byte) 0x21, (byte) 0x1D, (byte) 0xF0, (byte) 0x9A, (byte) 0x87,
+ (byte) 0xD6, (byte) 0x95, (byte) 0xE5, (byte) 0x5D, (byte) 0x7B, (byte) 0x43,
+ (byte) 0x0C, (byte) 0x37, (byte) 0x28, (byte) 0xC0, (byte) 0xBA, (byte) 0xC7,
+ (byte) 0x80, (byte) 0xB8, (byte) 0xA1, (byte) 0x02, (byte) 0x21, (byte) 0x00,
+ (byte) 0xCC, (byte) 0x26, (byte) 0x6F, (byte) 0xAD, (byte) 0x60, (byte) 0x4E,
+ (byte) 0x5C, (byte) 0xB9, (byte) 0x32, (byte) 0x57, (byte) 0x61, (byte) 0x8B,
+ (byte) 0x11, (byte) 0xA3, (byte) 0x06, (byte) 0x57, (byte) 0x0E, (byte) 0xF2,
+ (byte) 0xBE, (byte) 0x6F, (byte) 0x4F, (byte) 0xFB, (byte) 0xDE, (byte) 0x1D,
+ (byte) 0xE6, (byte) 0xA7, (byte) 0x19, (byte) 0x03, (byte) 0x7D, (byte) 0x98,
+ (byte) 0xB6, (byte) 0x23, (byte) 0x02, (byte) 0x20, (byte) 0x24, (byte) 0x80,
+ (byte) 0x94, (byte) 0xFF, (byte) 0xDD, (byte) 0x7A, (byte) 0x22, (byte) 0x7D,
+ (byte) 0xC4, (byte) 0x5A, (byte) 0xFD, (byte) 0x84, (byte) 0xC1, (byte) 0xAD,
+ (byte) 0x8A, (byte) 0x13, (byte) 0x2A, (byte) 0xF9, (byte) 0x5D, (byte) 0xFF,
+ (byte) 0x0B, (byte) 0x2E, (byte) 0x0F, (byte) 0x61, (byte) 0x42, (byte) 0x88,
+ (byte) 0x57, (byte) 0xCF, (byte) 0xC1, (byte) 0x71, (byte) 0xC9, (byte) 0xB9
+ };
+
+ private static final BigInteger RSA_KEY_N = new BigInteger("C600790C46F90315BA35636C973A6CC815322A6272BD0501CFE649ECC98A3A4EB1F23E863C644A0A29D6FAF9ACD87B9F2A6B130606EB831BB897A391956015E5", 16);
+ private static final BigInteger RSA_KEY_E = new BigInteger("010001", 16);
+
+ /*
+ openssl dsaparam -genkey -text -out dsakey.pem 1024
+ openssl dsa -in dsakey.pem -text -noout
+ openssl pkcs8 -topk8 -in dsakey.pem -outform d -nocrypt | recode ../x1 | sed 's/0x/(byte) 0x/g'
+ */
+ private static final byte[] DSA_KEY_PKCS8 = new byte[] {
+ (byte) 0x30, (byte) 0x82, (byte) 0x01, (byte) 0x4A, (byte) 0x02, (byte) 0x01,
+ (byte) 0x00, (byte) 0x30, (byte) 0x82, (byte) 0x01, (byte) 0x2B, (byte) 0x06,
+ (byte) 0x07, (byte) 0x2A, (byte) 0x86, (byte) 0x48, (byte) 0xCE, (byte) 0x38,
+ (byte) 0x04, (byte) 0x01, (byte) 0x30, (byte) 0x82, (byte) 0x01, (byte) 0x1E,
+ (byte) 0x02, (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0xD2, (byte) 0x18,
+ (byte) 0xDB, (byte) 0x94, (byte) 0x7C, (byte) 0xD6, (byte) 0x2E, (byte) 0xE2,
+ (byte) 0x07, (byte) 0x38, (byte) 0x42, (byte) 0xC4, (byte) 0x16, (byte) 0x24,
+ (byte) 0x94, (byte) 0x2F, (byte) 0xC1, (byte) 0x0F, (byte) 0x92, (byte) 0x0A,
+ (byte) 0x44, (byte) 0x44, (byte) 0x99, (byte) 0xFC, (byte) 0x01, (byte) 0x1B,
+ (byte) 0xF8, (byte) 0xF3, (byte) 0x82, (byte) 0x57, (byte) 0x01, (byte) 0x8D,
+ (byte) 0xE6, (byte) 0x22, (byte) 0x70, (byte) 0xA0, (byte) 0xD6, (byte) 0x05,
+ (byte) 0x0F, (byte) 0xF1, (byte) 0xD0, (byte) 0xF4, (byte) 0x0B, (byte) 0xA2,
+ (byte) 0xE4, (byte) 0x1E, (byte) 0xD3, (byte) 0x44, (byte) 0x79, (byte) 0x74,
+ (byte) 0x4C, (byte) 0xC1, (byte) 0xA7, (byte) 0xA5, (byte) 0x84, (byte) 0xD8,
+ (byte) 0xB9, (byte) 0xDF, (byte) 0xA3, (byte) 0x85, (byte) 0xFA, (byte) 0xF2,
+ (byte) 0xFD, (byte) 0x44, (byte) 0x0B, (byte) 0xB1, (byte) 0xA5, (byte) 0x82,
+ (byte) 0x8D, (byte) 0x06, (byte) 0x92, (byte) 0xCA, (byte) 0xB4, (byte) 0xFB,
+ (byte) 0xDF, (byte) 0xC2, (byte) 0xFD, (byte) 0xA7, (byte) 0xCB, (byte) 0x6F,
+ (byte) 0x03, (byte) 0xB9, (byte) 0xEF, (byte) 0xFD, (byte) 0x7F, (byte) 0xBC,
+ (byte) 0xB3, (byte) 0x1D, (byte) 0xA4, (byte) 0xE8, (byte) 0x7D, (byte) 0xA2,
+ (byte) 0xCF, (byte) 0x62, (byte) 0x35, (byte) 0x06, (byte) 0xC8, (byte) 0xFE,
+ (byte) 0xE6, (byte) 0xE7, (byte) 0x6E, (byte) 0xAE, (byte) 0x22, (byte) 0xE7,
+ (byte) 0x82, (byte) 0x38, (byte) 0x54, (byte) 0x82, (byte) 0xCD, (byte) 0xEA,
+ (byte) 0xD8, (byte) 0x69, (byte) 0xBB, (byte) 0x1C, (byte) 0xD3, (byte) 0x70,
+ (byte) 0x32, (byte) 0xB1, (byte) 0xFB, (byte) 0x07, (byte) 0x01, (byte) 0x66,
+ (byte) 0xCC, (byte) 0x24, (byte) 0xD6, (byte) 0x50, (byte) 0x46, (byte) 0x9B,
+ (byte) 0x02, (byte) 0x15, (byte) 0x00, (byte) 0xD6, (byte) 0xE6, (byte) 0x7E,
+ (byte) 0x1A, (byte) 0xE5, (byte) 0xCA, (byte) 0x1D, (byte) 0xB6, (byte) 0xAF,
+ (byte) 0x4E, (byte) 0xD9, (byte) 0x18, (byte) 0xE8, (byte) 0x87, (byte) 0xB1,
+ (byte) 0xBC, (byte) 0x93, (byte) 0xE1, (byte) 0x80, (byte) 0xF5, (byte) 0x02,
+ (byte) 0x81, (byte) 0x80, (byte) 0x19, (byte) 0x20, (byte) 0xCC, (byte) 0x18,
+ (byte) 0xF6, (byte) 0x8F, (byte) 0x73, (byte) 0xFA, (byte) 0x9F, (byte) 0x50,
+ (byte) 0xC8, (byte) 0x92, (byte) 0xBE, (byte) 0x07, (byte) 0x7C, (byte) 0x34,
+ (byte) 0xD8, (byte) 0x6F, (byte) 0x63, (byte) 0xC9, (byte) 0x35, (byte) 0x48,
+ (byte) 0x79, (byte) 0x79, (byte) 0x26, (byte) 0xEF, (byte) 0x1E, (byte) 0x99,
+ (byte) 0x54, (byte) 0xD7, (byte) 0x30, (byte) 0x2C, (byte) 0x68, (byte) 0xBC,
+ (byte) 0xFF, (byte) 0xF2, (byte) 0x4C, (byte) 0x6A, (byte) 0xD3, (byte) 0x2D,
+ (byte) 0x1C, (byte) 0x7A, (byte) 0x06, (byte) 0x11, (byte) 0x72, (byte) 0x92,
+ (byte) 0x9C, (byte) 0xAA, (byte) 0x95, (byte) 0x0E, (byte) 0x44, (byte) 0x2C,
+ (byte) 0x5F, (byte) 0x19, (byte) 0x25, (byte) 0xB4, (byte) 0xBF, (byte) 0x21,
+ (byte) 0x8F, (byte) 0xB7, (byte) 0x7E, (byte) 0x4B, (byte) 0x64, (byte) 0x83,
+ (byte) 0x59, (byte) 0x20, (byte) 0x20, (byte) 0x36, (byte) 0x84, (byte) 0xA4,
+ (byte) 0x1D, (byte) 0xB5, (byte) 0xCA, (byte) 0x7F, (byte) 0x10, (byte) 0x4E,
+ (byte) 0x27, (byte) 0x21, (byte) 0x8E, (byte) 0x2C, (byte) 0xA5, (byte) 0xF8,
+ (byte) 0xAC, (byte) 0xBD, (byte) 0xF5, (byte) 0xB5, (byte) 0xBA, (byte) 0xEB,
+ (byte) 0x86, (byte) 0x6F, (byte) 0x7F, (byte) 0xB1, (byte) 0xE0, (byte) 0x90,
+ (byte) 0x35, (byte) 0xCA, (byte) 0xA8, (byte) 0x64, (byte) 0x6E, (byte) 0x06,
+ (byte) 0x3D, (byte) 0x02, (byte) 0x3D, (byte) 0x95, (byte) 0x57, (byte) 0xB3,
+ (byte) 0x8A, (byte) 0xE2, (byte) 0x0B, (byte) 0xD3, (byte) 0x9E, (byte) 0x1C,
+ (byte) 0x13, (byte) 0xDE, (byte) 0x48, (byte) 0xA3, (byte) 0xC2, (byte) 0x11,
+ (byte) 0xDA, (byte) 0x75, (byte) 0x09, (byte) 0xF6, (byte) 0x92, (byte) 0x0F,
+ (byte) 0x0F, (byte) 0xA6, (byte) 0xF3, (byte) 0x3E, (byte) 0x04, (byte) 0x16,
+ (byte) 0x02, (byte) 0x14, (byte) 0x29, (byte) 0x50, (byte) 0xE4, (byte) 0x77,
+ (byte) 0x4F, (byte) 0xB2, (byte) 0xFF, (byte) 0xFB, (byte) 0x5D, (byte) 0x33,
+ (byte) 0xC9, (byte) 0x37, (byte) 0xF0, (byte) 0xB5, (byte) 0x8F, (byte) 0xFB,
+ (byte) 0x0D, (byte) 0x45, (byte) 0xC2, (byte) 0x00
+ };
+
+ private static final BigInteger DSA_KEY_P = new BigInteger("00d218db947cd62ee2073842c41624942fc10f920a444499fc011bf8f38257018de62270a0d6050ff1d0f40ba2e41ed34479744cc1a7a584d8b9dfa385faf2fd440bb1a5828d0692cab4fbdfc2fda7cb6f03b9effd7fbcb31da4e87da2cf623506c8fee6e76eae22e782385482cdead869bb1cd37032b1fb070166cc24d650469b", 16);
+ private static final BigInteger DSA_KEY_Q = new BigInteger("00d6e67e1ae5ca1db6af4ed918e887b1bc93e180f5", 16);
+ private static final BigInteger DSA_KEY_G = new BigInteger("1920cc18f68f73fa9f50c892be077c34d86f63c93548797926ef1e9954d7302c68bcfff24c6ad32d1c7a061172929caa950e442c5f1925b4bf218fb77e4b64835920203684a41db5ca7f104e27218e2ca5f8acbdf5b5baeb866f7fb1e09035caa8646e063d023d9557b38ae20bd39e1c13de48a3c211da7509f6920f0fa6f33e", 16);
+
+ private static final BigInteger DSA_KEY_priv = new BigInteger("2950e4774fb2fffb5d33c937f0b58ffb0d45c200", 16);
+ private static final BigInteger DSA_KEY_pub = new BigInteger("0087b82cdf3232db3bec0d00e96c8393bc7f5629551ea1a00888961cf56e80a36f2a7b316bc10b1d367a5ea374235c9361a472a9176f6cf61f708b86a52b4fae814abd1f1bdd16eea94aea9281851032b1bad7567624c615d6899ca1c94ad614f14e767e49d2ba5223cd113a0d02b66183653cd346ae76d85843afe66520904274", 16);
+
+ public void testGetOidFromPkcs8Encoded_Ec_NistP256() throws Exception {
+ assertEquals("1.2.840.10045.2.1", PubkeyUtils.getOidFromPkcs8Encoded(EC_KEY_PKCS8));
+ }
+
+ public void testGetOidFromPkcs8Encoded_Rsa() throws Exception {
+ assertEquals("1.2.840.113549.1.1.1", PubkeyUtils.getOidFromPkcs8Encoded(RSA_KEY_PKCS8));
+ }
+
+ public void testGetOidFromPkcs8Encoded_Dsa() throws Exception {
+ assertEquals("1.2.840.10040.4.1", PubkeyUtils.getOidFromPkcs8Encoded(DSA_KEY_PKCS8));
+ }
+
+ public void testGetOidFromPkcs8Encoded_Null_Failure() throws Exception {
+ try {
+ PubkeyUtils.getOidFromPkcs8Encoded(null);
+ fail("Should throw NoSuchAlgorithmException");
+ } catch (NoSuchAlgorithmException expected) {
+ }
+ }
+
+ public void testGetOidFromPkcs8Encoded_NotCorrectDer_Failure() throws Exception {
+ try {
+ PubkeyUtils.getOidFromPkcs8Encoded(new byte[] { 0x30, 0x01, 0x00 });
+ fail("Should throw NoSuchAlgorithmException");
+ } catch (NoSuchAlgorithmException expected) {
+ }
+ }
+
+ public void testGetAlgorithmForOid_Ecdsa() throws Exception {
+ assertEquals("EC", PubkeyUtils.getAlgorithmForOid("1.2.840.10045.2.1"));
+ }
+
+ public void testGetAlgorithmForOid_Rsa() throws Exception {
+ assertEquals("RSA", PubkeyUtils.getAlgorithmForOid("1.2.840.113549.1.1.1"));
+ }
+
+ public void testGetAlgorithmForOid_Dsa() throws Exception {
+ assertEquals("DSA", PubkeyUtils.getAlgorithmForOid("1.2.840.10040.4.1"));
+ }
+
+ public void testGetAlgorithmForOid_NullInput_Failure() throws Exception {
+ try {
+ PubkeyUtils.getAlgorithmForOid(null);
+ fail("Should throw NoSuchAlgorithmException");
+ } catch (NoSuchAlgorithmException expected) {
+ }
+ }
+
+ public void testGetAlgorithmForOid_UnknownOid_Failure() throws Exception {
+ try {
+ PubkeyUtils.getAlgorithmForOid("1.3.66666.2000.4000.1");
+ fail("Should throw NoSuchAlgorithmException");
+ } catch (NoSuchAlgorithmException expected) {
+ }
+ }
+
+ public void testRecoverKeyPair_Dsa() throws Exception {
+ KeyPair kp = PubkeyUtils.recoverKeyPair(DSA_KEY_PKCS8);
+
+ DSAPublicKey pubKey = (DSAPublicKey) kp.getPublic();
+
+ assertEquals(DSA_KEY_pub, pubKey.getY());
+
+ DSAParams params = pubKey.getParams();
+ assertEquals(params.getG(), DSA_KEY_G);
+ assertEquals(params.getP(), DSA_KEY_P);
+ assertEquals(params.getQ(), DSA_KEY_Q);
+ }
+
+ public void testRecoverKeyPair_Rsa() throws Exception {
+ KeyPair kp = PubkeyUtils.recoverKeyPair(RSA_KEY_PKCS8);
+
+ RSAPublicKey pubKey = (RSAPublicKey) kp.getPublic();
+
+ assertEquals(RSA_KEY_N, pubKey.getModulus());
+ assertEquals(RSA_KEY_E, pubKey.getPublicExponent());
+ }
+
+ public void testRecoverKeyPair_Ec() throws Exception {
+ KeyPair kp = PubkeyUtils.recoverKeyPair(EC_KEY_PKCS8);
+
+ ECPublicKey pubKey = (ECPublicKey) kp.getPublic();
+
+ assertEquals(EC_KEY_pub_x, pubKey.getW().getAffineX());
+ assertEquals(EC_KEY_pub_y, pubKey.getW().getAffineY());
+ }
+
+ private static class MyPrivateKey implements PrivateKey {
+ public String getAlgorithm() {
+ throw new UnsupportedOperationException();
+ }
+
+ public byte[] getEncoded() {
+ throw new UnsupportedOperationException();
+ }
+
+ public String getFormat() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ public void testRecoverPublicKey_FakeKey_Failure() throws Exception {
+ try {
+ PubkeyUtils.recoverPublicKey(null, new MyPrivateKey());
+ fail("Should not accept unknown key types");
+ } catch (NoSuchAlgorithmException expected) {
+ }
+ }
+}